Skip to content

A production-ready API client for Next.js with automatic token refresh, GraphQL support, and intelligent error handling. Built with TypeScript and Axios for robust, type-safe API communications.

Notifications You must be signed in to change notification settings

Saif-Mohammed1/axios-config

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

4 Commits
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

API Configuration Module

A robust, production-ready API client built with Axios for Next.js applications with GraphQL support, automatic token refresh, and comprehensive error handling.

πŸ“‹ Table of Contents

✨ Features

  • πŸ” Automatic Token Refresh - Seamlessly refreshes expired tokens without user intervention
  • πŸ”„ Request Queue Management - Queues requests during token refresh to prevent race conditions
  • πŸ›‘οΈ GraphQL Error Handling - Properly handles GraphQL-specific error structures
  • πŸš€ Server-Side Rendering Support - Compatible with Next.js server components
  • πŸ”’ Secure Token Storage - Built-in token management system
  • ⚑ Retry Logic - Smart retry mechanism with failure prevention
  • πŸ“Š Type-Safe - Full TypeScript support with comprehensive type definitions
  • πŸͺ Cookie Management - Automatic credential handling with withCredentials

πŸ—οΈ Architecture

Core Components

api/
β”œβ”€β”€ api.ts           # Main Axios instance with interceptors
β”œβ”€β”€ example.tsx      # Usage example with Next.js page
β”œβ”€β”€ TokenManager.ts  # Token management utility (referenced)
β”œβ”€β”€ appError.ts      # Custom error class (referenced)
└── README.md        # This file

Flow Diagram

Request β†’ Interceptor (Add Token) β†’ API Call
                                      ↓
                                   Success?
                                      ↓
                              β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
                              β”‚             β”‚
                            YES            NO
                              β”‚             β”‚
                         Return Data   401 Error?
                                          ↓
                                    β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”
                                    β”‚           β”‚
                                  YES          NO
                                    β”‚           β”‚
                            Refresh Token  Return Error
                                    β”‚
                            β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”
                            β”‚                β”‚
                         Success          Failure
                            β”‚                β”‚
                      Retry Request    Redirect to Login

πŸ“¦ Installation

Prerequisites

npm install axios
# or
yarn add axios
# or
pnpm add axios

Environment Variables

Create a .env.local file in your project root:

NEXT_PUBLIC_API_ENDPOINT=https://your-api-endpoint.com

βš™οΈ Configuration

Basic Setup

The API client is pre-configured with the following defaults:

const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_ENDPOINT,
  withCredentials: true, // Enables cookie-based authentication
  headers: {
    "Content-Type": "application/json",
  },
});

Custom Configuration

To modify the configuration, update the api.ts file:

const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_ENDPOINT,
  timeout: 10000, // Add request timeout
  withCredentials: true,
  headers: {
    "Content-Type": "application/json",
    // Add custom headers here
  },
});

πŸš€ Usage

Basic Usage

import api from "@/api/api";

// Simple GET request
const response = await api.get("/endpoint");

// POST request with data
const response = await api.post("/endpoint", {
  key: "value",
});

GraphQL Queries

import api from "@/api/api";

const QUERY = `
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`;

const response = await api.post("/graphql", {
  query: QUERY,
  variables: { id: "123" },
});

const user = response.data.data.user;

GraphQL Mutations

const MUTATION = `
  mutation CreateProduct($input: ProductInput!) {
    createProduct(input: $input) {
      id
      name
      price
    }
  }
`;

const response = await api.post("/graphql", {
  query: MUTATION,
  variables: {
    input: {
      name: "New Product",
      price: 99.99,
    },
  },
});

Server Component Usage (Next.js)

import { headers } from "next/headers";
import api from "@/api/api";

const Page = async () => {
  const headersList = await headers();

  const response = await api.post("/graphql", {
    query: YOUR_QUERY,
    variables: {
      /* your variables */
    },
    headers: Object.fromEntries(headersList.entries()),
  });

  return <YourComponent data={response.data.data} />;
};

🧩 Components

1. Request Interceptor

Automatically attaches the access token to every request:

