How to use the Fetch API in Node.js, Deno, and Bun — SitePoint

By admin
In this article, we’ll look at how to use the Fetch API with

Node.js
ORG

,

Deno
ORG

, and Bun.


Fetch API
PERSON

vs XMLHttpRequest

Fetching data via an HTTP request is fundamental web application activity. You may have made such calls in the browser, but

the Fetch API
PRODUCT

is natively supported in Node.js,

Deno
ORG

, and Bun.

In a browser, you might request information from a server so you can display it without a full screen refresh. This is typically known as an

Ajax
ORG

request or a single page application (

SPA
ORG

).

Between 1999 and 2015
DATE

,

XMLHttpRequest
ORG

was the only option — and remains so if you want to show file upload progress. XMLHttpRequest is a fairly clunky callback-based

API
ORG

, but it permits fine-grained control and, despite the name, it’ll handle responses in formats other than

XML
ORG

— such as text, binary, JSON, and HTML.

Browsers have implemented

the Fetch API
PRODUCT

from

2015
DATE

. It’s a simpler, easier, more consistent, promise-based alternative to XMLHttpRequest.

Your server-side code may also want to make HTTP requests — typically to call APIs on other servers. From their

first
ORDINAL

release, both the

Deno
ORG

and Bun runtimes usefully replicated the browser’s

Fetch API
PERSON

so that similar code could run on both the client and server. Node.js required a

third
ORDINAL

-party module such as node-fetch or axios until

February 2022
DATE

, when version

18
CARDINAL

added the standard

Fetch API
PRODUCT

. It’s still considered experimental, but you can now use fetch() everywhere with identical code in most cases.

A Basic Fetch Example

This simple example fetches response data from a URI:

const response = await fetch ( ‘

https://example.com/data.json
PERSON

‘ ) ;

The fetch() call returns a promise which resolves with a Response object providing information about the result. You can parse the HTTP response body into a JavaScript object using the promise-based .json() method:

const data = await response . json ( ) ;

Client-side vs Server-side Fetch

The API may be identical across platforms, but browsers enforce restrictions when making client-side fetch() requests:

Cross-origin resource sharing (CORS) Client-side

JavaScript
PRODUCT

can only communicate with API endpoints within its own domain. A script loaded from https://domainA.com/js/main.js can call any service at https://domainA.com/ , such as

https://domainA.com/api/
ORG

or https://domainA.com/data/ . It’s impossible to call a service on https://domainB.com/ — unless that server permits access by setting an HTTP Access-Control-Allow-Origin header.

Content Security Policy (

CSP
ORG

) Your web sites/apps can set a

Content-Security-Policy
ORG

HTTP header or meta tag to control permitted assets in a page. It can prevent accidental or malicious injection of scripts, iframes, fonts, images, videos, and so on. For example, setting default-src ‘self’ stops fetch() requesting data outside its own domain (XMLHttpRequest,

WebSocket
ORG

, server-sent events, and beacons are also restricted).

Server-side

Fetch API
PERSON

calls in

Node.js
ORG

,

Deno
ORG

, and Bun have fewer restrictions, and you can request data from any server. That said,

third
ORDINAL

-party APIs may:

require some sort of authentication or authorization using keys or OAuth

have maximum request thresholds, such as no more than one call per minute, or

make a commercial charge for access

You can use server-side fetch() calls to proxy client-side requests so you can avoid CORS and

CSP
ORG

issues. That said, remember to be a conscientious web citizen and don’t bombard services with

thousands
CARDINAL

of requests that could take them down!

Custom Fetch Requests

The example above requests data from the URI https://example.com/data.json . Below the surface,

JavaScript
PRODUCT

creates a Request object, which represents the full details of that request such as the method, headers, body, and more.

fetch() accepts

two
CARDINAL

arguments:

the resource – a string or URL object, and

– a string or URL object, and an optional options parameter with further request settings

For example:

const response = await fetch ( ‘

https://example.com/data.json
PERSON

‘ , { method : ‘GET’ , credentials : ‘omit’ , redirect : ‘error’ , priority : ‘high’ } ) ;

The options object can set following properties in Node.js or client-side code:

property values method GET (the default),

POST
ORG

,

PUT
ORG

,

PATCH
ORG

,

DELETE
ORG

, or HEAD headers a string or Headers object body can be a string, JSON, blob, etc. mode same-origin , no-cors , or cors credentials omit , same-origin , or include cookies and HTTP authentication headers redirect follow , error , or manual handling of redirects referrer the referring URL integrity subresource integrity hash signal an

AbortSignal
ORG

object to cancel the request

Optionally, you can create a Request object and pass it to fetch() . This may be practical if you can define API endpoints in advance or want to send a series similar requests:

const request = new Request ( ‘https://example.com/api/’ , { method : ‘POST’ , body : ‘{"a":

1
CARDINAL

, "b":

2
CARDINAL

, "c":

3
CARDINAL

}’ , credentials : ‘omit’ } ) ; console . log ( ` fetching ${ request . url } ` ) ; const response = await fetch ( request ) ;


Handling HTTP Headers

You
WORK_OF_ART

can manipulate and examine HTTP headers in the request and response using a Headers object. The

API
ORG

will be familiar if you’ve used JavaScript Maps:

