Build A Simple REST API With Node.js And Express

January 17, 2021 by Andreas Wik

Let’s look at how we can build a simple REST API with Node.js and Express. I previously wrote about how to set up a simple web server with Node.js and Express to serve HTML files, and if you’re completely new to Express you may wanna check that out.

As always when setting up a new project, run npm init -y in the terminal while you’re in your project folder.

Next, install Express with the following command: npm install express.

Create app.js in your project folder. Now, let’s start coding.

 

First, load Express in and create an Express app:

const express = require('express');

const app = express();

 

We will also have our Express app use a couple of middlewares.

express.json() helps us handle the incoming JSON data in the body of some of the requests.

// express.json() helps us handle the incoming JSON data in the request body
app.use(express.json());

 

Let’s also enable CORS requests, so that if you want to play around with this in your browser you don’t run into any nasty CORS errors. Install cors  with npm install cors.

Up in the top, among the other require statements, make sure you get cors using const cors = require(‘cors’);

const express = require('express');
const cors = require('cors');

 

Then, right before or after app.use(express.json()); add the following line: app.use(cors()); Worth noting is that this enables all CORS requests. However, you can easily change this. Go here to read about how to configure the behaviour.

// express.json() helps us handle the incoming JSON data in the request body
app.use(express.json());

// Enable CORS
app.use(cors());

 

Start the server:

app.listen(3000, () => {
    console.log('Server is up and running on PORT 3000.')
});

 

Now, in the terminal, run node app.js. This will start the server which will run on port 3000 and you can now access it via localhost:3000.

 

Setting up routes

Here comes the interesting part: handling different requests. This will be added before app.listen(), see the comment below:

const express = require('express');
const cors = require('cors');

const app = express();

// express.json() helps us handle the incoming JSON data in the request body
app.use(express.json());

// Enable CORS
app.use(cors());

// Create routes here...

// Start server
app.listen(3000, () => {
    console.log('Server is up');
});

Let’s say we’re building an API for a simple news platform. The editor should, at a bare minimum, be able to do the following:

  • Get (GET) all the articles
  • Publish (POST) a new article
  • Edit (PATCH) an existing article
  • Delete (DELETE) an existing article

 

Get all articles

So the first request we will handle will be a GET request to /articles. This will send back all articles in JSON format. app.get() is the function we will use to handle the GET request (we will use app.post() and app.patch() etc later on). We pass app.get() two parameters – the route and the callback function.

I define an array of article objects just for example purposes. In the real world it would be fetched from a database or other source. res.send(articles) sends back the response to the user along with our articles. res.send() sends back a status 200 per default if nothing else is set. You can specify a specific status using res.status(STATUS_CODE).send() instead. We will use this in the other routes.

// Get all articles
app.get('/articles', (req, res) => {

    // Send back list of articles... Status 200
    const articles = [
        {
            id: 293,
            title: "Woman left hallucinating for months after eating five-day-old service station sushi",
            text: "Medics treating the woman, a 34-year-old only identified as ‘JC’, were left bamboozled as to what was causing her illness - until they eventually something lurking in her gut"
        }, {
            id: 284,
            title: "Man shares cleaning hack to rescue grubby baking trays and leave them spotless",
            text: "A cleaning hack on TikTok showing how to transform old baking trays using baking soda and white vinegar has racked up more than 200,000 views - but not everyone is convinced"
        }, {
            id: 237,
            title: "Mum discovers photo of her family has been stolen and edited to remove husband",
            text: "Sarah Knuth told of her shock after discovering that a photo of her with her husband and two children had gone viral, but her husband's face had been photoshopped out"
        }
    ];

    res.send(articles);
});

 

Make sure you restart the server after saving any changes.

Now, if you fire up Postman and send a GET request to http://localhost:3000/articles you should get back a list of your articles.

 

Create article

Let’s move on to creating an article. It will be a POST request with the body containing the article data, title and text. For example:

{
	"title": "Dog saves human", 
	"text": "A dog in Westershire pulled an amazing trick which proved life saving."
}

 

So, this time we’ll use app.post(). We will access the body of the request via req which we get access to in the callback function. req.body to be exact. In case req.body is missing either title or text, we will reply back with a status 400 and an error message. However, if it all looks good, we continue on and send back a status 201.

// Create new article
app.post('/articles', (req, res) => {
    console.log(req.body);

    // Error: title or text missing from request body... Status 400
    if(!req.body.title || !req.body.text) {
        res.status(400).send({ error: 'Invalid format - body must contain title and text.' });
    }

    // Article successfully created... Status 201
    res.status(201).send();
});

 

Update article

To update an article, the user will send a PATCH request, and the body will contain the same pieces of information as when creating a new one. The route is slightly different though, as we are expecting the id of the article they want to update. It will be a call to /articles/:id. The value of id can be accessed via req.params.id.

