Using Axios in Node? Have a look at got

Axios is the Node HTTP client most people reach for by default, but got and Node's built-in fetch (Node 18+, backed by undici) are both worth a look first.

How they compare

gotaxiosbuilt-in fetch
RuntimeNode onlyNode + browsersNode 18+ (and browsers)
ModulesESM-only since v12CJS + ESMn/a (global)
MiddlewareHooks via .extend()Interceptors via .create()None (wrap it yourself)
Built-in retryYes, configurableNo (third-party)No
HTTP/2YesNoYes (via undici)
Pagination helpersYesNoNo
Timeout / abortFirst-classFirst-classAbortSignal.timeout(ms)

If you don't need retries, hooks, streams, or pagination, prefer the built-in fetch — one less dependency. Reach for got when you want first-class retries, HTTP/2, or pagination. Reach for axios when you also need browser parity and the larger interceptor ecosystem.

Hooks vs interceptors

Same task, different shapes — adding a custom header to every request from an instance:

1import got from 'got' 2 3const client = got.extend({ 4 prefixUrl: 'https://jsonplaceholder.typicode.com', 5 hooks: { 6 beforeRequest: [(opts) => { 7 opts.headers['X-Custom-Header'] = 'CustomHeaderValue' 8 }], 9 }, 10}) 11 12try { 13 const data = await client.get('posts/1').json() 14 console.log(data) 15} catch (err) { 16 console.error(err) 17}
1import axios from 'axios' 2 3const client = axios.create({ 4 baseURL: 'https://jsonplaceholder.typicode.com', 5}) 6 7client.interceptors.request.use((config) => { 8 config.headers['X-Custom-Header'] = 'CustomHeaderValue' 9 return config 10}) 11 12try { 13 const { data } = await client.get('/posts/1') 14 console.log(data) 15} catch (err) { 16 console.error(err) 17}

got returns response shorthand via .json(); axios puts the parsed body on response.data.