How to Type Axios Response Data in TypeScript
Axios provides built-in support for TypeScript generics, allowing
developers to define predictable data contracts for HTTP requests. By
typing your API responses, you gain compile-time type safety, IDE
auto-completion, and fewer runtime bugs. This guide demonstrates how to
pass generic types to Axios methods, manage the
AxiosResponse interface, create reusable API wrappers, and
type custom error responses.
Basic Typing with Axios Generics
Axios request methods such as get, post,
put, and delete accept generic type arguments.
When you pass a type to these methods, TypeScript assigns that type to
the data property of the returned
AxiosResponse object.
import axios from 'axios';
interface User {
id: number;
name: string;
email: string;
}
async function fetchUser(userId: number): Promise<User> {
// Pass the interface as a generic argument to axios.get
const response = await axios.get<User>(`https://api.example.com/users/${userId}`);
// response.data is typed as User
return response.data;
}Understanding the
AxiosResponse Wrapper
By default, Axios methods return a promise containing an
AxiosResponse<T> object. The generic parameter
T defines the shape of response.data.
The AxiosResponse interface is structured roughly as
follows:
interface AxiosResponse<T = any> {
data: T;
status: number;
statusText: string;
headers: Record<string, string>;
config: AxiosRequestConfig;
request?: any;
}When you write axios.get<User>(), TypeScript
resolves the return value to
Promise<AxiosResponse<User>>.
Typing POST, PUT, and PATCH Requests
For methods that send a request body, Axios allows you to type both the response and the request payload:
interface CreateUserDto {
name: string;
email: string;
}
interface CreateUserResponse {
id: number;
createdAt: string;
}
async function createUser(payload: CreateUserDto): Promise<CreateUserResponse> {
// axios.post<ResponseType, AxiosResponse<ResponseType>, RequestPayloadType>
const response = await axios.post<CreateUserResponse>(
'https://api.example.com/users',
payload
);
return response.data;
}Creating a Strongly Typed API Client
To avoid repeatedly writing response.data and unwrapping
AxiosResponse, you can create a reusable API client utility
that returns the payload directly.
import axios, { AxiosRequestConfig } from 'axios';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
headers: {
'Content-Type': 'application/json',
},
});
export const api = {
get: async <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {
const response = await apiClient.get<T>(url, config);
return response.data;
},
post: async <T, B = unknown>(url: string, body?: B, config?: AxiosRequestConfig): Promise<T> => {
const response = await apiClient.post<T>(url, body, config);
return response.data;
},
};
// Usage:
const user = await api.get<User>('/users/1'); // user is directly inferred as UserTyping Axios Errors
When requests fail, Axios throws an error that can be cast and
checked using the isAxiosError helper. You can pass a
generic type to AxiosError to define the structure of the
API error response.
import axios, { AxiosError } from 'axios';
interface ApiErrorResponse {
message: string;
errorCode: number;
}
async function safeFetchUser(userId: number) {
try {
const response = await axios.get<User>(`https://api.example.com/users/${userId}`);
return response.data;
} catch (error) {
if (axios.isAxiosError<ApiErrorResponse>(error)) {
// error.response?.data is typed as ApiErrorResponse | undefined
console.error(error.response?.data.message);
console.error(`Error Code: ${error.response?.data.errorCode}`);
} else {
console.error('Unexpected error:', error);
}
}
}Runtime Validation Considerations
TypeScript generics provide compile-time guarantees, but they do not validate the data at runtime. If an API returns an unexpected payload structure, TypeScript will still treat it as the specified generic type. For mission-critical applications, combine Axios generics with runtime schema validation libraries like Zod to ensure received data strictly matches your types before processing.