FrontendTypeScriptAxiosAPI ClientReactFull Stack

Building a Type-Safe API Client with TypeScript and Axios

Create a fully type-safe HTTP client in TypeScript using Axios with interceptors, error handling, authentication, and reusable request patterns.

Abdur Razzak

Abdur Razzak

Full-Stack Web Developer

February 26, 2025 9 min read

Why a Typed API Client?

An untyped API client is a source of runtime bugs — you cast response data to any and hope it matches what you expect. A typed API client using TypeScript generics ensures your response data has the exact shape you define, catching mismatches at compile time. This is especially valuable in large codebases where API shapes change over time.

Setting Up the Axios Instance

Create a single Axios instance with your base URL and default headers: axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, headers: { 'Content-Type': 'application/json' } }). This instance is shared across your application. Set a timeout to prevent hanging requests. Exporting this instance from a single file (lib/api.ts) makes it easy to mock in tests and update the base URL for different environments.

Request and Response Interceptors

Interceptors run before every request or after every response. Use a request interceptor to attach the auth token: read the token from your store or cookie and add it to the Authorization header. Use a response interceptor to handle 401 errors globally — redirect to login, refresh the token, or clear the session. This centralizes auth logic so you never forget to handle it in individual API calls.

Generic Typed API Methods

Create generic wrapper functions for each HTTP method: async function get<T>(url: string): Promise<T> { const response = await instance.get<T>(url); return response.data; }. TypeScript infers the return type from the generic parameter, so callers get full autocomplete and type checking. Define your API response types as TypeScript interfaces and pass them as generics: get<BlogPost[]>('/api/blogs').

Error Handling and Response Normalization

Normalize API errors into a consistent format your application can handle. Catch Axios errors in the response interceptor, extract the error message from response.data.message or a fallback, and re-throw a normalized ApiError with code, message, and status. All components catch ApiError and handle it uniformly — no more inconsistent error shapes from different endpoints.

Sharing API Types Between Frontend and Backend

In a monorepo (TypeScript frontend + Node.js backend), share API response types in a packages/types shared package. Both the Express route handler and the Axios client import from the same type definitions. When the API changes, TypeScript errors in both the backend and frontend alert you to update both sides consistently. This eliminates a whole class of frontend-backend contract bugs in MERN stack projects.

Share this article

All posts
#TypeScript#Axios#API Client#React#Full Stack
Abdur Razzak — Full Stack Web Developer

Full-Stack Expert

MERN · React · Next.js · WordPress