Skip to content

feat(axios-to-whatwg-fetch) #191

@AugustinMauroy

Description

@AugustinMauroy

Description

Since the fetch api is now available in Node.js, many use cases for Axios can be replaced with it. This can help reduce dependencies, reduce risk, and improve performance.

Examples

Axios often provides API that can be replaced by WHATWG Fetch.

  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.options(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])

Get

Before:

const base = 'https://dummyjson.com/todos';

const all = await axios.get(base)
console.log('\nGET /todos ->', all.status);
console.log(`Preview: ${all.data.todos.length} todos`);

After:

const base = 'https://dummyjson.com/todos';

const all = await fetch(base).then(async (res) => Object.assign(res, { data: await res.json() })).catch(() => null);
console.log('\nGET /todos ->', all.status);
console.log(`Preview: ${all.data.todos.length} todos`);

Post

Before:

const base = 'https://dummyjson.com/todos';

const created = await axios.post(
    `${base}/add`, {
        todo: 'Use DummyJSON in the project',
        completed: false,
        userId: 5,
    }, {
        headers: { 'Content-Type': 'application/json' }
    }
);
console.log('\nPOST /todos/add ->', created.status);
console.log('Preview:', created.data?.id ? `created id ${created.data.id}` : JSON.stringify(created.data).slice(0,200));

After:

const base = 'https://dummyjson.com/todos';


const created = await fetch(`${base}/add`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        todo: 'Use DummyJSON in the project',
        completed: false,
        userId: 5,
    }),
}).then(async (res) => Object.assign(res, { data: await res.json() }));
console.log('\nPOST /todos/add ->', created.status);
console.log('Preview:', created.data?.id ? `created id ${created.data.id}` : JSON.stringify(created.data).slice(0,200));

Put

Before:

const base = 'https://dummyjson.com/todos';

const updatedPut = await axios.put(
    `${base}/1`,
    { completed: false },
    { headers: { 'Content-Type': 'application/json' } }
);
console.log('\nPUT /todos/1 ->', updatedPut.status);
console.log('Preview:', updatedPut.data?.completed !== undefined ? `completed=${updatedPut.data.completed}` : JSON.stringify(updatedPut.data).slice(0,200));

After:

const base = 'https://dummyjson.com/todos';

const updatedPut = await fetch(`${base}/1`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ completed: false }),
}).then(async (res) => Object.assign(res, { data: await res.json() }))
console.log('\nPUT /todos/1 ->', updatedPut.status);
console.log('Preview:', updatedPut.data?.completed !== undefined ? `completed=${updatedPut.data.completed}` : JSON.stringify(updatedPut.data).slice(0,200));

Patch

Before:

const base = 'https://dummyjson.com/todos';

const updatedPatch = await axios.patch(
    `${base}/1`,
    { completed: true },
    { headers: { 'Content-Type': 'application/json' } }
);
console.log('\nPATCH /todos/1 ->', updatedPatch.status);
console.log('Preview:', updatedPatch.data?.completed !== undefined ? `completed=${updatedPatch.data.completed}` : JSON.stringify(updatedPatch.data).slice(0,200));

After:

const base = 'https://dummyjson.com/todos';

const updatedPatch = await fetch(`${base}/1`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ completed: true }),
}).then(async (res) => Object.assign(res, { data: await res.json() }));
console.log('\nPATCH /todos/1 ->', updatedPatch.status);
console.log('Preview:', updatedPatch.data?.completed !== undefined ? `completed=${updatedPatch.data.completed}` : JSON.stringify(updatedPatch.data).slice(0,200));

Delete

Before:

const base = 'https://dummyjson.com/todos';

const deleted = await axios.delete(`${base}/1`);
console.log('\nDELETE /todos/1 ->', deleted.status);
console.log('Preview:', deleted.data ? JSON.stringify(deleted.data).slice(0,200) : typeof deleted.data);

After:

const base = 'https://dummyjson.com/todos';

const deleted = await fetch(`${base}/1`, { method: 'DELETE' })
.then(async (res) => Object.assign(res, { data: await res.json() }));
console.log('\nDELETE /todos/1 ->', deleted.status);
console.log('Preview:', deleted.data ? JSON.stringify(deleted.data).slice(0,200) : typeof deleted.data);

Unsupported API

Axios provides some features that are not directly supported by WHATWG Fetch or too complex/risky to implement. These include:

  • axios.postForm(url[, data[, config]])
  • axios.putForm(url[, data[, config]])
  • axios.patchForm(url[, data[, config]])

So for these cases, the codemod should just warn the user with correct format file://<path-to-your-file>:<line-number>.

codemod structure

For a better maintainability, we should use diferent step on codemod workflow definition. I think it's can be something like import-process, supported-api and unsupported-api.

The test command for one node will be something like npx codemod jssg test -l typescript ./src/import-process.ts ./tests/ --filter import-process

The tests dirs will look like that:

tests/
├─ node1/
│  ├─ input/
│  │  ├─ file1.js
│  ├─ expected/
│  │  ├─ file1.js
├─ node2/
│  ├─ input/
│  │  ├─ file1.js
│  ├─ expected/
│  │  ├─ file1.js

REFS

Metadata

Metadata

Assignees

Projects

Status

🔖 Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions