Node.js Express Rest Api Tutorial
Click to share! ⬇️
Let’s see how to build a simple REST api using Express to allow for all of the CRUD operations. REST is referring to Representational State Transfer. It is a convention for building HTTP services via a client and server architecture. In REST, the HTTP protocol is used to facilitate Create, Read, Update, and Delete of resources on the server. These operations can be referred to collectively as CRUD operations. Each operation uses a different HTTP verb. Create uses a POST request. Read uses a GET request. Update uses a PUT request. Finally, Delete uses a DELETE request. In this tutorial, we will create all of these services using Node.js and Express.
Nội Dung Chính
Express Web Server
We’ve learned how to scaffold out an Express project already, so let’s go ahead and create a new one so we can build out our new REST api. Enter the following commands to get started.
- node $
mkdir express-rest
- node $
cd express-rest
- express-rest $
npm init --yes
We also need an index.js file in the root of the project, so we can create that too. Once that is complete, add the following code. We are requiring the express module, and then calling the express() function and assigning the result to the app
constant. The result is an Object, and by convention, it is typically named app. Visual Studio Code informs us of the following about express(). “Creates an Express application. The express() function is a top-level function exported by the express module.”
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Oh Hi There!');
});
app.listen(3000, () => console.log('Listening on port 3000'));
We can launch the web server by typing node index.js
at the command prompt.
Now that the server is running, we can load up http://localhost:3000 in the browser to see that all is good.
Now we will add a new route that will simulate an API. We want to be able to visit the /api/games endpoint and see all the games in the system. At this point, we have no database so we’ll just populate a simple array as an example.
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Oh Hi There!');
});
app.get('/api/games', (req, res) => {
res.send(['Mario', 'Zelda', 'Donkey Kong']);
});
app.listen(3000, () => console.log('Listening on port 3000'));
In order to test this, you’ll need to restart the webserver at the command line. First, type CTRL-C to halt the server, then re-launch the server again with node index.js
. Now, go ahead and visit http://localhost:3000/api/games in the browser and have a look!
Nodemon Express
As we move along, it is going to be tiresome to manually restart the server on every update we make. We can easily deal with this problem by making use of nodemon when launching the server. Nodemon is likely installed if you checked out our rendering HTML with node tutorial. If it’s not installed, just go ahead and type npm i -g nodemon
at the command line. Once ready, halt the server and relaunch it using nodemon index.js
.
Now when you make changes to your files like in line 5 here,
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Ima change this up!');
});
app.get('/api/games', (req, res) => {
res.send(['Mario', 'Zelda', 'Donkey Kong']);
});
app.listen(3000, () => console.log('Listening on port 3000'));
You will no longer need to stop and then restart the server. Go ahead and make your updates, and you’ll see the command line reflect this automatically.
Of course the result in the browser is updated as well.
Setting Port with an Environment Variable
So far we have been hard coding the values for different application variables such as the listening port. You’re not going to want to do that once your building anything bigger than a tutorial app. The more appropriate way to handle this would be to check for an environment variable with the name of PORT, and if it is set, use that value. If it is not set, you can fall back to a sensible default. Note the new code here.
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Ima change this up!');
});
app.get('/api/games', (req, res) => {
res.send(['Mario', 'Zelda', 'Donkey Kong']);
});
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}...`));
Now, the port can be set and read from the environment variable. Here, we set the port to 4000 instead of the 3000 we were using. Note that when we relaunch nodemon, the server is now listening on port 4000.
Express Route Parameters
Single Params
Route parameters are of supreme importance. It is by passing route parameters that a user can specify to the server what resource it wants to fetch. Consider the following code.
app.get('/api/games/:id', (req, res) => {
res.send(req.params.id);
});
What this says is that anytime a get request is made to /api/games
, anything that comes after that is a dynamic route parameter. So for example if we visit http://localhost:4000/api/games/25, then 25 is the route parameter. To fetch this in the code, you can use req.params.id. Here we visit http://localhost:4000/api/games/donkeykong and note we simply display the value of the route parameter in the browser.
Multiple Params
You can have more than one route parameter in the url. Consider this code.
app.get('/api/games/:title/:publisher', (req, res) => {
res.send(req.params);
});
Now if we visit http://localhost:4000/api/games/mario/nintendo here is the result we get.
Query Parameters
We can also access query parameters in the url. Here we update the code to allow for both route parameters and query parameters.
app.get('/api/games/:title/:publisher', (req, res) => {
res.send([req.params, req.query]);
});
Route parameters are used for primary and needed values for working with a resource. Query parameters on the other hand can be thought of as more optional in nature.
HTTP GET Requests
Now we can set up some get requests for fetching games from the server. This corresponds to the Read of crud in a rest api. We are simply using an array of games, as again there is no database just yet. So let’s set up an array of games like we see here.
const games = [{
id: 1,
title: 'Mario'
},
{
id: 2,
title: 'Zelda'
},
{
id: 3,
title: 'Donkey Kong'
}
];
Typically in the RESTful convention, if you make a get request to the api with no route parameters specified, then you should get back all resources. So if we visit /api/games, then we should see all games. This code here should do the trick.
// get all games
app.get('/api/games', (req, res) => {
res.send(games);
});
Looks good!
Now we want to be able to find a specific game only using a route parameter. For this, we can use the find function.
// get game by id
app.get('/api/games/:id', (req, res) => {
const game = games.find(g => g.id === parseInt(req.params.id));
if (!game) return res.status(404).send('The game with the given ID was not found.');
res.send(game);
});
This is also working nicely! When we provide the route parameter of 3
, we get the Donkey Kong game back.
HTTP POST Requests
Now we need to set up the code that will allow our web server to respond to http post requests. This corresponds to the Create of crud in a rest api. We can use a post request to add a new game to the system. Note the additional code here.
const express = require('express');
const app = express();
app.use(express.json());
const games = [{
id: 1,
title: 'Mario'
},
{
id: 2,
title: 'Zelda'
},
{
id: 3,
title: 'Donkey Kong'
}
];
// get all games
app.get('/api/games', (req, res) => {
res.send(games);
});
// get game by id
app.get('/api/games/:id', (req, res) => {
const game = games.find(g => g.id === parseInt(req.params.id));
if (!game) return res.status(404).send('The game with the given ID was not found.');
res.send(game);
});
// add a game
app.post('/api/games', (req, res) => {
const game = {
id: games.length + 1,
title: req.body.title
}
games.push(game);
res.send(game);
});
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}...`));
The first thing we notice in the snippet above is the app.use(express.json()); line. This specifies that we are adding in a piece of middleware to our application. The reason we need this is because we need to parse the title property from the body of the post request, req.body.title. Express needs to be able to parse json objects in the body of the request so this middleware turns that on.
Next, we see that we are posting to the collection, or in other words to /api/games. This example is a bit of a hack since we are not actually working with a database, but it gets the idea across. That is why we are getting the id by using games.length + 1. The title will be parsed from the json object in the post request via req.body.title. Finally, we simply push the new game onto our games array, and then send back the game as a response by convention.
Testing Endpoints With Postman
The way we can test out sending a post request with a json object in the body of the request is by using postman. We specify the request type as POST, provide the route for the games collection at http://localhost:4000/api/games, and set the json object to include a game with a title of Splatoon.
Once we click Send, we should get a response back like we see here with a new id of 4 and the title of the game Splatoon.
So it looks like everything worked perfectly! This means that if we now make simple a GET request to that same collection api of api/games, then we should now see 4 games instead of the 3 we originally had and that is exactly what we get.
Joi Input Validation
When setting up an api, it is important to make sure to validate any data that gets sent to the api. You can not trust any data that a user or another application might be sending to your api. A nice way to set up input validation when using express is with the popular Joi package. Let’s install it with npm!
Once Joi is installed, require it in to the file like so.
const Joi = require('joi');
Now we can update the app.post() call like this. First, we define the schema or validation rules we need. Here we simply say the title must be at least 2 characters. Then, we make that call to the validate() method passing in the request body and the schema. From there, we just check for errors and if there are any, send them right back as a response.
// add a game
app.post('/api/games', (req, res) => {
const schema = {
title: Joi.string().min(2).required()
};
const result = Joi.validate(req.body, schema);
if (result.error) {
res.status(400).send(result.error)
}
const game = {
id: games.length + 1,
title: req.body.title
}
games.push(game);
res.send(game);
});
Great! Now, we can send a bad request using postman once again and see the result.
The response we get back is an error, just like we expected!
{
"isJoi": true,
"name": "ValidationError",
"details": [
{
"message": "\"title\" length must be at least 2 characters long",
"path": [
"title"
],
"type": "string.min",
"context": {
"limit": 2,
"value": "x",
"key": "title",
"label": "title"
}
}
],
"_object": {
"title": "x"
}
}
HTTP PUT Requests
To update an existing resource on the server you can use a PUT request. This corresponds to the Update of crud in a rest api. Let’s see how to set up the code to handle a PUT request so we can update a game in the application. This one is just slightly more complicated. Let’s review the steps we need to complete first.
- Look up the game in the application
- If it is not found, return a 404 error
- Validate the data being sent to the server
- If that data is invalid, send an error 400 bad request
- If all checks out, update the game
- Send the updated game back as a response
This would translate into something like this in our code.
// update a game
app.put('/api/games/:id', (req, res) => {
const game = games.find(g => g.id === parseInt(req.params.id));
if (!game) return res.status(404).send('The game with the given ID was not found.');
const schema = {
title: Joi.string().min(2).required()
};
const result = Joi.validate(req.body, schema);
if (result.error) {
res.status(400).send(result.error)
}
game.title = req.body.title;
res.send(game);
});
Let’s test it out! We will send a PUT request to the server specifying the id of 3, and passing a json object in the body of the request with a new title for this game. Right now, the game with the id of 3 is ‘Donkey Kong’. We will try to change it to ‘Cave Story’.
We get a response back like we expect.
Finally, we just want to again make a GET request to the collection in our browser and game 3 should now be ‘Cave Story’.
HTTP Delete Requests
Finally, we will learn how to implement the Delete of crud operations in our rest api. We can follow a similar logic as updating a course. Here is the code we can add.
// delete a game
app.delete('/api/games/:id', (req, res) => {
const game = games.find(g => g.id === parseInt(req.params.id));
if (!game) return res.status(404).send('The game with the given ID was not found.');
const index = games.indexOf(game);
games.splice(index, 1);
res.send(game);
});
Now, let’s send a DELETE request in postman to delete the game with an id of 2.
We get back the game which was deleted as a response, which is what we expect.
Finally, we once again make a GET request to the api to list all of our games. We can see that game 2 is now missing. Oh no! Zelda don’t go!
Node.js Express Rest Api Tutorial Summary
In this tutorial we learned all about setting up a simple REST api using Node.js and Express together. We covered the main verbs to use such as GET, POST, PUT, and DELETE, as well as all of the CRUD operations. Express makes it pretty easy to set these up with app.get(), app.post(), app.put(), and app.delete(). In addition, we can use Mongoose for CRUD.
Some Key Points To Remember
- REST defines a set of conventions for creating HTTP services:
- POST: to create a resource
- GET: to read a resource
- PUT: to update a resource
- DELETE: to delete a resource
- You can use Express for building web servers with Node.js.
- Nodemon is a great way to watch for changes in files and automatically restart the node process.
- Environment variables can store various settings for an application. To read an environment variable, use process.env.
- Never trust data sent by the client. Perform input validation using Joi instead.
Useful Snippets For Express and REST
// Creating a web server
const express = require('express');
const app = express();
// Creating a resource
app.post('/api/resources', (req, res) => {
// Create the resource and return the resource object
resn.send(resource);
});
// Getting all the resources
app.get('/api/resources', (req, res) => {
// To read query string parameters (?sortBy=title)
const sortBy = req.query.sortBy;
// Return the resources
res.send(resources);
});
// Getting a single resource
app.get('/api/resources/:id', (req, res) => {
const resourceId = req.params.id;
// Lookup the resource and if not found, return 404
res.status(404).send('Resource not found.');
// Else, return the resource object
res.send(resource);
});
// Updating a resource
app.put('/api/resources/:id', (req, res) => {
// If resource not found, return 404, otherwise update it
// and return the updated object.
});
// Deleting a resource
app.delete('/api/resources/:id', (req, res) => {
// If resource not found, return 404, otherwise delete it
// and return the deleted object.
});
// Listen on port 4000
app.listen(4000, () => console.log('Listening…'));
// Reading the port from an environment variable
const port = process.env.PORT || 5000;
app.listen(port);
Click to share! ⬇️