99

Concurrent visitors over the last 30 minutes. (~)

Peak concurrent visitors -
Yesterday: 0 / Today: 0

Fetch vs Axios - A Guide to HTTP Data Communication

Fetch vs Axios - A Guide to HTTP Data Communication

Data communication between the server and the client is one of the core concepts of web development. In this post, we compare the Fetch function and the Axios library for data communication in JavaScript and learn how to use each one.

Fetch is a built-in function available in web browsers, and it lets you get data from a server over HTTP.
Axios is a JavaScript library for HTTP communication, like the fetch function, and it provides several convenient features.

# Data communication

The client-server model is the basic structure of communication.
When a client requests information from a server, the server responds to that request, and they use HTTP (HyperText Transfer Protocol) to do so.

HTTP is a communication protocol for exchanging data on the web.

Client-server model

Requests and responses include the following information by default.

  • Request:

    • url: The address of the server to request
    • method: The kind of request (GET, POST, PUT, DELETE, and so on)
    • headers: Request metadata
    • body: Request data
  • Response:

    • status: The response status code (200, 400, 500, and so on)
    • headers: Response metadata
    • body: Response data
    • ok: Whether the request was handled successfully

# URL

A URL is an address that shows where a specific resource is located on the internet.
It has the following structure.

URL structure

# Method

method indicates the kind of request, and it is mainly used like this.

The meaning of each method is a convention, not a strict standard.
The meaning can differ depending on how the server is developed.

  • GET: Read data
  • POST: Create data
  • PUT / PATCH: Update data (all/part)
  • DELETE: Delete data

