Understanding API key authentication in Node.js
Tags: node, webdev
Published: 2022-11-23 15:54:51

**Written by [Muhammed Ali](https://blog.logrocket.com/author/muhammedali/)✏️**

When building an API with Node.js, there are different options available regarding authentication. Some of these include the JSON Web Token, OAuth, the API key, and more. In this article, we’ll learn how to authenticate a Node.js API using API Keys. 

Using API keys has an advantage if you want to set a limit or track how often a particular user is using an API. By using API keys, the user doesn’t need to worry about multi-factor authentication with their username and password. Your API’s user will be able to automate data fetching on the application. 

In this tutorial, we’ll create an API with Node.js. Then, we’ll create an authentication system that creates an API key whenever a user registers on the application. With the newly created API key, the user will be able to access all of the routes on the API. 

You can find the complete code on [GitHub](https://github.com/khabdrick/api-key-nodejs). To follow along with this article, you’ll need:

*   Basic knowledge of Node.js
*   [Node](https://nodejs.org/en/download/)[.js](https://nodejs.org/en/download/) installed on your machine

## Table of contents

*   [Initial project setup](#initial-project-setup)
*   [Build an authentication system](#build-authentication-system)
*   [Develop routes for the server](#develop-routes-for-server)

## Initial project setup

First, we’ll handle all the installation and initial setup to run our application. We’ll use [Express](https://expressjs.com/) to develop the API and [Nodemon](https://nodemon.io/) to run the API server and listen for changes in the code in real-time. 

We’ll have to install them, but first, create a folder for your project and run the following command to create a `package.json` file for your project:

```bash
$ npm init -y
```

Now, update the `"scripts"` module in your `package.json` file with the code below so that we'll be able to run the server with Nodemon:

```javascript
...
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "nodemon Server.js"
  }
...
```

Later in the article, we’ll create the `Server.js` file where we’ll run the server from. Now, install Nodemon and Express by running the following command:

```bash
npm install express nodemon
```

## Build an authentication system <a name="build-authentication-system">

The authentication system takes in a given username and creates user data, containing the username, API key, and a count of usage on a particular day. We’ll need the count so that we can set a limit on how many times a user can use the API on a particular day. 

We’ll start by creating a function called `genAPIKey()` that generates the API when a new user is created. The function will generate a `base-36` string that contains 30 characters within `A-Z` and `0-9`, which will represent the API key. You can start by creating a new JavaScript file called `apiAuth.js` and pasting the following code:

```javascript
const genAPIKey = () => {
  //create a base-36 string that contains 30 chars in a-z,0-9
  return [...Array(30)]
    .map((e) => ((Math.random() * 36) | 0).toString(36))
    .join('');
};
```

Next, we’ll develop a function that creates the user data when a username is entered. We‘ll store this new user in an array, so we need some initial data to get started. Create a new file called `initialData.js` and paste the following code:

```javascript
const users = [
  {
    _id: 1587912,
    api_key: "rwuy6434tgdgjhtiojiosi838tjue3",
    username: "username",
    usage: [{ date: "2022-10-10", count: 17 }],
  },
];
const Countries = [
  { _id: 1, name: "Nigeria" },
  { _id: 2, name: "China" },
];
module.exports = { users, Countries };
```

Now, we’ll develop the function that creates a user by pasting the following code in the `apiAuth.js` file:

```javascript
const users = require('./initialData').users; // import initial data
...

const createUser = (_username, req) => {
  let today = new Date().toISOString().split('T')[0];
  let user = {
    _id: Date.now(),
    api_key: genAPIKey(),
    username: _username,
    usage: [{ date: today, count: 0 }],
  };

  console.log('add user');
  users.push(user);
  return user;
};
```

Next, we’ll develop a function that will authenticate the API key so that you can access specified parts of the API. This function will compare the registered API key with the `x-api-key`, which will come with the request. 

If the request goes through, the count per day increases, and if you’ve reached your max request per day, you’ll receive an error. Paste the following code into your `apiAuth.js` file:

```javascript
const authenticateKey = (req, res, next) => {
  let api_key = req.header("x-api-key"); //Add API key to headers
  let account = users.find((user) => user.api_key == api_key);
  // find() returns an object or undefined
  if (account) {
    //If API key matches
    //check the number of times the API has been used in a particular day
    let today = new Date().toISOString().split("T")[0];
    let usageCount = account.usage.findIndex((day) => day.date == today);
    if (usageCount >= 0) {
      //If API is already used today
      if (account.usage[usageCount].count >= MAX) {
        //stop if the usage exceeds max API calls
        res.status(429).send({
          error: {
            code: 429,
            message: "Max API calls exceeded.",
          },
        });
      } else {
        //have not hit todays max usage
        account.usage[usageCount].count++;
        console.log("Good API call", account.usage[usageCount]);
        next();
      }
    } else {
      //Push todays's date and count: 1 if there is a past date
      account.usage.push({ date: today, count: 1 });
      //ok to use again
      next();
    }
  } else {
    //Reject request if API key doesn't match
    res.status(403).send({ error: { code: 403, message: "You not allowed." } });
  }
};
module.exports = { createUser, authenticateKey };
```

We’re exporting so that the `Server.js` can use these functions.

## Develop routes for the server <a name="develop-routes-for-server">

In this section, we’ll create the routes that we’ll use to access the data in the API while applying the API key checks. We’ll create endpoints to register a user, add countries to the country list, and also get the list of countries. Requests to add or get countries will require API key authentication. 

To get started, create a new file called `Server.js` and paste the code below. The code contains imports that we’ll use later on, as well as what will be outputted on the homepage:

```javascript
const express = require('express');
const app = express();
const port = 4000;
const API = require('./apiAuth');

// Get initial data for users and countries
const { users, Countries } = require('./initialData');
//handle json body request
app.use(express.json());

app.get('/', (req, res) => {
  //home page
  res.status(200).send({ data: { message: 'You can get list of countires at /api/country.' } });
});
```

Using the `createUser()` function we developed earlier, we’ll create a route that will add new users to the user list and generate the user data. Paste the following code in your `Server.js` file:

```javascript
...
app.post('/api/register', (req, res) => {
  //create a new with "user:Username"
  let username = req.body.username;
  let user = API.createUser(username, req);
  res.status(201).send({ data: user });
});
```

Now, we’ll develop the routes to create and get countries. In this route, we’ll include the `authenticateKey()` function we developed earlier so that the API key sent in the header of the request can be authenticated and the request can be counted. Then, we’ll finally set the port that the server should listen on. Paste the following code in your `Server.js` file:

```javascript
app.get('/api/country', API.authenticateKey, (req, res) => {
  //get list of all Countries   
  let today = new Date().toISOString().split('T')[0];
  console.log(today);
  res.status(200).send({
    data: Countries,
  });
});
app.post('/api/country', API.authenticateKey, (req, res) => {
  //add a new country
  let country = {
    _id: Date.now(),
    name: req.body.country,
  };
  Countries.push(country);
  res.status(201).send({
    data: country,
  });
});

app.listen(port, function (err) {
  if (err) {
    console.error('Failure to launch server');
    return;
  }
  console.log(`Listening on port ${port}`);
});
```

Now, we’re done with the coding part of this tutorial, and we can move into testing. We’ll test the API endpoints with [cURL](https://curl.se/). Before testing, open up your terminal and run the following command to start the server:

```bash
npm run dev
```

Open another terminal window and run the following command to create a new user. Once the command is run, you’ll be provided with some user data, one of which includes the API key:

```javascript
curl -d "user:User1" -X POST http://127.0.0.1:4000/api/register -w "\n"
```

![Create New User API Key](https://blog.logrocket.com/wp-content/uploads/2022/11/create-new-user-api-key.png) Now that you have created a user, you can use the country endpoints. Let’s try to retrieve the countries that we have on the list using the API key that we were provided with. You can do so by running the command below. Make sure you replace `2agp59nwu8nrszm4p6kfriekoeo0s1` in the command below with your own API key:

```javascript
curl http://127.0.0.1:4000/api/country -H "x-api-key: 2agp59nwu8nrszm4p6kfriekoeo0s1" -w  "\n"
```

![Retrieve Country Endpoints](https://blog.logrocket.com/wp-content/uploads/2022/11/retrieve-country-endpoints.png)

## Conclusion

In this tutorial, we created an API with Node.js and developed an authentication system that generates an API key whenever a user is registered. With the newly created API key, the user is able to access all the routes on the API and API usage can be tracked. 

You can build upon the knowledge you have obtained in this article by adding API key authentication to your Node.js API that uses a database to store data. In addition to user authentication, you can use the API key for the advantages that were mentioned in the introductory section of this article.

---

## 200’s only ✔️ Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, [try LogRocket] (https://logrocket.com/signup/). 

[![LogRocket Network Request Monitoring](https://blog.logrocket.com/wp-content/uploads/2019/12/network-request-filter-2-1.png)](https://lp.logrocket.com/blg/signup) 

[LogRocket] (https://lp.logrocket.com/blg/signup) is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. [Start monitoring for free] (https://lp.logrocket.com/blg/signup).