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
got | axios | built-in fetch | |
|---|---|---|---|
| Runtime | Node only | Node + browsers | Node 18+ (and browsers) |
| Modules | ESM-only since v12 | CJS + ESM | n/a (global) |
| Middleware | Hooks via .extend() | Interceptors via .create() | None (wrap it yourself) |
| Built-in retry | Yes, configurable | No (third-party) | No |
| HTTP/2 | Yes | No | Yes (via undici) |
| Pagination helpers | Yes | No | No |
| Timeout / abort | First-class | First-class | AbortSignal.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.