api.interceptors.request.use((config) => {
  const token = tokenManager.getAccessToken();
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

2. Response Interceptor

Handles responses and errors, including:

  • GraphQL error transformation
  • 401 (Unauthorized) error handling
  • Token refresh mechanism
  • Request queuing during refresh

3. Token Refresh Logic

Key features:

  • Single Refresh - Only one refresh request at a time
  • Request Queue - Queues pending requests during refresh
  • Failure Prevention - Stops retry attempts after refresh failure
  • Automatic Retry - Retries original request with new token

🚨 Error Handling

Custom Error Class

The module uses a custom AppError class for consistent error handling:

throw new AppError(message, statusCode);

GraphQL Error Handling

GraphQL errors are automatically transformed into proper HTTP errors:

// GraphQL error structure
{
  errors: [
    {
      message: "Error message",
      extensions: {
        code: "UNAUTHENTICATED",
        status: 401,
      },
    },
  ];
}

// Transformed to AppError
new AppError("Error message", 401);

Error Response Structure

try {
  const response = await api.post("/graphql", { query });
} catch (error) {
  if (error instanceof AppError) {
    console.error(`Error ${error.statusCode}: ${error.message}`);
  }
}

πŸ”‘ Token Management

TokenManager Interface

The module relies on a TokenManager utility with the following methods:

tokenManager.getAccessToken(); // Retrieves current access token
tokenManager.setAccessToken(token); // Stores new access token
tokenManager.clearAccessToken(); // Removes access token
tokenManager.setLogOut(true); // Sets logout state

Token Refresh Flow

  1. Request fails with 401 error
  2. Check if refresh is already in progress
  3. If not, start refresh process:
    • Send refresh mutation to GraphQL endpoint
    • Store new access token
    • Retry all queued requests
  4. If refresh fails:
    • Clear tokens
    • Set logout flag
    • Redirect to login page

πŸ”Œ GraphQL Integration

Refresh Token Mutation

The module uses this GraphQL mutation to refresh tokens:

mutation {
  refreshAccessToken {
    access_token
  }
}

Error Code Mapping

GraphQL Code HTTP Status Action
UNAUTHENTICATED 401 Trigger token refresh
Other errors 500 or custom Return error to caller

🎯 Best Practices

1. Server-Only Usage

Keep the API client server-side only:

import "server-only";

2. Error Boundaries

Wrap components using the API with error boundaries:

import ErrorHandler from "@/components/Error/errorHandler";

try {
  const data = await fetchData();
  return <Component data={data} />;
} catch (error) {
  return <ErrorHandler message={error.message} />;
}

3. Type Safety

Always define TypeScript interfaces for your data:

interface Product {
  id: string;
  name: string;
  price: number;
}

const response = await api.post<{ data: { products: Product[] } }>("/graphql", {
  query: PRODUCTS_QUERY,
});

4. Request Optimization

  • Use field selection in GraphQL queries
  • Implement pagination
  • Cache responses when appropriate
const OPTIMIZED_QUERY = `
  query GetProducts($limit: Int, $page: Int) {
    products(limit: $limit, page: $page) {
      docs {
        id
        name
        price
      }
      meta {
        total
        totalPages
      }
    }
  }
`;

πŸ› Troubleshooting

Common Issues

Issue: Infinite Refresh Loop

Cause: Refresh token endpoint also returns 401

Solution: Ensure the refresh endpoint uses a different authentication method (refresh token cookie)

Issue: Requests Failing After Token Refresh

Cause: Queued requests not being resolved properly

Solution: Check that refreshSubscribers array is properly managed

Issue: CORS Errors

Cause: withCredentials: true requires proper CORS configuration on the server

Solution: Server must set:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: <specific-origin>

Issue: "Cannot read property 'data' of undefined"

Cause: GraphQL response structure mismatch

Solution: Always check for errors before accessing data:

if (response.data.errors) {
  // Handle error
}
const result = response.data.data;

Debugging Tips

  1. Enable Axios Logging:

    api.interceptors.request.use((config) => {
      console.log("Request:", config);
      return config;
    });
  2. Check Token Storage:

    console.log("Token:", tokenManager.getAccessToken());
  3. Monitor Refresh State:

    console.log("Is Refreshing:", isRefreshing);
    console.log("Refresh Failed:", refreshFailed);

πŸ“ Example Implementation

See example.tsx for a complete Next.js page implementation showing:

  • GraphQL query definition
  • Server-side data fetching
  • Error handling
  • Component rendering with fetched data

🀝 Contributing

When modifying the API configuration:

  1. Ensure backward compatibility
  2. Update type definitions
  3. Test token refresh flow
  4. Update this documentation
  5. Add error handling for edge cases

πŸ“„ License

This module is part of the project and follows the same license terms.

πŸ”— Related Documentation


Last Updated: November 2025
Version: 1.0.0

About

A production-ready API client for Next.js with automatic token refresh, GraphQL support, and intelligent error handling. Built with TypeScript and Axios for robust, type-safe API communications.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published