Skip to content

Commit 9cd0fda

Browse files
AugustinMauroybrunocrohJakobJingleheimer
authored
feat(axios-to-whatwg-fetch): introduce (#269)
Co-authored-by: Bruno Rodrigues <swe@brunocroh.com> Co-authored-by: Jacob Smith <3012099+JakobJingleheimer@users.noreply.github.com>
1 parent 9759be3 commit 9cd0fda

67 files changed

Lines changed: 1381 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package-lock.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
schema_version: "1.0"
2+
name: "@nodejs/axios-to-whatwg-fetch"
3+
version: 1.0.0
4+
description: Replace `axios` with `fetch`
5+
author: Bruno Rodrigues
6+
license: MIT
7+
workflow: workflow.yaml
8+
category: migration
9+
10+
targets:
11+
languages:
12+
- javascript
13+
- typescript
14+
15+
keywords:
16+
- transformation
17+
- migration
18+
19+
registry:
20+
access: public
21+
visibility: public
22+
23+
# needed for removing dependencies
24+
capabilities:
25+
- fs
26+
- child_process
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "@nodejs/axios-to-whatwg-fetch",
3+
"version": "1.0.0",
4+
"description": "Replace `axios` with `fetch`",
5+
"type": "module",
6+
"scripts": {
7+
"test": "node --run test:workflow && node --run test:remove-dependencies",
8+
"test:workflow": "npx codemod jssg test -l tsx ./src/workflow.ts --strictness cst",
9+
"test:remove-dependencies": "npx codemod jssg test -l json ./src/remove-dependencies.ts ./tests/remove-dependencies --allow-child-process --allow-fs --strictness cst"
10+
},
11+
"repository": {
12+
"type": "git",
13+
"url": "git+https://github.com/nodejs/userland-migrations.git",
14+
"directory": "recipes/axios-to-whatwg-fetch",
15+
"bugs": "https://github.com/nodejs/userland-migrations/issues"
16+
},
17+
"author": "Bruno Rodrigues",
18+
"license": "MIT",
19+
"homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/axios-to-whatwg-fetch/README.md",
20+
"devDependencies": {
21+
"@codemod.com/jssg-types": "^1.0.9"
22+
},
23+
"dependencies": {
24+
"@nodejs/codemod-utils": "*",
25+
"dedent": "^1.7.0"
26+
}
27+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Transform } from '@codemod.com/jssg-types/main';
2+
import type Json from '@codemod.com/jssg-types/langs/json';
3+
import removeDependencies from '@nodejs/codemod-utils/remove-dependencies';
4+
5+
/**
6+
* Remove `axios` and `@types/axios` dependencies from package.json
7+
*/
8+
const transform: Transform<Json> = async (root) => {
9+
return removeDependencies(['axios', '@types/axios'], {
10+
packageJsonPath: root.filename(),
11+
runInstall: false,
12+
persistFileWrite: false,
13+
});
14+
};
15+
16+
export default transform;

0 commit comments

Comments
 (0)