|
| 1 | +# Axios to WHATWG Fetch Codemod |
| 2 | + |
| 3 | +## Description |
| 4 | + |
| 5 | +This codemod transforms code using Axios to leverage the WHATWG Fetch API, which is now natively available in Node.js. By replacing Axios with Fetch, you can reduce dependencies, mitigate risks, and improve performance. |
| 6 | + |
| 7 | +## Supported Transformations |
| 8 | + |
| 9 | +The codemod supports the following Axios methods and converts them to their Fetch equivalents: |
| 10 | + |
| 11 | +- `axios.request(config)` |
| 12 | +- `axios.get(url[, config])` |
| 13 | +- `axios.delete(url[, config])` |
| 14 | +- `axios.head(url[, config])` |
| 15 | +- `axios.options(url[, config])` |
| 16 | +- `axios.post(url[, data[, config]])` |
| 17 | +- `axios.put(url[, data[, config]])` |
| 18 | +- `axios.patch(url[, data[, config]])` |
| 19 | +- `axios.postForm(url[, data[, config]])` |
| 20 | +- `axios.putForm(url[, data[, config]])` |
| 21 | +- `axios.patchForm(url[, data[, config]])` |
| 22 | +- `axios.request(config)` |
| 23 | + |
| 24 | +### Examples |
| 25 | + |
| 26 | +#### GET Request |
| 27 | + |
| 28 | +```diff |
| 29 | + const base = 'https://dummyjson.com/todos'; |
| 30 | + |
| 31 | +- const all = await axios.get(base); |
| 32 | ++ const all = await fetch(base).then(async (res) => Object.assign(res, { data: await res.json() })).catch(() => null); |
| 33 | + console.log('\nGET /todos ->', all.status); |
| 34 | + console.log(`Preview: ${all.data.todos.length} todos`); |
| 35 | +``` |
| 36 | + |
| 37 | +#### POST Request |
| 38 | + |
| 39 | +```diff |
| 40 | + const base = 'https://dummyjson.com/todos'; |
| 41 | + |
| 42 | +- const created = await axios.post( |
| 43 | +- `${base}/add`, { |
| 44 | +- todo: 'Use DummyJSON in the project', |
| 45 | +- completed: false, |
| 46 | +- userId: 5, |
| 47 | +- }, { |
| 48 | +- headers: { 'Content-Type': 'application/json' } |
| 49 | +- } |
| 50 | +- ); |
| 51 | ++ const created = await fetch(`${base}/add`, { |
| 52 | ++ method: 'POST', |
| 53 | ++ headers: { 'Content-Type': 'application/json' }, |
| 54 | ++ body: JSON.stringify({ |
| 55 | ++ todo: 'Use DummyJSON in the project', |
| 56 | ++ completed: false, |
| 57 | ++ userId: 5, |
| 58 | ++ }), |
| 59 | ++ }).then(async (res) => Object.assign(res, { data: await res.json() })); |
| 60 | + console.log('\nPOST /todos/add ->', created.status); |
| 61 | + console.log('Preview:', created.data?.id ? `created id ${created.data.id}` : JSON.stringify(created.data).slice(0,200)); |
| 62 | +``` |
| 63 | + |
| 64 | + #### POST Form Request |
| 65 | + |
| 66 | + ```diff |
| 67 | + const formEndpoint = '/submit'; |
| 68 | + |
| 69 | + - const created = await axios.postForm(formEndpoint, { |
| 70 | + - title: 'Form Demo', |
| 71 | + - completed: false, |
| 72 | + - }); |
| 73 | + + const created = await fetch(formEndpoint, { |
| 74 | + + method: 'POST', |
| 75 | + + body: new URLSearchParams({ |
| 76 | + + title: 'Form Demo', |
| 77 | + + completed: false, |
| 78 | + + }), |
| 79 | + + }).then(async (res) => Object.assign(res, { data: await res.json() })); |
| 80 | + console.log('Preview:', created.data); |
| 81 | + ``` |
| 82 | + |
| 83 | +#### PUT Request |
| 84 | + |
| 85 | +```diff |
| 86 | + const base = 'https://dummyjson.com/todos'; |
| 87 | + |
| 88 | +- const updatedPut = await axios.put( |
| 89 | +- `${base}/1`, |
| 90 | +- { completed: false }, |
| 91 | +- { headers: { 'Content-Type': 'application/json' } } |
| 92 | +- ); |
| 93 | ++ const updatedPut = await fetch(`${base}/1`, { |
| 94 | ++ method: 'PUT', |
| 95 | ++ headers: { 'Content-Type': 'application/json' }, |
| 96 | ++ body: JSON.stringify({ completed: false }), |
| 97 | ++ }).then(async (res) => Object.assign(res, { data: await res.json() })); |
| 98 | + console.log('\nPUT /todos/1 ->', updatedPut.status); |
| 99 | + console.log('Preview:', updatedPut.data?.completed !== undefined ? `completed=${updatedPut.data.completed}` : JSON.stringify(updatedPut.data).slice(0,200)); |
| 100 | +``` |
| 101 | + |
| 102 | +#### DELETE Request |
| 103 | + |
| 104 | +```diff |
| 105 | + const base = 'https://dummyjson.com/todos'; |
| 106 | + |
| 107 | +- const deleted = await axios.delete(`${base}/1`); |
| 108 | ++ const deleted = await fetch(`${base}/1`, { method: 'DELETE' }) |
| 109 | ++ .then(async (res) => Object.assign(res, { data: await res.json() })); |
| 110 | + console.log('\nDELETE /todos/1 ->', deleted.status); |
| 111 | + console.log('Preview:', deleted.data ? JSON.stringify(deleted.data).slice(0,200) : typeof deleted.data); |
| 112 | +``` |
| 113 | + |
| 114 | +#### `request` axios Method |
| 115 | + |
| 116 | +```diff |
| 117 | + const base = 'https://dummyjson.com/todos'; |
| 118 | + |
| 119 | +- const customRequest = await axios.request({ |
| 120 | +- url: `${base}/1`, |
| 121 | +- method: 'PATCH', |
| 122 | +- headers: { 'Content-Type': 'application/json' }, |
| 123 | +- data: { completed: true }, |
| 124 | +- }); |
| 125 | ++ const customRequest = await fetch(`${base}/1`, { |
| 126 | ++ method: 'PATCH', |
| 127 | ++ headers: { 'Content-Type': 'application/json' }, |
| 128 | ++ body: JSON.stringify({ completed: true }), |
| 129 | ++ }).then(async (res) => Object.assign(res, { data: await res.json() })); |
| 130 | + console.log('\nPATCH /todos/1 ->', customRequest.status); |
| 131 | + console.log('Preview:', customRequest.data?.completed !== undefined ? `completed=${customRequest.data.completed}` : JSON.stringify(customRequest.data).slice(0,200)); |
| 132 | +``` |
| 133 | + |
| 134 | +## Unsupported APIs |
| 135 | + |
| 136 | +The codemod does not yet cover Axios features outside of direct request helpers, such as interceptors, cancel tokens, or instance configuration from `axios.create()`. |
| 137 | + |
| 138 | +## References |
| 139 | + |
| 140 | +- [Fetch Spec](https://fetch.spec.whatwg.org) |
| 141 | +- [Axios Documentation](https://axios-http.com) |
| 142 | +- [Node.js Documentation](https://nodejs.org/docs/latest/api/globals.html#fetch) |
0 commit comments