Axios Config: Shallow vs Deep Merging Explained

When making HTTP requests with Axios, configurations can be defined at the global level, on a specific Axios instance, or individually within a single request. Understanding the difference between shallow and deep merging of these configurations is essential for effectively managing headers, query parameters, and other request options without accidentally wiping out critical default settings. This article breaks down how shallow and deep merging work in Axios, which properties are affected by each approach, and how to properly structure your request configurations.

Shallow Merging

Shallow merging (often achieved using Object.assign() or the object spread operator ...) copies top-level properties from one object to another. If a property value is a primitive (such as a string, number, or boolean), the value is overwritten. If a property value is a nested object, the shallow merge completely replaces the entire target object reference with the source object reference instead of combining their inner properties.

Example in Axios Context

If Axios used pure shallow merging for all properties, a scenario with query parameters would behave like this:

// Instance configuration
const api = axios.create({
  params: {
    apiKey: '12345',
    version: 'v1'
  }
});

// Request configuration
api.get('/users', {
  params: {
    limit: 10
  }
});

Under a shallow merge, the request's params object ({ limit: 10 }) completely replaces the instance params object. As a result, apiKey and version are discarded, and only limit=10 is sent to the server.

Deep Merging

Deep merging (recursive merging) inspects nested objects. When both the source and target contain an object at the same key, deep merging enters those objects and merges their sub-properties rather than replacing the parent object entirely.

Example in Axios Context

Using the previous example under a deep merge strategy, the resulting params object would be:

{
  apiKey: '12345',
  version: 'v1',
  limit: 10
}

Both the default configuration properties and the request-level properties are preserved.

How Axios Handles Merging

Axios does not apply a single blanket merge strategy across all configuration properties. Instead, it uses a hybrid approach based on the property type:

1. Headers (Deep Merged)

The headers configuration receives specialized deep merging. Axios combines headers from multiple layers according to a strict hierarchy:

  1. axios.defaults.headers.common
  2. axios.defaults.headers[method] (e.g., get, post)
  3. Instance-level headers
  4. Request-level headers

If you define an Authorization header at the instance level and a Content-Type header at the request level, Axios deep-merges them so both headers are sent in the final HTTP request.

2. General Configuration Properties (Shallowly Replaced)

Most non-header properties use shallow replacement. These include:

For these properties, whatever you specify at the request level completely replaces the default or instance-level setting.

3. Arrays (Replaced, Not Merged)

Properties that hold arrays, such as transformRequest and transformResponse, are completely overwritten by lower-level configurations rather than concatenated.

Summary of Differences

Feature Shallow Merging Deep Merging
Object Handling Replaces nested objects entirely Merges sub-properties inside nested objects
Axios Usage Applied to params, auth, data, arrays, and primitives Applied specifically to headers
Risk Can unintentionally drop baseline nested properties Can unintentionally retain stale sub-properties
Precedence Later top-level values overwrite earlier top-level values Deeper values overwrite earlier values at the same key path

To avoid unexpected request behavior, always supply complete nested objects (such as params) when overriding them at the request level, while relying on Axios to automatically preserve and merge your baseline headers.