Configure Custom Path Parameters in Axios

By default, the Axios HTTP client natively serializes the params object into URL query parameters rather than dynamically substituting path parameters in the request URL. This guide demonstrates how to implement a clean, automated path parameter replacement mechanism in Axios using request interceptors. By configuring this logic, you can define URLs with placeholders such as /users/:id or /posts/{postId} and have them resolved automatically at runtime.


Understanding the Problem

Axios maps the params configuration directly to the query string:

// Default behavior
axios.get('/users/:id', { params: { id: 42 } });
// Resulting URL: /users/:id?id=42 (Not /users/42)

To resolve placeholders in the path, you must intercept the request configuration before it is dispatched and replace tokens matching your chosen naming convention.


Step 1: Create a Request Interceptor

Using an Axios request interceptor is the most efficient way to achieve custom path substitution globally or on a specific Axios instance.

Here is the implementation supporting both :param and {param} syntax:

import axios from 'axios';

// Create an Axios instance
const apiClient = axios.create({
  baseURL: 'https://api.example.com',
});

// Add the path parameter replacement interceptor
apiClient.interceptors.request.use((config) => {
  if (!config.url || !config.pathParams) {
    return config;
  }

  let url = config.url;
  const pathParams = config.pathParams;

  // Replace :paramName or {paramName} placeholders
  Object.entries(pathParams).forEach(([key, value]) => {
    const encodedValue = encodeURIComponent(String(value));
    const colonPattern = new RegExp(`:${key}(?=[/?#]|$)`, 'g');
    const bracePattern = new RegExp(`\\{${key}\\}`, 'g');

    url = url.replace(colonPattern, encodedValue).replace(bracePattern, encodedValue);
  });

  config.url = url;
  return config;
});

export default apiClient;

Step 2: Making Requests with Path Parameters

With the interceptor configured, supply the pathParams property inside the request configuration object:

import apiClient from './apiClient';

// Example using colon syntax
apiClient.get('/users/:userId/posts/:postId', {
  pathParams: {
    userId: 123,
    postId: 456,
  },
  params: {
    includeComments: true, // Still works for query parameters
  },
});
// Dispatched URL: https://api.example.com/users/123/posts/456?includeComments=true

// Example using curly brace syntax
apiClient.delete('/organizations/{orgId}/members/{memberId}', {
  pathParams: {
    orgId: 'acme-corp',
    memberId: 99,
  },
});
// Dispatched URL: https://api.example.com/organizations/acme-corp/members/99

Step 3: TypeScript Support (Optional)

If you are using TypeScript, extend the AxiosRequestConfig interface via module augmentation to ensure type safety for the pathParams field:

import axios from 'axios';

declare module 'axios' {
  export interface AxiosRequestConfig {
    pathParams?: Record<string, string | number | boolean>;
  }
}

Key Considerations

  1. URL Encoding: Always pass values through encodeURIComponent to avoid breaking the URL structure when values contain special characters or spaces.
  2. Separation of Concerns: Keep path parameters in a dedicated pathParams object rather than overloading params to avoid unintended query string generation.
  3. Unmatched Placeholders: If required, add a validation check inside the interceptor to throw an error if any unreplaced :param or {param} tokens remain in config.url after processing.