const headers = new Headers ( { ‘Content-Type’ : ‘text/plain’ , } ) ; headers .

append
PERSON

( ‘Authorization’ , ‘Basic abc123’ ) ; headers . set ( ‘

Content-Type’
WORK_OF_ART

, ‘application/json’ ) ; const type = headers . get ( ‘

Content-Type’
WORK_OF_ART

) ; if ( headers . has ( ‘Authorization’ ) ) { headers . delete ( ‘Authorization’ ) ; } headers . forEach ( ( value , name ) => { console . log ( ` ${ name } : ${ value } ` ) ; } ) ; const response = await fetch ( ‘

https://example.com/data.json
PERSON

‘ , { method : ‘GET’ , headers } ) ; response . headers . forEach ( ( value , name ) => { console . log ( ` ${ name } : ${ value } ` ) ; } ) ;


Fetch Promise Resolve
WORK_OF_ART

and Reject

You might presume a fetch() promise will reject when an endpoint returns a

404
CARDINAL

Not Found or similar server error. It doesn’t! The promise will resolve, because that call was successful — even if the result wasn’t what you expected.

A fetch() promise only rejects when:

you make an invalid request — such as fetch(‘httttps://!invalid\URL/’);

you abort the fetch() request, or

request, or there’s a network error, such as a connection failure


Analyzing Fetch Responses

Successful fetch
WORK_OF_ART

() calls return a Response object containing information about the state and returned data. The properties are:

property description ok true if the response was successful status the HTTP status code, such as

200
CARDINAL

for success statusText the HTTP status text, such as OK for a

200
CARDINAL

code url the URL redirected true if the request was redirected type the response type: basic , cors , error , opaque , or opaqueredirect headers the response Headers object body a ReadableStream of body content (or null) bodyUsed true if the body has been read

The following Response object methods all return a promise, so you should use await or .then blocks:

method description text() returns the body as a string json() parses the body to a

JavaScript
PRODUCT

object arrayBuffer() returns the body as an

ArrayBuffer
PRODUCT

blob() returns the body as a Blob formData() returns the body as a

FormData
ORG

object of key/value pairs clone() clones the response, typically so you can parse the body in different ways

const response = await fetch ( ‘

https://example.com/data.json
PERSON

‘ ) ; if ( response .

ok &&
ORG

response . headers . get ( ‘

Content-Type’
WORK_OF_ART

) === ‘application/json’ ) { const obj = await response . json ( ) ; }

Aborting Fetch Requests

Node.js won’t time out a fetch() request; it could run forever!

Browsers
PERSON

can also wait

between one
CARDINAL

and

five minutes
TIME

. You should abort fetch() under normal circumstances where you’re expecting a reasonably quick response.

The following example uses an

AbortController
ORG

object, which passes a signal property to the

second
ORDINAL

fetch() parameter. A timeout runs the .abort() method if fetch doesn’t complete within

five seconds
TIME

:

const controller = new

AbortController
ORG

( ) , signal = controller . signal , timeout = setTimeout ( ( ) => controller . abort ( ) ,

5000
CARDINAL

) ; try { const response = await fetch ( ‘https://example.com/slowrequest/’ , { signal } ) ;

clearTimeout
PERSON

( timeout ) ; console . log ( response . ok ) ; } catch ( err ) { console . log ( err ) ; }

Node.js,

Deno
ORG

, Bun, and most browsers released since

mid-2022
DATE

also support

AbortSignal
ORG

. This offers a simpler timeout() method so you don’t have to manage your own timers:

try { const response = await fetch ( ‘https://example.com/slowrequest/’ , { signal :

AbortSignal
PERSON

. timeout (

5000
CARDINAL

) , } ) ; console . log ( response . ok ) ; } catch ( err ) { console . log ( err ) ; }

Effective Fetches

Like any asynchronous, promise-based operation, you should only make fetch() calls in series when the input of a call depends on the output of a previous one. The following code doesn’t perform as well as it could because each API call must wait for the previous one to resolve or reject. If each response takes

one second
TIME

, it’ll take a total of

three seconds
TIME

to complete:

const response1 = await fetch ( ‘

https://example1.com/api/
ORG

‘ ) ; const

response2
PERSON

= await fetch ( ‘https://example2.com/api/’ ) ; const response3 = await fetch ( ‘https://example3.com/api/’ ) ;

The Promise.allSettled() method runs promises concurrently and fulfills when all have resolved or rejected. This code completes at the speed of the slowest response. It will be

three
CARDINAL

times faster:

const data = await Promise . allSettled ( [ ‘

https://example1.com/api/
ORG

‘ , ‘https://example2.com/api/’ , ‘https://example3.com/api/’ ] . map ( url => fetch ( url ) ) ) ;

data returns an array of objects where:

each has a status property string of "fullfilled" or "rejected"

property string of or if resolved, a value property returns the fetch() response

property returns the response if rejected, a reason property returns the error

Summary

Unless you’re using a legacy version of Node.js (

17
CARDINAL

or below),

the Fetch API
PRODUCT

is available in

JavaScript
ORG

on both the server and client. It’s flexible, easy to use, and consistent across all runtimes. A

third
ORDINAL

-party module should only be necessary if you require more advanced functionality such as caching, retries, or file handling.