How to Build Rest API with Node js and Express js

javascript-logo

Getting Started

In this example. we are gonna build restful API with node.js and express.js web framework for CRUD (create, read, update, delete) operations. Before we start we need to have node.js installed on the working machine, you can find info on the official documentation.

To get started first we need to create a new folder, we will call it node-rest-api, that will contain our node.js application, after we need to navigate to inside the folder (because I am using Windows I will use GitBash, if you are using Mac or Linux, you can use terminal with relevant commands, but I will use the word "terminal" so don't get confused).

Firstly inside the terminal, we need to run the npm init command, after a very short time we will be asked to insert a couple of parameters, and we will leave everything default. Now we can open the root folder node-rest-api with text editor by choice. Inside the root folder, we can find the package.json file, that was created automatically with the npm init command. Inside the package.json file we can find the parameters that we confirmed in the last step.

package.json:

{
  "name": "rest-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

In the next step, we will open the terminal again and install the Express.js. web framework with the command npm i express, and what exactly is express?

Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. With the package installed we will add new file in the root folder with a name server.js. Here we will set up all the code to start our node.js server, like in the next code example.

server.js:

const http = require('http');
const app = require('./app.js');
const port = process.env.PORT || 3000;
const server = http.createServer(app);
server.listen(port, () => {
    console.log('Server listening on port ' + port);
});

Firstly we are importing the 'http' package, this package provides us with functionality to start up the server, after we are importing the app module which we will create it in the moment, next we are setting the port to the .env file (still doesn't exist we will create it later), or in case is not here we are using port 3000, after we are creating a server constant and with the http package we are calling createServer() and we will pass the app constant which we imported moments ago. And lastly, we are calling the server to listen on the port.

app.js:

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

app.use((req, res, next) => {
    res.status(200).json({
        message: 'It is on!'
    });
});

module.exports = app;

In the app.js file we are importing 'express' package, in the next line, we are creating a new constant app, and executing express on it. Lastly, we are calling a method use() on the app constant, and we need to pass to it an arrow function where we have request, response and next (req and res with short names). Inside we are sending a response with res and setting the status code to 200 for successful and then the JSON method, to sent back a JSON response with the message 'It is on!'. On the last line, we are exporting the app module.

We can now run node server.js in the terminal, and we will keep the process running. To see the results we will open our favorite browser and we will go to localhost:3000 and we get the message: 'It is on!'. There is a useful tool for sending requests to the server is called Postman and I highly recommend it to you to use it.

Adding Routes

Now that we have very basic restful API working, we need to add more endpoints to the API. First, we are gonna make a new folder and name it routes, inside the routes folder we will make a new file called posts.js, and we will add CRUD operations (create, read, update and delete). posts.js

const express = require('express');
const router = express.Router();

router.get('/', (req, res, next) => {
    res.status(200).json({
        message: 'Returning all posts'
    });
});

router.get('/:postId', (req, res, next)=>{
    res.status(200).json({
        message: 'Return post with ID: ' + req.params.postId
    });
});

router.post('/', (req, res, next) => {
    res.status(200).json({
        message: 'Added post'
    });
});

router.put('/:postId', (req, res, next)=>{
    res.status(200).json({
        message: 'Updated post with ID: ' + req.params.postId
    });
});

router.delete('/:postId', (req, res, next)=>{
    res.status(200).json({
        message: 'Deleted post with ID: ' + req.params.postId
    });
});

module.exports = router;

The first endpoint is for GET requests which returns all the posts, second is also GET request but returns a single post by ID, the ID is sent through params. Third is a POST request, which is adding a new post, next is the PUT request, for updating posts, and the last one is DELETE request. After we have added our first router module we need to update the app.js to use the new router module, like in the next example:

Updated app.js

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

const postRoutes = require('./routes/posts');

app.use('/posts', postRoutes);

module.exports = app;

Now we have a simple CRUD restful API built with node.js and express.js. To make use of it, it is needed to be connected to the database.

Logining and Error Handling

Logging is important so we can see our incoming requests to the rest API. We will use the Morgan NPM package for logging. Before we can use it we need to run npm i morgan in the terminal, you can find more info about Morgan here. To add Morgan in our project we need to add the following code in our app.js:

const morgan = require('morgan'); //Importing the package
app.use(morgan('dev')); //Using the package, dev is the format.

We are now also not handling errors. If we send some invalid request we are getting a default page, but we want to send JSON response instead. To catch all error request we need to do the error handling after we are using the requests in app.js

app.use('/posts', postRoutes); //Here we are already using the router.
//We need to do the error handling below the routes.
//Here we handle the errors for the requests.
app.use((req, res, next) => {
    const error = new Error('Not Found');   //We are creating new error object, with not found.
    error.status = 404; //Here we are setting the error status code to 404.
    next(error);    //Here we are executing the next callback, to forward the error request, and passing the error.
});

//Here we will handle all kinds of errors.
app.use((error, req, res, next) => {
    res.status(error.status || 500);    //Here we are setting the status code from the error or 500.
    res.json({
        error: {
            message: error.message  //Here we are returning JSON data with a message.
        }
    })
});

And here is the final look of app.js file:

const express = require('express');
const morgan = require('morgan');
const app = express();
const postRoutes = require('./routes/posts');

app.use(morgan('dev'));
app.use('/posts', postRoutes);

app.use((req, res, nexr) => {
    const error = new Error('Not Found');
    error.status = 404;
    next(error);
});

app.use((error, req, res, next) => {
    res.status(error.status || 500);
    res.json({
        error: {
            message: error.message
        }
    })
});

module.exports = app;

And lastly, we will look at CORS errors, firstly what is CORS? CORS stands for Cross-Origin Resource Sharing and it is a security concept. We have a client and the server and if both coming from the same server (like localhost), then this will be successful. But if the client and the server are both on different servers (like rest API's in production) then it will be not successful. But for rest API's we need to allow the access because the concept behind rest API's is to be consumed by other clients on other servers. We can send some headers to disable the protection from the server to the client and allow access. How do these headers look, let's see in the next example:

app.use((req, res, next) => {
    res.header('Access-Contol-Allow-Origin', '*');  //here we are allowing the access to any origin with the * symbol.
    res.header('Access-Contol-Allow-Headers',
     'Origin, X-Requested-With, Content-Type, Accept, Authorization');  //here we are defining which kind of headers we want to accept.
     if(req.method === 'OPTIONS'){
         res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE'); //we are telling the client what methods can be used.
         return res.status(200).json({});
     }
     next();
});
//ABOVE ROUTES

Parsing the Body

For parsing the body of the requests we will need to install a new NPM package: body-parser. To install the package we simply need to use npm i body-parser in terminal, and to use it we need to import the package at the top of our app.js file. And we will use it just below the last middleware we added morgan:

const bodyParser = require('body-parser');

app.use(morgan('dev'));
app.use(bodyParser.urlencoded({extended: false})); // parse application/x-www-form-urlencoded
app.use(bodyParser.json()); // parse application/json

Now with body-parser when we create a new post object we can get the data from the incoming request, let's update the post request in the next example.

Post endpoint from posts.js

router.post('/', (req, res, next) => {
    const post = {
        title: req.body.title,  //here we are setting the title from the request body.
        body: req.body.body //here we are setting the text body from the request body.
    }
    res.status(200).json({
        message: 'New post created',
        createdPost: post   //here we will pass the created post to the response.
    });
});

Adding MongoDB with Mongoose

Finnaly we can add a database to the rest API, we are gonna use one of the most popular NoSql databases Mongo DB. Mongo DB uses JSON like documents with schema. To start using MongoDB we will need to add one more npm package called mongoose. Mongoose is object modeling tool designed for MongoDB, about mongoose.js you can find a lot more in the documentation. To start using mongoose firstly we need to install in with npm i mongoose in the terminal and import the package in top of the app.js file.

const mongoose = require('mongoose');
//to use mongoose you will need to config. your local mongo db, 
//I have done mine with robo3t you can find it https://robomongo.org/
mongoose.connect('mongodb://localhost:27017/your_local_db_name', {useNewUrlParser: true});

To use the mongoose first we will need to configure a model (or schema). In the root folder, we will now add a model folder, and here we will define all of our models. Let's define the post model, to do that we need to create post.js inside the model's folder.

post.js

const mongoose = require('mongoose');

//Here we are defining our post model
const postSchema = mongoose.Schema({
    _id: mongoose.Schema.Types.ObjectId,
    title: String,
    body: String
});

module.exports = mongoose.model('Post', postSchema);

To use our new post model we need to import it to the posts.js file in the routes folder. After that we will update all our routes, like in the following example:

posts.js

const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const Post = require('../models/post'); //importing the model

router.get('/', (req, res, next) => {
    //returning all the posts
    Post.find((error, posts) => {
        if(error) return res.status(500).json({error: error});
        return res.status(200).json({
            posts: posts
        });
    });
});

router.get('/:postId', (req, res, next)=> {
    //finding the post by id and returning with the response
    Post.findById(req.params.postId, (error, post) => {
        if(error) return res.status(500).json({error: error});
        return res.status(200).json({
            post: post
        });
    });
});


router.post('/', (req, res, next) => {
    //creating new post object
    const post = new Post({
        _id: new mongoose.Types.ObjectId(),
        title: req.body.title,
        body: req.body.body
    });
    //method provides by the mongoose for saving the object
    post.save((error, result) => {
        if(error) return res.status(500).json({error: error});
        return res.status(200).json({
            message: 'New post created',
            createdPost: post
        })
    });
});

router.put('/:postId', (req, res, next) => {
    //updating a post by id
    Post.updateOne({_id: req.params.postId}, {$set: {title: req.body.title, body: req.body.body}}, (error, post) => {
        if(error) return res.status(500).json({error: error});
        return res.status(200).json({
            message: 'Post updated',
            updatedPost: post
        });
    });
});

router.delete('/:postId', (req, res, next) => {
    //deleting a post by id
    Post.deleteOne({_id: req.params.postId}, (error, post) => {
        if(error) return res.status(500).json({error: error});
        return res.status(200).json({
            message: 'Post removed',
            removedPost: post
        });
    });
});

module.exports = router;

With this, we are completing one part of our rest API. You can find all of the code in my GitHub repository.




#nodejs #expressjs #javascript #mobgodb

Author: Aleksandar Vasilevski |