Calling an API with authorization using native nodejs with zero external dependencies

nodejs Aug 19, 2022

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.

Tags