Custom Query Parameter Serialization in Axios with qs
Axios is a popular promise-based HTTP client for JavaScript, but its
default query parameter serialization cannot always handle complex data
structures like nested objects or custom-formatted arrays. To overcome
this limitation, Axios provides the paramsSerializer
configuration option, which allows developers to delegate query string
creation to specialized libraries such as qs. By
integrating qs, you gain complete control over how arrays,
indices, nested properties, and special characters are encoded into
request URLs.
Why Axios Requires Custom Serialization
By default, Axios uses basic internal mechanisms (or
URLSearchParams in modern environments) to serialize the
params object into a URL query string. While this works for
simple key-value pairs, it often fails or produces unwanted results when
handling:
- Arrays (e.g., producing
ids=1&ids=2instead ofids[]=1&ids[]=2orids=1,2). - Deeply nested objects (e.g.,
filter: { status: 'active', role: 'admin' }). - Custom encoding rules for reserved characters.
Backend frameworks often expect specific query parameter formats, making standard serialization incompatible.
Integrating
qs with paramsSerializer
The qs library is a querystring parsing and stringifying
library supporting security features, nested objects, and diverse array
formatting options.
Basic Implementation in Axios v1.x
In modern Axios versions (v1.0.0 and newer),
paramsSerializer accepts an object with a custom
serialize function or configuration options.
import axios from 'axios';
import qs from 'qs';
const response = await axios.get('https://api.example.com/items', {
params: {
categories: ['electronics', 'appliances'],
filter: { inStock: true, priceLimit: 500 }
},
paramsSerializer: {
serialize: (params) => qs.stringify(params, { arrayFormat: 'brackets' })
}
});Global Configuration with Axios Instances
To avoid specifying paramsSerializer in every single
request, you can configure it globally on a custom Axios instance
created via axios.create:
import axios from 'axios';
import qs from 'qs';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
paramsSerializer: {
serialize: (params) => qs.stringify(params, {
arrayFormat: 'repeat',
allowDots: true,
skipNulls: true
})
}
});
// Used automatically on all requests with params
apiClient.get('/search', { params: { tags: ['js', 'node'], user: null } });
// URL Result: /search?tags=js&tags=nodeConfiguring Array Formatting
in qs
Different APIs expect arrays formatted in different ways. The
arrayFormat option in qs allows you to switch
between standard conventions:
indices(default inqs):tags[0]=1&tags[1]=2brackets:tags[]=1&tags[]=2repeat:tags=1&tags=2comma:tags=1,2
qs.stringify({ ids: [10, 20] }, { arrayFormat: 'comma' });
// Output: ids=10%2C20 (or ids=10,20 depending on encode setting)Handling Nested Objects
When working with nested objects, qs provides the
allowDots option to switch from bracket notation to dot
notation:
- Bracket Notation (default):
user[name]=John&user[age]=30 - Dot Notation (
allowDots: true):user.name=John&user.age=30
Using paramsSerializer with qs ensures that
your outbound Axios requests match whatever query parameter format your
target API requires without needing manual URL string construction.