Even for the same URL, how the server handles the request and what it responds with depend on the kind of request (method).
Also, you cannot use methods that the server does not support.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Get all users fetch('https://api.heropy.dev/v0/users', { method: 'GET' // Or you can leave it out! }) // Or fetch('https://api.heropy.dev/v0/users') // Create a new user fetch('https://api.heropy.dev/v0/users', { method: 'POST', body: JSON.stringify({ name: 'HEROPY', age: 85, emails: ['thesecon@gmail.com'] }) }) // Update user information fetch('https://api.heropy.dev/v0/users/ywTTX', { method: 'PUT', body: JSON.stringify({ name: "HEROPY", emails: ['thesecon@gmail.com'] }) }) // Delete a user fetch('https://api.heropy.dev/v0/users/ywTTX', { method: 'DELETE' })
Examples of different kinds of requests (methods)

# Headers

headers contain metadata about the request and the response.
Metadata can include many kinds of information, such as the data type, encoding, authentication information, and cache control.
You write them as an object, like this.

JS
1
2
3
4
5
6
7
8
9
10
fetch('https://api.heropy.dev/v0/users', { headers: { 'Content-type': 'application/json', Apikey: 'KDnREmPe9B1', Username: 'ParkYoungWoong', Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...', 'X-Username': 'ParkYoungWoong', '...' } })

Headers with the X- prefix, such as X-Frame-Options and X-Username, are custom non-standard headers.
They are not part of the official HTTP specification, and they are used by developers or organizations to mark a specific purpose or meaning.
The IETF (Internet Engineering Task Force) recommends using custom headers without the X- prefix.

# Body

body is the content (the actual data) you want to send with a request or response.
You mostly send text (JSON) or FormData.

JS
1
2
3
4
5
6
7
8
9
10
11
fetch('https://api.heropy.dev/v0/users', { method: 'POST', headers: { 'Content-type': 'application/json' }, // User information (data) to create body: JSON.stringify({ name: 'HEROPY', age: 85 }) })

# Status

status indicates the response status code for the request.
HTTP status codes fall into the following five categories.

  • 1xx: Processing
  • 2xx: Success
  • 3xx: Redirection
  • 4xx: Client error
  • 5xx: Server error

Here are the status codes you will see most often.

  • 200: Handled successfully
  • 400: Bad request
  • 401: Missing authentication information
  • 403: No permission
  • 404: Not found
  • 500: Server error

404 Page Not Found example

# Fetch vs Axios

fetch is a Web API that web browsers started supporting in 2015.
It supports asynchronous communication based on Promise, and you can use it right away without installing anything.

JS
1
2
3
fetch('https://api.heropy.dev/v0/users') .then(res => res.json()) .then(data => console.log(data)) // { total: 7, users: User[] }

axios is an HTTP client library first released around 2015.
Like fetch, it supports asynchronous communication based on Promise.
It offers cross-browser compatibility, as well as extra features such as automatic JSON data conversion and server-side support.

You can link it from a CDN,

HTML
1
<script src="https://cdn.jsdelivr.net/npm/axios@1.6.2/dist/axios.min.js"></script>
Axios CDN

or install it with NPM.

BASH
1
npm i axios
Axios NPM
JS
1
2
3
4
import axios from 'axios' axios.get('https://api.heropy.dev/v0/users') .then(res => console.log(res.data)) // { total: 7, users: User[] }

Both fetch and axios are widely used for data communication in modern web development, but they differ in their strengths and how you use them.
Simply put, you can use fetch right away without installing anything, while axios provides richer features and a more concise API.

Let's compare how to use fetch and axios through simple CRUD (Create / Read / Update / Delete) examples that use JSONPlaceholder.

# Reading data

To read data, you mostly use the GET method.

The fetch function uses the GET method by default, so you do not need to set any options.
You need to parse the data you get with .json().

JS
1
2
3
4
5
6
;(async () => { // const res = await fetch('https://api.heropy.dev/v0/users', { method: 'GET' }) const res = await fetch('https://api.heropy.dev/v0/users') const data = await res.json() console.log(data) })()
fetch

axios also uses the GET method by default, so you do not need to set any options.
It also provides a separate method (function) for each HTTP method, such as .get() and .post(), so using them explicitly is a good idea too.
You can access the data you get through the data property of the response object.

JS
1
2
3
4
5
6
7
8
import axios from 'axios' ;(async () => { // const res = await axios({ url: 'https://api.heropy.dev/v0/users', method: 'GET' }) // const res = await axios.get('https://api.heropy.dev/v0/users') const res = await axios('https://api.heropy.dev/v0/users') console.log(res.data) })()
axios

Reading does not send data, so the request should not include data to send.
With the fetch function, you must leave out the body option or explicitly set it to null or undefined. If you set any other value, an error occurs.

Error: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.

With axios, you also should not set data to send, but even if you do, it leaves the data out and sends the request.

If you want to include details about what to read in the request, you can use a query string.
For example, you can use OMDbAPI, which provides movie information.

With fetch, you can include the query string directly in the URL,

JS
1
2
3
4
5
6
7
;(async () => { const res = await fetch('https://omdbapi.com/?apikey=7035c60c&s=avengers', { method: 'GET' }) const data = await res.json() console.log(data) })()
fetch

or, if you have the query information as an object, you can use the URLSearchParams class.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
const queries = new URLSearchParams({ apikey: '7035c60c', s: 'avengers' }) console.log(queries.toString()) // 'apikey=7035c60c&s=avengers' ;(async () => { const res = await fetch(`https://omdbapi.com/?${queries}`, { method: 'GET' }) const data = await res.json() console.log(data) })()
fetch w. URLSearchParams

With axios, you can also include the query string directly in the URL, or pass the query information as an object with the params option.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
;(async () => { const res = await axios.get(`https://omdbapi.com/?apikey=7035c60c&s=avengers`) console.log(res.data) })() ;(async () => { const res = await axios({ url: `https://omdbapi.com/`, method: 'GET', params: { apikey: '7035c60c', s: 'avengers' } }) console.log(res.data) })()
axios

# Creating and updating data

When you send data to create or update, you mostly use the POST or PUT method.
POST is for creating, and PUT or PATCH is for updating.

The fetch function sends data with the body option.
Set the type of the data to send to application/json, then convert the data to a string with the JSON.stringify method and put it in body.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
;(async () => { const res = await fetch('https://api.heropy.dev/v0/users', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'HEROPY', age: 85, isValid: true, emails: ['thesecon@gmail.com'] }) }) const data = await res.json() console.log(data) })()

axios uses the data option instead of body.
You do not need to convert the data to a string yourself, because it converts it to a JSON string automatically.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
;(async () => { const res = await axios({ url: 'https://api.heropy.dev/v0/users', method: 'POST', headers: { 'Content-Type': 'application/json', }, data: { name: 'HEROPY', age: 85, isValid: true, emails: ['thesecon@gmail.com'] } }) console.log(res.data) })()

To update data, you can use the PUT or PATCH method.
In this case, you need to provide the ID of the data to identify which data to update.
You can pass it in various ways, such as in the URL, headers, or body, depending on the API specification.

PUT and PATCH both mean updating, but PUT means updating the whole data, while PATCH means updating only part of it.
In many cases, people use only PUT without separating the two meanings.

In the following example, the ID (ywTTX) of the user information to update is included in the URL.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
;(async () => { const res = await axios({ url: 'https://api.heropy.dev/v0/users/ywTTX', method: 'PUT', headers: { 'Content-Type': 'application/json', }, data: { name: "HEROPY", emails: ['thesecon@gmail.com'] } }) console.log(res.data) })()

# Deleting data

To delete data, you can use the DELETE method.
As with updating, you need to provide the ID of the data, according to the API specification, to identify which data to delete.

JS
1
2
3
4
5
6
7
;(async () => { const res = await fetch('https://api.heropy.dev/v0/users/ywTTX', { method: 'DELETE' }) const data = await res.json() console.log(data) })()
fetch
JS
1
2
3
4
5
6
7
;(async () => { const res = await axios({ url: 'https://api.heropy.dev/v0/users/ywTTX', method: 'DELETE' }) console.log(res.data) })()
axios

# Sending files

To send a file from the client to the server, you can use several approaches, such as using FormData or converting the file to a Base64 string.

# FormData

FormData, a Web API, creates form data that can be sent to a server through an HTML <form>.
On the instance (formData) returned by calling the new FormData() constructor, you can use the append method to add names and data explicitly.

JS
1
2
3
4
5
6
const formData = new FormData() // formData.append('name', data) formData.append('id', userId) formData.append('pw', userPw) formData.append('file', file) // ...
Creating FormData
JS
1
2
3
4
5
6
7
8
9
10
let file = null document.querySelector('input[type="file"]') .addEventListener('change', event => { file = event.target.files[0] }) document.querySelector('button') .addEventListener('click', async () => { if (!file) return sendFile(file) })
JS
1
2
3
4
5
6
7
8
9
10
async function sendFile(file) { const formData = new FormData() formData.append('file', file) const res = await fetch('URL', { method: 'METHOD', body: formData }) return await res.json() }
fetch
JS
1
2
3
4
5
6
7
8
9
10
11
async function sendFile(file) { const formData = new FormData() formData.append('file', file) const res = await axios({ url: 'URL', method: 'METHOD', data: formData }) return res.data }
axios

If you use a <form>, you can add data without the append method.
However, you must add the name attribute to each <input> to specify the name of the data.

HTML
1
2
3
4
5
6
<form> <input type="text" autocomplete="username" name="id" /> <input type="password" autocomplete="current-password" name="pw" /> <input type="file" name="file" /> <button type="submit">Submit</button> </form>
JS
1
2
3
4
5
6
7
8
9
10
11
12
document.querySelector('form') .addEventListener('submit', async event => { event.preventDefault() // Prevent the default behavior so the page does not reload! const formData = new FormData(event.target) // Pass a reference to the `<form>` element! const res = await axios({ url: 'URL', method: 'METHOD', data: formData }) return res.data })

You can check the data in formData like this.

JS
1
2
3
4
5
6
7
8
formData.get('name') // Get the single value that matches 'name'! formData.getAll('name') // Get all values that match 'name' as an array! // ... // Check all data! for (const [key, value] of formData) { console.log(key, value) }

# JSON strings

With Base64, you can also send a file as a string.

Base64 is a way to encode binary data as text.
It is useful when information can be sent only as text, such as in email or URLs, but the downside is that the converted data is larger than the original.

Building on the FormData example above, write an extra async function (toBase64) that converts a file to a Base64 string.

JS
1
2
3
4
5
6
7
8
function toBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader() reader.readAsDataURL(file) reader.onload = () => resolve(reader.result) reader.onerror = error => reject(error) }) }

Then, in the sendFile function, take the file, convert it to Base64, and send it.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
async function sendFile(file) { const base64 = await toBase64(file) const res = await fetch('https://api.heropy.dev/v0/users/ywTTX', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ photo: { name: file.name, data: base64 } }) }) return await res.json() }
fetch
JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function sendFile(file) { const base64 = await toBase64(file) const res = await axios({ url: 'https://api.heropy.dev/v0/users/ywTTX', method: 'PUT', headers: { 'Content-Type': 'application/json' }, data: { photo: { name: file.name, data: base64 } } }) return res.data }
axios

# Error handling

The fetch function does not reject the promise based on the response status.
It rejects only when the network fails or the request cannot be made at all.

For example, if you send a request to OMDbAPI without an API key like this, you get a 401 Unauthorized error.
But the fetch function does not reject the promise for the status code (401), so the catch block does not run.

JS
1
2
3
4
5
6
7
8
9
;(async () => { try { const res = await fetch('https://omdbapi.com/?apikey=&s=avengers') // Error: 401 Unauthorized! const data = await res.json() console.log(data) // { Response: 'False', Error: 'No API key provided.' } } catch (err) { console.error('Error:', err) } })()

So you need to check the properties of the response object according to the API specification and handle the error manually, like this.

JS
1
2
3
4
5
6
7
8
9
10
11
12
;(async () => { try { const res = await fetch('https://omdbapi.com/?apikey=&s=avengers') // Error: 401 Unauthorized! const data = await res.json() // { Response: 'False', Error: 'No API key provided.' } if (data.Response === 'False') { return console.error(data.Error) // 'No API key provided.' } console.log(data) } catch (err) { console.error('Error:', err) } })()

axios is convenient because it automatically rejects the promise for response status codes that are not successful (2xx).

JS
1
2
3
4
5
6
7
8
;(async () => { try { const res = await axios('https://omdbapi.com/?apikey=&s=avengers') // Error: 401 Unauthorized! console.log(res.data) } catch (err) { console.error('Error:', err) // { name: 'AxiosError', message: 'Request failed with status code 401', ... } } })()

# Timeouts

If a network request does not get a response within a certain time, you can stop the request.
This prevents endless waiting and reduces unnecessary network traffic.

By default, web browsers do not allow timeouts longer than a certain time (1-5 minutes).

The fetch function has no timeout setting by default, so you need extra work.
With the Promise.race static method, you can write and use a function that returns whichever promise settles first, like this.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function fetchWithTimeout(url, options, timeout = 1000) { return Promise.race([ fetch(url, options), new Promise((_, reject) => setTimeout(() => reject(new Error('Request timed out!')), timeout) ) ]) } ;(async () => { try { await fetchWithTimeout('https://api.heropy.dev/v0/delay?t=3000', { method: 'GET' }, 2000) } catch (err) { console.log('Error:', err) } })()
fetch

Or you can set a timeout more concisely with the AbortSignal.timeout static method.
AbortSignal.timeout automatically aborts the request after the specified time.

JS
1
2
3
4
5
6
7
8
9
;(async () => { try { await fetch('https://api.heropy.dev/v0/delay?t=3000', { signal: AbortSignal.timeout(2000) // 2 seconds }) } catch (err) { console.log('Error:', err) // TimeoutError: The operation was aborted due to timeout } })()
fetch

With axios, you can easily add a timeout with the timeout option.

JS
1
2
3
4
5
6
7
8
9
10
11
;(async () => { try { await axios({ url: 'https://api.heropy.dev/v0/delay?t=3000', method: 'GET', timeout: 2000 // 2 seconds }) } catch (err) { console.log('Error:', err) } })()
axios

# Canceling requests

When needed, you can cancel a network request at any time.
This reduces unnecessary network traffic, and you do not have to handle unnecessary responses.

To cancel a request with the fetch function, you need to use AbortController.
On the instance (controller) returned by calling the new AbortController() constructor, use the signal property and pass it as an option to the fetch function.
When needed, you can cancel the request by calling the controller.abort() method.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const controller = new AbortController() ;(async () => { try { const res = await fetch('https://api.heropy.dev/v0/delay?t=3000', { method: 'GET', signal: controller.signal }) const data = await res.json() console.log(data) } catch (err) { if (err.name === 'AbortError') { console.log('The request was canceled!') } console.log(err) } })() document.querySelector('h1') .addEventListener('click', () => { controller.abort() // Cancel the request! })

axios works the same way.

axios used to have a built-in approach with axios.CancelToken, but it has been deprecated since version 0.22.0.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const controller = new AbortController() ;(async () => { try { const res = await axios({ url: 'https://api.heropy.dev/v0/delay?t=3000', method: 'GET', signal: controller.signal }) console.log(res.data) } catch (err) { if (err.name === 'AbortError') { console.log('The request was canceled!') } } })() document.querySelector('button.cancel') .addEventListener('click', () => { controller.abort() })

# Credentials

When you send a request to a server, the user's authentication information included with it is called 'credentials'.
Depending on the API specification, cookies, the basic authorization header, and so on can also be included and sent automatically.

When you include credentials, you must use HTTPS so that they are not exposed to man-in-the-middle attacks.

With the fetch function, you can set whether to send credentials automatically with the credentials option.
The default value is omit, which does not send credentials, and if you set it to include, credentials are included in the request.
(To send them only for the same origin, use same-origin.)

JS
1
2
3
4
5
6
7
8
9
10
;(async () => { const res = await fetch('https://api.heropy.dev/v0/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'HEROPY', age: 85 }), credentials: 'include' }) const data = await res.json() console.log(data) })()
fetch

With axios, you set whether to send credentials automatically with the withCredentials option.
The default value is false.

JS
1
2
3
4
5
6
7
8
9
10
;(async () => { const res = await axios({ url: 'https://api.heropy.dev/v0/users', method: 'POST', headers: { 'Content-Type': 'application/json' }, data: { name: 'HEROPY', age: 85 }, withCredentials: true }) console.log(res.data) })()
axios

If you want to include them manually, you can add the header information yourself, like this.
How you use it can differ depending on the API specification.

Authorization: 'Bearer ...' is widely used in modern authentication mechanisms such as JWT (JSON Web Tokens).
Bearer (the holder) means that the user who has the token can access the resource.

JS
1
2
3
4
5
6
7
8
9
10
11
12
;(async () => { const res = await axios({ url: 'https://api.heropy.dev/v0/users', method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...', }, data: { name: 'HEROPY', age: 85 } }) console.log(res.data) })()

# Custom configuration

With a custom configuration, you can make your request code concise and easy to manage.

With the fetch function, you can create a configuration the way you want and reuse it, like this.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function api(method, endpoint, data) { const url = 'https://api.heropy.dev/v0' + endpoint const timeout = 2000 const options = { method, headers: { Apikey: 'KDnREmPe9B1', Username: 'ParkYoungWoong' }, body: data ? JSON.stringify(data) : null } return Promise.race([ fetch(url, options), new Promise((_, reject) => setTimeout(() => reject(new Error('Request timed out!')), timeout) ) ]) } api('GET', '/users') api('POST', '/users', { name: 'HEROPY', age: 85 }) api('PUT', `/users/${userId}`, { age: 127 }) api('DELETE', `/users/${userId}`)
fetch

With axios, you can create a custom configuration more easily with the axios.create function.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const api = axios.create({ baseURL: 'https://api.heropy.dev/v0', timeout: 2000, headers: { Apikey: 'KDnREmPe9B1', Username: 'ParkYoungWoong' } }) api.get('/users') api.post('/users', { name: 'HEROPY', age: 85 }) api.put(`/users/${userId}`, { age: 127 }) api.delete(`/users/${userId}`)
axios

# Interceptors

An interceptor is a feature that intercepts a request before it reaches the server, or a response before it is handled, to run shared logic.
It is very useful because you can manage repetitive tasks in one place, such as attaching an authentication token automatically, handling error responses in a common way, and managing loading state.

The fetch function does not provide an interceptor feature, so you need to write a wrapper function yourself, like this.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async function interceptedFetch(url, options = {}) { // Before the request (acts as an interceptor) options.headers = { ...options.headers, Authorization: `Bearer ${localStorage.getItem('token')}` } const res = await fetch(url, options) // After the response (acts as an interceptor) if (res.status === 401) { console.log('Authentication has expired.') } return res } interceptedFetch('https://api.heropy.dev/v0/users')
fetch

axios provides request and response interceptors through the interceptors property.
You can register a request interceptor with axios.interceptors.request.use and a response interceptor with axios.interceptors.response.use.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Request interceptor axios.interceptors.request.use(config => { config.headers.Authorization = `Bearer ${localStorage.getItem('token')}` return config }) // Response interceptor axios.interceptors.response.use( res => res, err => { if (err.response?.status === 401) { // When authentication expires, handle it, for example by moving to the login page console.log('Authentication has expired.') } return Promise.reject(err) } ) axios.get('https://api.heropy.dev/v0/users')
axios

You can also register interceptors on an instance created with axios.create, so they work even better together with the custom configuration we looked at earlier.

JS
1
2
3
4
5
6
7
8
9
10
11
12
const api = axios.create({ baseURL: 'https://api.heropy.dev/v0', timeout: 2000 }) // Register an interceptor on the instance api.interceptors.request.use(config => { config.headers.Authorization = `Bearer ${localStorage.getItem('token')}` return config }) api.get('/users')
axios

# Concurrent requests

When you need to call several APIs at the same time, you can handle multiple requests in parallel with the Promise.all static method.
This can reduce the total response time compared to handling each request one after another.

With Promise.all, if even one of the passed promises is rejected, the whole thing is rejected.
If you want to use the remaining results even when some requests fail, you can use Promise.allSettled.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
;(async () => { try { const [usersRes, postsRes] = await Promise.all([ fetch('https://api.heropy.dev/v0/users'), fetch('https://api.heropy.dev/v0/posts') ]) const users = await usersRes.json() const posts = await postsRes.json() console.log(users, posts) } catch (err) { console.error('Error:', err) } })()
fetch
JS
1
2
3
4
5
6
7
8
9
10
11
;(async () => { try { const [usersRes, postsRes] = await Promise.all([ axios.get('https://api.heropy.dev/v0/users'), axios.get('https://api.heropy.dev/v0/posts') ]) console.log(usersRes.data, postsRes.data) } catch (err) { console.error('Error:', err) } })()
axios

axios used to have the axios.all and axios.spread methods for concurrent requests, but they have been deprecated since version 0.20.0.
axios.all is a simple wrapper around Promise.all, and axios.spread is replaced by destructuring assignment syntax, so you do not need them.

# Streaming responses

Sometimes a server does not send data all at once, but splits it into chunks and sends them one by one.
Streaming responses are typically used in AI chatbots, large file downloads, and so on.

The fetch function provides the response body as a ReadableStream, so you can process the data in order as it arrives.

JS
1
2
3
4
5
6
7
8
9
10
11
;(async () => { const res = await fetch('https://api.heropy.dev/v0/stream') const reader = res.body.getReader() const decoder = new TextDecoder() while (true) { const { done, value } = await reader.read() if (done) break console.log(decoder.decode(value)) // Print each data chunk in order as it arrives } })()
fetch

By default, axios uses XMLHttpRequest in the browser, so it could not handle streaming responses directly. Since version 1.7.0, however, the adapter: 'fetch' option has been added, so you can make it use the Fetch API internally.
When you use it together with responseType: 'stream', the response data is returned as a ReadableStream, so you can handle streaming the same way as with fetch.

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
;(async () => { const { data } = await axios.get('https://api.heropy.dev/v0/stream', { responseType: 'stream', adapter: 'fetch' }) const reader = data.getReader() const decoder = new TextDecoder() while (true) { const { done, value } = await reader.read() if (done) break console.log(decoder.decode(value)) // Print each data chunk in order as it arrives } })()
axios

# Node.js support

You can also use fetch and axios as data communication tools in a Node.js environment.

Since Node.js 18, the fetch function is built in, so you can use it right away without installing anything.

JS
1
2
3
fetch('https://api.heropy.dev/v0/users') .then(res => res.json()) .then(data => console.log(data)) // User[]
fetch

In environments older than Node.js 18, you can install and use the node-fetch package.

BASH
1
npm i node-fetch
JS
1
2
3
4
5
import fetch from 'node-fetch' fetch('https://api.heropy.dev/v0/users') .then(res => res.json()) .then(data => console.log(data)) // User[]
fetch

axios also supports the Node.js environment, so you can install and use it the same way.

BASH
1
npm i axios
JS
1
2
3
4
import axios from 'axios' axios.get('https://api.heropy.dev/v0/users') .then(res => console.log(res.data)) // User[]
axios