Codementor Events

Making a RESTful Backend with Node.js

Published Jun 19, 2018Last updated Oct 03, 2018

REST (Representational State Transfer) is an architectural style for developing web services that advocates for using the HTTP request types as they were originally intended. GET requests are for lookups, PUT requests are for mutation, POST requests are for creation, and DELETE requests are for deletion. REST also requires that the client doesn't need to know anything about the structure of the API in order for it to be used.

In this article, we'll look at the process for building a modern backend for a note-taking application in Node.js that adheres to RESTful principles.

Start by running the following command in the project's root directory to initialize the package.json:

npm init

Then, install the needed dependencies:

npm i --save express mongoose
npm i --save-dev babel-cli babel-preset-env nodemon

A brief explaination for each of these modules:

  • Express is what we'll use to bootstrap the web server
  • Mongoose interacts with the MongoDB database
  • Babel compiles ES6+ javascript into a browser-readable format
  • Nodemon automatically restarts the application when file changes are detected

Babel

Create a new file called .babelrc and populate it with the following:

{ 
  "presets": ["env"]
}

This tells bable to use the "env" plugin, which is specifically for transpiling ES6 code.

After this, head over to package.json and set up the "scripts" section as follows:

{ 
... 
"scripts": { 
  "start": "nodemon server.js --exec babel-node --presets env", 
  "release": "npm run clean && npm run build && npm run serve", 
  "clean": "rm -rf dist && mkdir dist", "build": "babel . -s -D -d dist --presets env --ignore node_modules", 
  "serve": "node dist/server.js" 
}, 
...
}

With this configuration, you can execute npm start during active development to enable live-reloading, and npm run release to deploy the application for production use.

Express

Now that the application is set up for the use of ES6, we'll move on to setting up the server.

Start by setting creating the directory structure for our project files:

notes-app/
├── controllers/
│ └── notebookController.js
│ └── notebookModel.js
├── models/ 
├── routes/
│ └── index.js
├── app.js
├── server.js
└── package.json

Server

Now, that you've got the barebones structure of the application created, head over to the server.js file, and populate it with this basic server code:

import app from './app';

const port = process.env.PORT || '3000'; app.listen(port); 

console.log(`Listening on port ${port}`);

Models

We're going to need to tell the server what information our note objects are going to contain. To do this, we'll set up a database schema like the following in notebookModel.js:

import mongoose, {
    Schema
} from 'mongoose';

/**
 * Create database scheme for notes
 */
const NoteScheme = new Schema({
    title: {
        type: String,
        required: "What is the note's title?"
    },
    text: {
        type: String,
        required: "What is the note?"
    },
    date: {
        type: Date,
        default: new Date
    }
});

export default mongoose.model('Note', NoteScheme);

Routes

Now that we have our Note model set up, we'll move on to our routes. Each route will determine which method to execute when they are invoked by a client, depending on which HTTP method is specified.

In our app, we will have routes that do the following:

  • Get all notes
  • Get a specific note
  • Create a note
  • Update an existing note
  • Delete a note

In the index.js file, we'll define the routes like so:

import notebook from '../controllers/notebookController';

export default (app) => {
    app.route('/notes')
        .get(notebook.getAllNotes)
        .post(notebook.createNote);

    app.route('/notes/:noteId')
        .get(notebook.getNote)
        .put(notebook.updateNote)
        .delete(notebook.deleteNote);
};

Controllers

With our routes defined, we can move on to write the functions that each route will invoke in notebookController.js:

import mongoose from 'mongoose'; 
import note from '../models/notebookModel.js';

exports.getNote = (req, res) => {
    note.findById(req.params.noteId, (err, note) => {
        if (err) {
            res.send(err);
        }

        res.json(note);
    });
};

exports.getAllNotes = (req, res) => {
    note.find({}, (err, notes) => {
        if (err) {
            res.send(err);
        }

        res.json(notes);
    });
};

exports.createNote = (req, res) => {
    const newNote = new note(req.body);

    newNote.save((err, note) => {
        if (err) {
            res.send(err);
        }

        res.json(note);
    });
};

exports.updateNote = (req, res) => {
    note.findOneAndUpdate({
        _id: req.params.noteId
    }, req.body,
        (err, note) => {
            if (err) {
                res.send(err);
            }

            res.json(note);
        });
};

exports.deleteNote = (req, res) => {
    note.remove({
        _id: req.params.noteId
    }, (err) => {
        if (err) {
            res.send(err);
        }

        res.json({
            message: `note ${req.params.noteId} successfully deleted`
        });
    });
};

App

To tie everything together, we'll write our app.js file which tells the server where the routes are, and what middleware functions to use. These are functions that have access to the request and response objects, so they can be used to handle errors and to parse request data.

Our app.js file should look something like this:

import express from 'express';
import mongoose from 'mongoose';
import bodyParser from 'body-parser';

import routes from './routes/index.js';

const app = express();

/**
    * Connect to the database
    */

mongoose.connect('mongodb://localhost');

/**
    * Middleware
    */

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// catch 400
app.use((err, req, res, next) => {
    console.log(err.stack);
    res.status(400).send(`Error: ${res.originUrl} not found`);
    next();
});

// catch 500
app.use((err, req, res, next) => {
    console.log(err.stack)
    res.status(500).send(`Error: ${err}`);
    next();
});

/**
    * Register the routes
    */

routes(app);

export default app;

Usage

Our app is now complete, and we can move on to making REST calls to create, update, fetch, and delete notes.

REST calls can be made with a number of programs, but I personally suggest Insomnia. A simple usage guide can be found on their "Getting started" page.

First, to get a list of all notes in the database, execute a GET request at localhost:3000/notes :

The response should be an empty array since we haven't created any notes yet.

To create a note, change the data type (the dropdown directly below the URL field) to "Form URL Encoded" if it isn't already, specify "title" and "text", and execute a POST request at the same URL:

You will notice that several pieces of information are returned: The "_id" parameter is a unique number defined when the POST request is executed, and we can use it to tell the server what note we want to interact with.

For example, if we want to change that note's text, we can execute a PUT request with the note's id appended to the URL after specifying the new text:

By default, mongoose's "findOneAndUpdate" method returns the unaltered object, so the response will still show the old note text. However, if you then change the request type to "GET", you will see the updated object:

Finally, to delete a note, change the request type to "DELETE" and execute a request at the appropriate URL:

Discover and read more posts from Shane Wignall
get started
post comments2Replies
Geoffrey Callaghan
a year ago

Fabform is a good form backend service to take a look at

bhagyashree patil
6 years ago

I have seen your code fully and make it run also but I want to make a form in using the mean stack but the connection with MongoDB is not happening and I have installed every file and all but still, it is not running please help me out. I just want a simple form code which will take input from a user and store it in MongoDB.