In this day and age, APIs are the new "gold". They can be used to automate, retrieve or change many things. Many services offer APIs here. Some hide their APIs behind verifications (Meta I look at you π₯΄) or are directly available for free. If you want to create your own API for your services, you quickly face the question of how to do it. In this article, I would like to build a small and simple API with you.
If you don't yet know what an API is, you should definitely take a look at my article and APIs as a whole π
What is NodeJS?
In simple terms, NodeJS is JavaScript that runs as an application and is not just limited to websites. This makes it possible to build applications or entire server systems based on JavaScript. This is of course a blessing for experienced web developers and makes them feel much more comfortable in server environments than if they had to write C# or PHP. NodeJS is also platform-independent.
NodeJS is also suitable for small scripts that were "previously" written in Powershell, Bash/Batch or Python.
For my part, I love NodeJS and am very happy that JavaScript has now managed to establish itself as an application and server language. If you want to learn more about NodeJS, I recommend the documentation for NodeJS.
Fundamental considerations π€
First of all, you need to think about what exactly you want to do. In this guide, we will only cover a small use case. In this case, Express provides our web server.
First, let's assume we just want to return a random name from a fixed list. To achieve this, we first set up a new NodeJS project.
Our NodeJS project
In order to create NodeJS projects, you will of course need NodeJS. This should be installed in advance (I recommend the latest version, alternatively the latest LTS [long term service]).
Once everything is installed, we can create a new folder somewhere and execute the following command:
npm init
NPM
NPM in this case is nothing other than the Node Package Manager, with which you can create and provide your own packages, as well as install and use other libraries. In this case, we create a new package. The command then guides you through the setup:
If you look at the project now, you will see that only one package.json
file was created. It is the linchpin for your NodeJS project:
Install and use Express π
Since Express is a third-party web server library, we now need to install it. NPM also offers a command for this:
npm install express
{
"name": "nodejs-express-simple-api",
"version": "0.0.1",
"description": "Simple NodeJS and Express REST-API",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Marco Franke",
"license": "MIT",
"dependencies": {
"express": "^4.18.2"
}
}
These are your project dependencies. If you also want to publish your project on NPM, these will also be installed when the user installs your project (with npm install
). There are also devDependencies
. These are packages that are only needed at development time, e.g. Linter or other tools. You can also see that there is now a folder node_modules
. This is where all your installed NPM packages are stored. This folder can tend to be large and is not shared with all other projects!
What we are still missing is a file that is to be executed. In the setup of npm init
, a file was also suggested that should serve as the entry point. In the standard case, this is index.js
. We now create this file and open the project with Visual Studio Code (not necessary, but I love VS Code β€οΈ).
We write your first web server
When the project is now open, you grab the index.js
and first import the express
package:
const express = require('express');
This allows us to access and use everything from Express. Now we instantiate a new instance of Express and assign a port to the web server (3000 is the standard NodeJS port, by convention, but can be changed as required):
const app = express();
const port = 3000;
All we need to do now is start the server and specify a route on which the server "listens" and provides content. We can do this quite easily with:
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
Now we have a web server that listens to port 3000
and returns the content Hello World!
on the root route /
.
You can then simply start the web server with the command:
node index.js
π‘
In Visual Studio Code, you can simply press F5 and select NodeJS as the project. The project will then start automatically and you can even debug it with breakpoints.
If you now surf to your page with http://localhost:3000/
, you should see "Hello World".
With this you have now written your first web server π
The path to our own API π£οΈ
But now we want to provide a REST API that randomly returns us any names.
app.get('/generate/names', (req, res) => {
res.send('Hello Names!');
});
We want to use JSON as the return and not provide any data directly to the browser (as a website). To do this, we now have to tell Express that it should please give us the return as application/json
. Express also offers a function for this purpose res.json()
. Res
is the response that is sent to the browser. We can simply pass the object to it and it is sent to the browser as JSON.
app.get('/generate/names', (req, res) => {
// list of random names
names = ["John", "Paul", "George", "Ringo"];
// get random index
let randomIndex = Math.floor(Math.random() * names.length);
// return random name
res.json({ randomName: names[randomIndex]});
});
If we now open http://localhost:3000/generate/names
, we get our random name as JSON from our API:
Passing parameters to the API
Currently, we open the API via browser, i.e. the HTTP method GET
.
I have already mentioned these HTTP methods in my article about REST APIs. If you haven't read the article yet, take a look π
If we now want to give the API parameters, we only have two options:
- Route parameters
- Query string parameters
Both types are specified via the address, but are anchored differently.
Route parameters
Route parameters
are also passed via the address line, but are not added to the end of the address, but are an integral part of the address itself:
GET http://test.xyz/[parameter]/
If we now change our code so that we want to differentiate between male and female names, we could do this via this. Express also offers a solution for this, as we can read the request
to the API. Route parameters are specified in app.get()
in the first parameter with
:[parameter name]
:
app.get('/generate/names/:gender', (req, res) => {
const gender = req.params.gender;
// list of random names with gender
const names = [
{ name: 'John', gender: 'male' },
{ name: 'Emma', gender: 'female' },
{ name: 'Michael', gender: 'male' },
{ name: 'Sophia', gender: 'female' },
// Add more names with their genders here
];
// get all names for a specific gender
const genderNames = names.filter(name => name.gender == gender)
// get random index of named for specific gender
let randomIndex = Math.floor(Math.random() * genderNames.length);
// return random name object
res.json({ randomName: genderNames[randomIndex]});
});
We can access the parameters with req.params
and then extract the named parameter. Consequently, we would still have to check whether the transfer is male
or female
and, if not, exit with an error:
if(!gender.includes('male', 'female')){
res.status(500).send("Gender must be `male` or `female`")
}
So if we now surf to http://localhost:3000/generate/names/male
, we get this return:
Query string parameters
Query string parameters
are added to the address at the end. You have probably seen an address like this before:
GET http://google.com/?q=searchTerm
Everything after the ?
signals to the browser/server that parameters are now coming from here. These parameters consist of keys/values that are specified with =
. Several parameters themselves are separated with &
.
If we now want to include a search that only returns male or female names that contain a certain character, we can do this using these query string parameters. The parameter name q
has become the standard for searches. We change the code as follows:
app.get('/generate/names/:gender', (req, res) => {
// get the gender param from the url
const gender = req.params.gender;
// get the search term from the query string
const searchTerm = req.query.q;
// check if gender is male or female, if not exit with 500 error
if(!gender.includes('male', 'female')){
res.status(500).send("Gender must be `male` or `female`")
}
// list of random names with gender
const names = [
{ name: 'John', gender: 'male' },
{ name: 'Emma', gender: 'female' },
{ name: 'Michael', gender: 'male' },
{ name: 'Sophia', gender: 'female' },
// Add more names with their genders here
];
const genderNames = names.filter(name => name.gender == gender);
const searchTermNames = genderNames.filter(name => name.name.includes(searchTerm));
// get random index
let randomIndex = Math.floor(Math.random() * searchTermNames.length);
// return random name
res.json({ randomName: searchTermNames[randomIndex]});
});
As you can see, we can retrieve the query string parameters with req.params.q
. Let's try it out by calling the following URL: http://localhost:3000/generate/names/male?q=Mi
And indeed, we now get all male names that contain "Mi":
Passing complete data to the API πΎ
Up to now, we have only passed data to the API via GET
. However, if we now want to generate data (e.g. a blog article in a system), we have to pass entire data that would be too large for the address line. To do this, we use POST
to transfer data to the API as JSON
. This is no longer possible with the browser, so we have to use cURL, Postman or other API tools.
Prepare endpoint
In order to receive data in our API, we have to prepare it accordingly. For example, let's assume we want to pass the list of names and gender to the API and it should simply return a random name for a gender.
To do this, we need to change our route from app.get()
to app.post()
(this is exactly how it would behave with PUT
, PATCH
, DELETE
etc.):
app.post('/generate/names/:gender', (req, res) => {
// get the gender param from the url
const gender = req.params.gender;
// get the search term from the query string
const searchTerm = req.query.q;
// check if gender is male or female, if not exit with 500 error
if(!gender.includes('male', 'female')){
res.status(500).send("Gender must be `male` or `female`")
}
// list of random names with gender
const names = [
{ name: 'John', gender: 'male' },
{ name: 'Emma', gender: 'female' },
{ name: 'Michael', gender: 'male' },
{ name: 'Sophia', gender: 'female' },
// Add more names with their genders here
];
const genderNames = names.filter(name => name.gender == gender);
const searchTermNames = genderNames.filter(name => name.name.includes(searchTerm));
// get random index
let randomIndex = Math.floor(Math.random() * searchTermNames.length);
// return random name
res.json({ randomName: searchTermNames[randomIndex]});
});
And import, use and configure the bodyParser
from Express:
// put this underneath "const express = require('express');"
const bodyParser = require('body-parser')
// this should be right under declaring express and the port
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
We can then query the body
of the request with req.body
. The modified code would look like this:
app.post('/generate/names/:gender', (req, res) => {
// get the gender param from the url
const gender = req.params.gender;
// get the search term from the query string
const searchTerm = req.query.q;
// check if gender is male or female, if not exit with 500 error
if(!gender.includes('male', 'female')){
res.status(500).send("Gender must be `male` or `female`")
}
// get the body of the request with all names and genders
const names = req.body;
const genderNames = names.filter(name => name.gender == gender);
const searchTermNames = genderNames.filter(name => name.name.includes(searchTerm));
// get random index
let randomIndex = Math.floor(Math.random() * searchTermNames.length);
// return random name
res.json({ randomName: searchTermNames[randomIndex]});
});
With const names = req.body;
we now assign the body of our request to our variable name
. However, this also means that we have to pass this and, above all, check that the body corresponds to our structure, otherwise the code would pop with error messages. But I'll leave that part to you π
If we now call our API like this and send it the expected body, we should also get our result, namely a name for a defined gender with "Mi" in the name:
And lo and behold, it works π
With the basics in place, you can now write your own APIs and make services available to the world. Welcome to the world of APIs π¨βπ
If you like my posts, it would be nice if you follow my Blog for more tech stuff.