Written by Muhammed Ali✏️
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. To follow along with this article, you’ll need:
Table of contents
Initial project setup
First, we’ll handle all the installation and initial setup to run our application. We’ll use Express to develop the API and Nodemon 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:
$ 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:
...
"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:
npm install express nodemon
Build an 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:
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:
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:
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:
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
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:
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:
...
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:
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. Before testing, open up your terminal and run the following command to start the server:
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:
curl -d "user:User1" -X POST http://127.0.0.1:4000/api/register -w "\n"
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:
curl http://127.0.0.1:4000/api/country -H "x-api-key: 2agp59nwu8nrszm4p6kfriekoeo0s1" -w "\n"
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.
LogRocket 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.
Top comments (3)
I think this is using basic auth. I wrote a small tutorial on detailing different options: Securing a Node.js API: A Simple Guide to Authentication. Probably the most secure way is to use jwt tokens(or bearer which are simiar, but broader in scope).
Hello thank you for posting
I add same logic in my API that you describe
github.com/amitSharma7741/Hindi_jo...
.
but i got one error my database not update count value
i took many request but count value still = 1;
i again and again check my mongoDB Atlas but count value is not changing
Please help me....
Cool post when you don’t need all the bells and whistles of the Auth* libs.
Tip: check out nanoid for random key generator. npmjs.com/package/nanoid