Fetching from an API is one of the most repeated tasks for anyone writing JavaScript backends with node.js. There are dozens of libraries for doing this including node-fetch
, isomorphic-fetch
, axios
, etc etc, however, there's actually a couple of native ways that are fairly simple to use and require no external dependencies at all.
Let's talk about a few of them!
The 'https' way
In a world prior to node 18, many people may or may not realize that it's actually not that hard to simply use the built in node.js https
module to do a simple API request with ZERO external dependencies.
const https = require("node:https");
const request = (options) => {
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = "";
res.on("data", (d) => {
data += d;
});
res.on("end", () => {
data = JSON.parse(data.toString().trim());
if (res.statusCode === 200) {
resolve(data);
} else {
reject(new Error(`Error code: ${res.statusCode}.`));
}
});
res.on("error", (error) => {
reject(new Error(error));
});
});
req.end();
});
};
const auth = `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`;
request({
hostname: "foobar.com",
port: 443,
path: "/api/users/1",
method: "GET",
headers: {
Accept: "application/json",
Authorization: auth,
},
})
.then((data) => {
console.log(data);
})
.catch((error) => {
// Do something with the error
});
Here you can see we used the native via require("node:http")
.
You can optionally start by creating your own function that returns a promise, here above there's a request
function of our own.
The https.request
function returns an instance of the ClientRequest
class. And takes options, and a callback function. The callback function contains a parameter for an instance the IncomingMessage
class, or basically, the response.
You can use the res.on("data", callback)
like you would when you're reading a stream. There's a few event handers you can listen to, build the full response data by just building up a big response string.
Then in res.on("end", callback)
you can check the status code, and send back data as JSON or however you want to send back data to the calling function by calling resolve
and resolving the promise. If anything goes wrong such as the status not being 200, you can reject
with an error, or a string, just something to let the caller know what's going on.
There's also an res.on("error", callback)
handler in case something else goes wrong in the response.
Finally, you just call req.end()
.
Now you have your own custom request
function to make API requests with!
The last piece is just building up the correct Authorization
headers to send along with the headers
option. That's often just a base64 encoded string in the format of username:password
along with Basic ${auth}
.
As seen above...
const auth = Basic ${Buffer.from(${user}:${password}).toString("base64")};
The 'fetch' way
For a while now, the fetch
API has been available in most browsers. However, it wasn't until recently that you have access to the fetch
API in node. As of 18.0.0, you are now able to simply use fetch
in basically the same way you would in the browser. In 17.5.0 you could use the --experimental-fetch
flag, but that will no longer be needed in 18+.
fetch("https://foobar.com/api/users/1", {
headers: new Headers({
'Authorization': `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`,
'Content-Type': 'application/json'
}),
})
.then((resp) => {
if (response.status >= 200 && response.status <= 299) {
return response.json();
} else {
throw Error(response.statusText);
}
})
.catch(() => {
return resp.text();
})
Well, that was a lot simpler. That's thanks to the work of the folks who put together the new undici
library that powers fetch in node!
Conclusion
tldr; if you're in node 18, use fetch, otherwise, use https.
The key takeaway here is that you probably don't need to add some big fetching library just to make a simple HTTP request in node. It's simple enough to make your own thing.
Top comments (0)