Calling an API with authorization using native nodejs with zero external dependencies
Tags: node, fetch, api, native
Published: 2023-07-17 21:30:13

---
title: Calling an API with authorization using native nodejs with zero external dependencies
published: true
date: 2022-08-19 19:17:48 UTC
tags: nodejs,fetch,api,native
canonical_url: https://www.jonathancreamer.com/calling-an-api-with-authorization-using-native-nodejs-with-zero-external-dependencies/
---

 ![Calling an API with authorization using native nodejs with zero external dependencies](https://www.jonathancreamer.com/content/images/2022/08/IMG_4649.jpg)

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](https://caniuse.com/fetch). However, it wasn't until recently that you have access to the `fetch` API in node. As of [18.0.0](https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V18.md#fetch-experimental), 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](https://undici.nodejs.org/#/) 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.