axios.get() accepts a request URL and one optional configuration object. Query parameters and HTTP headers are not separate positional arguments; they are properties of that configuration object.

That boundary matters because code that treats params and headers as independent config arguments either becomes invalid JavaScript or places data where Axios does not read it.

The GET method has one config boundary

The call shape is:

axios.get(url, config)

The request configuration can contain several concerns at once:

axios.get('/merchants', {
  params: {
    search: searchTerm,
    sort: `${sortField}.${sortDirection}`,
  },
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

params controls query-string serialization. headers controls request headers. Both belong to the same configuration object.

The request can therefore be represented at the HTTP layer as:

GET /merchants?search=acme&sort=name.asc
Authorization: Bearer <token>

The query string and the header occupy different parts of HTTP, but Axios receives their per-request configuration through the same JavaScript object.

An anonymous object inside another object is invalid syntax

A common mistake is to write a second object literal inside the config object without assigning it to a property:

axios.get('/merchants', {
  params: {
    search: searchTerm,
  },
  {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  },
});

The outer braces already define an object literal. After params, JavaScript expects another property definition. A standalone object literal is not a valid property entry in that position.

The correction is structural:

axios.get('/merchants', {
  params: {
    search: searchTerm,
  },
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

params and headers are sibling properties of one object.

A third argument is not a second Axios config

JavaScript functions can be called with more arguments than a function uses, which makes another variant less obvious:

axios.get(
  '/merchants',
  {
    params: {
      search: searchTerm,
    },
  },
  {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  },
);

This is valid JavaScript syntax, but it does not match the Axios GET API shape. The GET request alias takes the URL followed by one optional config object. A third object is not a second request config.

Keep every per-request option inside the second argument:

axios.get('/merchants', {
  params: {
    search: searchTerm,
  },
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Other request options use the same object

The same rule applies when the request also needs timeout or cancellation:

const controller = new AbortController();

axios.get('/merchants', {
  params: {
    search: searchTerm,
    sort: `${sortField}.${sortDirection}`,
  },
  headers: {
    Authorization: `Bearer ${token}`,
  },
  timeout: 5000,
  signal: controller.signal,
});

params, headers, timeout, and signal are request-config properties. Adding another request option does not add another positional config argument.

The configuration can also be assembled before dispatch:

const config = {
  params: {
    search: searchTerm,
    sort: `${sortField}.${sortDirection}`,
  },
  headers: {
    Authorization: `Bearer ${token}`,
  },
};

const response = await axios.get('/merchants', config);

A named config object is useful when code needs to add or inspect options before sending the request.

Promise chains do not change request configuration

Whether response handling uses .then() or async/await does not change the Axios call signature.

A Promise chain can keep the request configuration in one place:

loadingData = true;

axios.get(`${PUBLIC_API_BASE_URL}/merchants`, {
  params: {
    search: searchTerm,
    sort: `${sortField}.${sortDirection}`,
  },
  headers: {
    Authorization: `Bearer ${token}`,
  },
})
  .then((response) => {
    data = response.data.data;
  })
  .catch((error) => {
    console.error('Failed to fetch merchants:', error);
    toast.error('Failed to fetch merchants');
  })
  .finally(() => {
    loadingData = false;
  });

The equivalent async/await form keeps the same request config:

async function fetchMerchants() {
  loadingData = true;

  try {
    const response = await axios.get(
      `${PUBLIC_API_BASE_URL}/merchants`,
      {
        params: {
          search: searchTerm,
          sort: `${sortField}.${sortDirection}`,
        },
        headers: {
          Authorization: `Bearer ${token}`,
        },
      },
    );

    data = response.data.data;
  } catch (error) {
    console.error('Failed to fetch merchants:', error);
    toast.error('Failed to fetch merchants');
  } finally {
    loadingData = false;
  }
}

The control-flow style affects readability, not where params or headers belong.

Shared authentication can move out of individual calls

If many requests use the same bearer token, repeating the header in every call adds duplication. An Axios instance and a request interceptor can centralize shared authentication.

const api = axios.create({
  baseURL: PUBLIC_API_BASE_URL,
});

api.interceptors.request.use((config) => {
  const token = getAccessToken();

  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }

  return config;
});

The individual request then only carries request-specific options:

const response = await api.get('/merchants', {
  params: {
    search: searchTerm,
    sort: `${sortField}.${sortDirection}`,
  },
});

This does not change the boundary. Axios still builds one effective request configuration from defaults, interceptors, and the per-request config object.

Treat the config object as the request control surface

A compact model is:

axios.get(
  URL,
  {
    params,
    headers,
    timeout,
    signal,
    ...other request options
  }
)

The HTTP concepts remain distinct, but the Axios API groups their per-request configuration under one object.

When a GET request needs query parameters and authorization together, adding another config object is the wrong abstraction. Put params and headers beside each other inside the single config argument that follows the URL.