So first we confirm that the id parameter is a number, otherwise send back a 400 status code. Next we check the request body in req.body and send back a 400 if that doesn’t pass our test. In case an article with the provided id can’t be found, we send back a 404 status. Finally… If all goes well when updating the article, we send back a happy status 200 response.

// Update existing article
app.patch('/articles/:id', (req, res) => {
    console.log(`Updating article with ID ${req.params.id}`);

    // Error: Invalid id parameter, not a number... Status 400
    if(isNaN(req.params.id)) {
        res.status(400).send({ error: 'Invalid id parameter.' });
    }

    // Error: title or text missing from body... Status 400
    if(!req.body.title || !req.body.text) {
        res.status(400).send({ error: 'Invalid format - body must contain title and text.' });
    }   

    // Error: Article with ID req.params.id could not be found... Status 404
    // For example purposes, let's just check if it's between 1 and  999
    if(req.params.id < 1 || req.params.id > 999) {
        res.status(404).send();
    }

    // Article successfully updated... Status 200
    res.send();
});

 

 

Delete article

To delete an article, the user will send a DELETE request to /articles/:id. Just as with our update handler above, we check if the id parameter is valid. We then also check if an article with the provided id exists, if not we send a 404 status code back. If all is fine and dandy and the article is successfully deleted we send back a 200 status code.

// Delete existing article
app.delete('/articles/:id', (req, res) => {
    console.log(`Deleting article with ID ${req.params.id}`);

    // Error: Invalid id parameter, not a number... Status 400
    if(isNaN(req.params.id)) {
        res.status(400).send({ error: 'Invalid id parameter.' });
    }

    // Error: Article with ID req.params.id could not be found... Status 404
    // For example purposes, let it fail if outside of the range 1-999
    if(req.params.id < 1 || req.params.id > 999) {
        res.status(404).send();
    }

    // Article successfully deleted... Status 200
    res.send();
});

 

Complete app.js code:

const express = require('express');
const cors = require('cors');

const app = express();

// express.json() helps us handle the incoming JSON data in the request body
app.use(express.json());

// Enable CORS
app.use(cors());

// Get all articles
app.get('/articles', (req, res) => {

    // Send back list of articles... Status 200
    const articles = [
        {
            id: 293,
            title: "Woman left hallucinating for months after eating five-day-old service station sushi",
            text: "Medics treating the woman, a 34-year-old only identified as ‘JC’, were left bamboozled as to what was causing her illness - until they eventually something lurking in her gut"
        }, {
            id: 284,
            title: "Man shares cleaning hack to rescue grubby baking trays and leave them spotless",
            text: "A cleaning hack on TikTok showing how to transform old baking trays using baking soda and white vinegar has racked up more than 200,000 views - but not everyone is convinced"
        }, {
            id: 237,
            title: "Mum discovers photo of her family has been stolen and edited to remove husband",
            text: "Sarah Knuth told of her shock after discovering that a photo of her with her husband and two children had gone viral, but her husband's face had been photoshopped out"
        }
    ];

    res.send(articles);
});

// Create new article
app.post('/articles', (req, res) => {
    console.log(req.body);

    // Error: title or text missing from body... Status 400
    if(!req.body.title || !req.body.text) {
        res.status(400).send({ error: 'Invalid format - body must contain title and text.' });
    }

    // Save article
    // Article successfully created... Status 201
    res.status(201).send();
});

// Update existing article
app.patch('/articles/:id', (req, res) => {
    console.log(`Updating article with ID ${req.params.id}`);

    // Error: Invalid id parameter, not a number... Status 400
    if(isNaN(req.params.id)) {
        res.status(400).send({ error: 'Invalid id parameter.' });
    }

    // Error: title or text missing from body... Status 400
    if(!req.body.title || !req.body.text) {
        res.status(400).send({ error: 'Invalid format - body must contain title and text.' });
    }   

    // Error: Article with ID req.params.id could not be found... Status 404
    // For example purposes, let's just check if it's between 1 and  999
    if(req.params.id < 1 || req.params.id > 999) {
        res.status(404).send();
    }

    // Article successfully updated... Status 200
    res.send();
});

// Delete existing article
app.delete('/articles/:id', (req, res) => {
    console.log(`Deleting article with ID ${req.params.id}`);

    // Error: Invalid id parameter, not a number... Status 400
    if(isNaN(req.params.id)) {
        res.status(400).send({ error: 'Invalid id parameter.' });
    }

    // Error: Article with ID req.params.id could not be found... Status 404
    // For example purposes, let it fail if outside of the range 1-999
    if(req.params.id < 1 || req.params.id > 999) {
        res.status(404).send();
    }

    // Article successfully deleted... Status 200
    res.send();
});

// Start server
app.listen(3000, () => {
    console.log('Server is up and running on PORT 3000');
});

 

I think that’s about it – a little boilerplate for an Express REST API.

 

Recommended reading

Share this article