|
| 1 | +--- |
| 2 | +date: '2026-05-09T00:00:00.000Z' |
| 3 | +category: migrations |
| 4 | +title: Axios to WHATWG Fetch |
| 5 | +layout: blog-post |
| 6 | +author: AugustinMauroy |
| 7 | +--- |
| 8 | + |
| 9 | +# Migrate from Axios to WHATWG Fetch |
| 10 | + |
| 11 | +This codemod transforms code using [Axios](https://github.com/axios/axios) to leverage the [WHATWG Fetch API](https://fetch.spec.whatwg.org/), which is now natively available in Node.js. |
| 12 | + |
| 13 | +## Why doing this? |
| 14 | + |
| 15 | +- **Native Support**: Fetch is built into Node.js, eliminating the need for external libraries and their associated maintenance overhead. |
| 16 | +- **Improved Performance**: Fetch is optimized for modern JavaScript runtimes, often resulting in better performance compared to Axios. |
| 17 | +- **Better Standards Compliance**: Fetch adheres closely to web standards, making it easier to write cross-platform code that works both in Node.js and browsers. |
| 18 | +- **Reduced Security Risks**: Removing Axios eliminates potential vulnerabilities associated with third-party dependencies, enhancing the security of your application. |
| 19 | + |
| 20 | +## Node.js Version Requirements |
| 21 | + |
| 22 | +- Node.js v18.0.0 or later (Fetch API is available but marked experimental) |
| 23 | +- Node.js v21.0.0 or later (Fetch API is stable) |
| 24 | + |
| 25 | +> If your package currently supports Node.js versions earlier than v18.0.0, you cannot migrate to the Fetch API without dropping support for those versions. |
| 26 | +> This requires bumping the major version of your package AND updating the engines field in your package.json to require Node.js >= v18.0.0. |
| 27 | +
|
| 28 | +## Supported Transformations |
| 29 | + |
| 30 | +The codemod supports the following Axios methods and converts them to their Fetch equivalents: |
| 31 | + |
| 32 | +- `axios.request(config)` |
| 33 | +- `axios.get(url[, config])` |
| 34 | +- `axios.delete(url[, config])` |
| 35 | +- `axios.head(url[, config])` |
| 36 | +- `axios.options(url[, config])` |
| 37 | +- `axios.post(url[, data[, config]])` |
| 38 | +- `axios.put(url[, data[, config]])` |
| 39 | +- `axios.patch(url[, data[, config]])` |
| 40 | +- `axios.postForm(url[, data[, config]])` |
| 41 | +- `axios.putForm(url[, data[, config]])` |
| 42 | +- `axios.patchForm(url[, data[, config]])` |
| 43 | + |
| 44 | +## Examples |
| 45 | + |
| 46 | +### GET Request |
| 47 | + |
| 48 | +```diff |
| 49 | +const base = 'https://dummyjson.com/todos'; |
| 50 | + |
| 51 | +- const all = await axios.get(base); |
| 52 | ++ const all = await fetch(base).then(async (res) => Object.assign(res, { data: await res.json() })).catch(() => null); |
| 53 | + console.log('\nGET /todos ->', all.status); |
| 54 | + console.log(`Preview: ${all.data.todos.length} todos`); |
| 55 | +``` |
| 56 | + |
| 57 | +### POST Request |
| 58 | + |
| 59 | +```diff |
| 60 | +const base = 'https://dummyjson.com/todos'; |
| 61 | + |
| 62 | +- const created = await axios.post( |
| 63 | +- `${base}/add`, { |
| 64 | +- todo: 'Use DummyJSON in the project', |
| 65 | +- completed: false, |
| 66 | +- userId: 5, |
| 67 | +- }, { |
| 68 | +- headers: { 'Content-Type': 'application/json' } |
| 69 | +- } |
| 70 | +- ); |
| 71 | ++ const created = await fetch(`${base}/add`, { |
| 72 | ++ method: 'POST', |
| 73 | ++ headers: { 'Content-Type': 'application/json' }, |
| 74 | ++ body: JSON.stringify({ |
| 75 | ++ todo: 'Use DummyJSON in the project', |
| 76 | ++ completed: false, |
| 77 | ++ userId: 5, |
| 78 | ++ }), |
| 79 | ++ }).then(async (res) => Object.assign(res, { data: await res.json() })); |
| 80 | + console.log('\nPOST /todos/add ->', created.status); |
| 81 | + console.log('Preview:', created.data?.id ? `created id ${created.data.id}` : JSON.stringify(created.data).slice(0,200)); |
| 82 | +``` |
| 83 | + |
| 84 | +### POST Form Request |
| 85 | + |
| 86 | +```diff |
| 87 | +const formEndpoint = '/submit'; |
| 88 | + |
| 89 | +- const created = await axios.postForm(formEndpoint, { |
| 90 | +- title: 'Form Demo', |
| 91 | +- completed: false, |
| 92 | +- }); |
| 93 | ++ const created = await fetch(formEndpoint, { |
| 94 | ++ method: 'POST', |
| 95 | ++ body: new URLSearchParams({ |
| 96 | ++ title: 'Form Demo', |
| 97 | ++ completed: false, |
| 98 | ++ }), |
| 99 | ++ }).then(async (res) => Object.assign(res, { data: await res.json() })); |
| 100 | + console.log('Preview:', created.data); |
| 101 | +``` |
| 102 | + |
| 103 | +### PUT Request |
| 104 | + |
| 105 | +```diff |
| 106 | +const base = 'https://dummyjson.com/todos'; |
| 107 | + |
| 108 | +- const updatedPut = await axios.put( |
| 109 | +- `${base}/1`, |
| 110 | +- { completed: false }, |
| 111 | +- { headers: { 'Content-Type': 'application/json' } } |
| 112 | +- ); |
| 113 | ++ const updatedPut = await fetch(`${base}/1`, { |
| 114 | ++ method: 'PUT', |
| 115 | ++ headers: { 'Content-Type': 'application/json' }, |
| 116 | ++ body: JSON.stringify({ completed: false }), |
| 117 | ++ }).then(async (res) => Object.assign(res, { data: await res.json() })); |
| 118 | + console.log('\nPUT /todos/1 ->', updatedPut.status); |
| 119 | + console.log('Preview:', updatedPut.data?.completed !== undefined ? `completed=${updatedPut.data.completed}` : JSON.stringify(updatedPut.data).slice(0,200)); |
| 120 | +``` |
| 121 | + |
| 122 | +### DELETE Request |
| 123 | + |
| 124 | +```diff |
| 125 | +const base = 'https://dummyjson.com/todos'; |
| 126 | + |
| 127 | +- const deleted = await axios.delete(`${base}/1`); |
| 128 | ++ const deleted = await fetch(`${base}/1`, { method: 'DELETE' }) |
| 129 | ++ .then(async (res) => Object.assign(res, { data: await res.json() })); |
| 130 | + console.log('\nDELETE /todos/1 ->', deleted.status); |
| 131 | + console.log('Preview:', deleted.data ? JSON.stringify(deleted.data).slice(0,200) : typeof deleted.data); |
| 132 | +``` |
| 133 | + |
| 134 | +### `request` Axios Method |
| 135 | + |
| 136 | +```diff |
| 137 | +const base = 'https://dummyjson.com/todos'; |
| 138 | + |
| 139 | +- const customRequest = await axios.request({ |
| 140 | +- url: `${base}/1`, |
| 141 | +- method: 'PATCH', |
| 142 | +- headers: { 'Content-Type': 'application/json' }, |
| 143 | +- data: { completed: true }, |
| 144 | +- }); |
| 145 | ++ const customRequest = await fetch(`${base}/1`, { |
| 146 | ++ method: 'PATCH', |
| 147 | ++ headers: { 'Content-Type': 'application/json' }, |
| 148 | ++ body: JSON.stringify({ completed: true }), |
| 149 | ++ }).then(async (res) => Object.assign(res, { data: await res.json() })); |
| 150 | +console.log('\nPATCH /todos/1 ->', customRequest.status); |
| 151 | +console.log('Preview:', customRequest.data?.completed !== undefined ? `completed=${customRequest.data.completed}` : JSON.stringify(customRequest.data).slice(0,200)); |
| 152 | +``` |
| 153 | + |
| 154 | +## Unsupported APIs |
| 155 | + |
| 156 | +The codemod does not yet cover Axios features outside of direct request helpers, such as interceptors, cancel tokens, or instance configuration from `axios.create()`. |
| 157 | + |
| 158 | +## Recognition |
| 159 | + |
| 160 | +We would like to thank the maintainers of [Axios](https://github.com/axios/axios) for their support of the package over time and for its contributions to the ecosystem. |
0 commit comments