Speed up your RESTful API development in Node.js with Swagger

Speed up your RESTful API development in Node.js with Swagger

In this tutorial, you'll seen how Swagger helps to develop a robust RESTful API while at the same time providing an elegant documentation that can boost up cooperation among developers (for example backend and frontend developers).

In this tutorial, you'll seen how Swagger helps to develop a robust RESTful API while at the same time providing an elegant documentation that can boost up cooperation among developers (for example backend and frontend developers).

Introduction

So it happened I started this project with friend of mine, a new app to launch on the mobile market. I offered to write the server-side, a simple web API. Once he got my code he immediately asked me "Sam how do I know the routes and so on?! Write a documentation or something!". And then... Swagger came up.

I presume many of you encountered this situation before or will in the future, a developer may write the best web API service but without proper documentation how can other developers discover and use it? So in this tutorial I want to introduce Swagger, a famous open-source framework to help you creating a RESTful APIs.

Swagger offers:

  • Interactive documentation.
  • Discoverability.
  • Client SDK generation.

In addition, it is supported by many programming languages.

One feature I like is the mock mode, which allows developers to design routes without writing a single line of code in javascript.

This is the approach I am going to follow in this tutorial: We first focus on the design and then on the code.

In the tutorial we are going create a RESTful API to manage our movies collection.

You can directly clone the app repository from my Github here.

Prerequisites

Let's move to the installation of Swagger.

Installation and run the example

As said before Swagger is supported in node.js through its own module, here is the link to the github project: The documentation is pretty straight-forward to help beginners understand and configure Swagger.

Once we open the command line we install the module (global).  npm install -g swagger 

Now let's create our project.

 swagger project create movie-collection 

The prompt asks for the framework we want to use, choose express. Once completed, let's take a look at the project directory:

-- api  
---- controllers 
------ hello_world.js
---- helpers
---- mocks
---- swagger
------ swagger.yaml

-- config 
---- default.yaml

-- test
---- api
------ controllers
-------- hello_world.js
------ helpers

-- app.js
-- package.json

N.B. Each subfolder contains a README.md that for simplicity I didn't include above.

  • api: As the name suggests, everything related to the API development, the controllers, mocks, helpers. A special mention goes to the /swagger folder which contains the file swagger.yaml, an important file we are going to edit to define everything related to the project information and routes. Throughout the tutorial I am going to explain it all so don't worry for now.
  • config: It contains default.yaml that, as the documentation states, drives the application's config directory. Though we are not going to customize the file I still suggest you guys to read the related documentation to understand the engine beneath the "magic" of declaring APIs without writing a line of code but through yaml or json.
  • test: Your test for controllers and helpers are (guess what!) created here.

Obviously, app.js is the main file which runs the server.

Have you noticed that there is already a controller called hello_world.js? Whenever you create a new project, the module adds an example route, a GET request to /hello which takes a name as parameter and greets the person. Not original at all, I give you that, but as we are beginners it is good see Swagger in action and get insight on how it works.

What about taking a look at the example in action?

In order to run the example I have to introduce Swagger editor.

Swagger editor is an elegant browser-based editor which really simplifies our efforts to develop a web API. In particular, it provides:

  • Validation and endpoint routing.
  • Docs on the fly generation.
  • Easy-to-read Yaml.


The picture above shows you the UI of the Swagger editor of our app. On the left you can see the yaml file we are going to edit (swagger.yaml remember?) while on the right the list of routes. By clicking on any of them we can have a good understanding of the parameter required, the format of request and responses, more generally a description of the route.

Now, to launch the example, start the app by running

 swagger project start 

NB whenever we modify a file, it automatically restarts the server (great!)

Then, open a second command line and launch the editor with:

 swagger project edit 

If everything went well the editor should open a new tab in your browser. On the right side of the page you should notice the example path for a GET request to /hello, so open the tab and, at the bottom, click on the button try this operation.


We are asked to enter a paramenter name: Go for it and test the route.

Here is my result:


Let's look at the yaml file on the left, precisely on the path part:


paths:
  /hello:
    # binds a127 app logic to a route
    x-swagger-router-controller: hello_world
    get:
      description: Returns 'Hello' to the caller
      # used as the method name of the controller
      operationId: hello
      parameters:
        - name: name
          in: query
          description: The name of the person to whom to say hello
          required: false
          type: string
      responses:
        "200":
          description: Success
          schema:
            # a pointer to a definition
            $ref: "#/definitions/HelloWorldResponse"
        # responses may fall through to errors
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

This short piece of yaml is what we need to define our routes:

x-swagger-router-controller: This is the controller, the file we have in the /api/controllers/hello_world.js.

Then the http methods must be listed, in the example just a GET.

operationId: This refers to the function, in the controller, in charge of the business logic.

parameters: The list of required parameters are defined here. The parameter name is the only one and you may see that it is in the path, it is not required and it is a string.

When it comes to the responses, Swagger shows its potential:

We can define a different response for each situation, given the http status or errors, a particular response can be defined. In the example, for the status "200" there is a pointer $ref to a response definition while for other statuses, we go to default with its own $ref.

Why is that? In order to keep the yaml file clean, we can define all our response under definitions while at the same time reusing the definition for different responses.

Before we finish with the yaml file, let's take a look at one more piece of code:

consumes:
  - application/json
# format of the responses to the client (Accepts)
produces:
  - application/json

On the top of the file it has defined what the routes consume and produce, it is the format of the request parameters and related response. Those rules are currently applying to all the paths defined in the file. It is also possible to customize these rules for a single path/http method by including these properties inside of it.

However, for the purpose of the tutorial, we will stick to application/json for all the routes, so a single definition on top is more than enough.

Lastly, let's take a look inside /api/controllers/hello_world.js to check the function hello:

function hello(req, res) {
    var name = req.swagger.params.name.value || 'stranger';
    var hello = util.format('Hello, %s', name);
    res.json(hello);
}

Nothing exotic here, though you should notice that with Swagger the req object has a new property swagger which contains the parameters we defined.

In the next section we finally start developing our app.

Working with mocks

Working with mocks does not require any code to be written, just editing the yaml file, so we can focus on the design part first.

First of all, rerun the project adding the flag -m to the command which tells Swagger to run in mock mode, then run the editor in the second window.

 swagger movie-collection start -m 

GET /movie

The first route returns the complete list of movies in our collection.

Delete the example /hello and add these lines of code:

/movie:
    # our controller name
    x-swagger-router-controller: movie
    get:
      description: get the movies list
      # define the type of response for Success "200" and Error
      responses:
        "200":
          description: Success
          schema:
            $ref: "#/definitions/GetMoviesListResponse"
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

First, we define our controller movie but there is no need to define the operationId in charge of the business logic. In addition, no need for the parameters. Take note, the successful response has a schema GetMoviesResponse.

The definitions, as usual, are listed at the bottom of the file, so copy and paste this code:

GetMoviesListResponse:
    required:
      - movies
    properties:
      # The array of movies
      movies:
        type: array
        items: 
          type: object
          properties:
            id:
              type: string
            title:
              type: string
            year:
              type: number

The response is nothing but an array of movies which contain a unique id, a title and the year.

NB swagger editor autosaves whatever we input, which is great for us, but I noticed that sometimes is gets stuck. So if that is the case for you, try to refresh the page.

Here is the code inside the editor which automatically created a better view on the right.


Now test the route and check the result:


Since we didn't define any operationId, in mock mode swagger returns a standard value according to the properties type.

Since id and title are strings, it returns "string" while for the year, as a number, it returns 1.

For the record, if not satisfied by the standard values, it is always possible to define the mock controller in /api/mocks and add a operationId. Then, the business logic just returns the answer in the format you defined in the definitions, but you can customize the values for each property.

However, I am not going to cover this step because I want to stick to the promise of working with mocks without writing a single line of code.

POST /movie

Let's create a route to add a new movie to the list. After get: add the following yaml lines:

 post:
      description: add a new movie to the list
      # movie info to be stored
      parameters:
        - name: title
          description: Movie properties
          in: body
          required: true
          schema:
            $ref: "#/definitions/Movie"
      responses:
        "200":
          description: Success
          schema:
            $ref: "#/definitions/GeneralResponse"
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

This time we have a parameter in the body which is simply the movie object with a title and year. Let's add the schema in the definitions:

Movie:
    type: object
    properties:
      title:
        type: string
        description: task object name
      year:
        type: number
        description: task description
    required:
      - title
      - year

NB We declared the two fields title and year as required.

What about the responses? The error response is always the same (reusability) while we defined a GeneralResponse. So, copy this code in the definition:

GeneralResponse:
    type: object
    properties:
      success:
        type: number
        description: returns 1 if successful
      description:
        type: string
        description: a short comment 
    required:
      - success
      - description

So to speak, we have a success field set to 1 and a description of the operations. This will be a common response for all our routes when we create or update a movie, the only difference will be the description text.

Let's try to add a movie:


Here is the result:

GET /movie/{id}

Now let's retrieve a single movie. This time we need the parameter in the path, precisely the unique id.

We first need to create a new path called /movie/{id} so copy and paste the following inside of it:

/movie/{id}:
    # our controller name
    x-swagger-router-controller: movie
    get:
      description: get a movie
      # define the type of response for Success "200" and Error
      parameters:
        - name: id
          type: string
          in: path
          required: true
      responses:
        "200":
          description: Success
          schema:
            $ref: "#/definitions/GetMovieResponse"
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

Notice that this time the parameter, in its own field in has path as value.

The successful response it's the movie object itself, so we need to add GetMovieResponse to the list of definitions:

GetMovieResponse:
    required:
      - id
      - title
      - year
    properties:
      id:
        type: string
      title: 
        type: string
      year:
        type: number
  Movie:
    type: object
    properties:
      title:
        type: string
        description: task object name
      year:
        type: number
        description: task description
    required:
      - title
      - year

Let's try it, for the id parameter, we can mock it with a fake one:


and here is the expected response:

PUT /movie/{id}

Now it's time create the route to update a movie, given the id in the path and a new title and year in the body. Copy the following code in the editor:

put:
      description: update a movie
      # define the parameters
      parameters:
        - name: id
          description: Movie id
          type: string
          in: path
          required: true
        - name: title
          description: Movie properties
          in: body
          required: true
          schema:
            $ref: "#/definitions/Movie"
      responses:
        "200":
          description: Success
          schema:
            $ref: "#/definitions/GeneralResponse"
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

I really like the order to define the parameters, no matter if they come from the body, path, header etc. They are all consequently listed where in assumes the correct value.

Notice the GeneralResponse, the same schema we used for adding a new movie

So, update a file:


And the result:

DELETE /movie/{id}

The last operation, let's delete a movie by a given id.

Copy and past the following code:

delete:
      description: delete a movie
      # define the parameters
      parameters:
        - name: id
          description: Movie id
          type: string
          in: path
          required: true
      responses:
        "200":
          description: Success
          schema:
            $ref: "#/definitions/GeneralResponse"
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

There is no need to further discuss the yaml file, we simply define the parameter and the response is GeneralResponse.

Try the operation:


Here is the result:


...And congratulations! We finished the design and now it's about time to write some javascript code.

Implement the controller and run the real app

The first thing to implement is the data-storage logic. Well, for the purpose of the tutorial we are not going to use any real database (MySQL, MongoDB...) but simply store the data in memory for the time the app runs.

We need to add a new package, crypto, to create the unique id for any stored movie:

npm install crypto --save

Let's create a file db.js /config. We are going to export a simple object which mocks the DB operations of add, retrieve, update and delete movies.

Here is the code to copy

'use strict;'
//Include crypto to generate the movie id
var crypto = require('crypto');

module.exports = function() {
    return {
        movieList : [],
        /*
         * Save the movie inside the "db".
         */
        save(movie) {
            movie.id = crypto.randomBytes(20).toString('hex'); // fast enough for our purpose
            this.movieList.push(movie);
            return 1;            
        },
        /*
         * Retrieve a movie with a given id or return all the movies if the id is undefined.
         */
        find(id) {
            if(id) {
                return this.movieList.find(element => {
                        return element.id === id;
                    });    
            }else {
                return this.movieList;
            }
        },
        /*
         * Delete a movie with the given id.
         */
        remove(id) {
            var found = 0;
            this.movieList = this.movieList.filter(element => {
                    if(element.id === id) {
                        found = 1;
                    }else {
                        return element.id !== id;
                    }
                });
            return found;            
        },
        /*
         * Update a movie with the given id
         */
        update(id, movie) {
            var movieIndex = this.movieList.findIndex(element => {
                return element.id === id;
            });
            if(movieIndex !== -1) {
                this.movieList[movieIndex].title = movie.title;
                this.movieList[movieIndex].year = movie.year;
                return 1;
            }else {
                return 0;
            }
        }        
    }
};  

The names of the functions are the same as the traditional operations with Mongoose. The first property is a list to store all the movies in the collection.

  • Save: We need to create a unique id, here is where crypto comes to help.
  • Find: If the id is passed, then it returns the specific movie, all the list otherwise.
  • Remove: The .filter method of javascript arrays does pretty much what we need, remove and return the found movie.
  • Update: Update the movie if it exists in the collection.

NB it is very likely some of you may find better ways to implement the operations above, or even point out that I should return a shallow copy of the list instead of the property itself. Because this is just a simple demo to fake CRUD operations, I would suggest you customize my object for your needs or even connect to a real database.

Movie controller

In /api/controllers create movie.js and paste the following code:

 'use strict';
    // Include our "db"
    var db = require('../../config/db')();
    // Exports all the functions to perform on the db
    module.exports = {getAll, save, getOne, update, delMovie};

    //GET /movie operationId
    function getAll(req, res, next) {
      res.json({ movies: db.find()});
    }
    //POST /movie operationId
    function save(req, res, next) {
        res.json({success: db.save(req.body), description: "Movie added to the list!"});
    }
    //GET /movie/{id} operationId
    function getOne(req, res, next) {
        var id = req.swagger.params.id.value; //req.swagger contains the path parameters
        var movie = db.find(id);
        if(movie) {
            res.json(movie);
        }else {
            res.status(204).send();
        }        
    }
    //PUT /movie/{id} operationId
    function update(req, res, next) {
        var id = req.swagger.params.id.value; //req.swagger contains the path parameters
        var movie = req.body;
        if(db.update(id, movie)){
            res.json({success: 1, description: "Movie updated!"});
        }else{
            res.status(204).send();
        }

    }
    //DELETE /movie/{id} operationId
    function delMovie(req, res, next) {
        var id = req.swagger.params.id.value; //req.swagger contains the path parameters
        if(db.remove(id)){
            res.json({success: 1, description: "Movie deleted!"});
        }else{
            res.status(204).send();
        }

    }

We first included our db and then exported all the functions we need to use as operationId and handled the business logic.

Notice that in the case that the id belongs to a movie, getOne, update, and delMovie return the same formatted json. We defined the same schema in the Swagger editor earlier.

On the other hand, in case there is no movie with the id of the request, we send a status of no content "204".

Now rerun the project without the -m flag and in the editor we have to add the operationId to each route, right after the "verb":

    #in /movie
    get:
      operationId: getAll
    #in /movie
    post:
      operationId: save
    #in /movie/{id}
    get:
      operationId: getOne
    #in /movie/{id}
    put:
      operationId: update
    #in /movie/{id}
    get:
      operationId: delMovie

Add a movie

The same as before, add a movie


Result:

Get all the movies

After we added more movies, let's try retrieve them all by a GET request to /movie:


Side note, a true Star Wars fan must have noticed that year of "The Empire Strikes Back" is wrong, it was released in 1980. In addition I forgot a "s" in the title.

Good, time to update the movie!

Update a movie

Add the id as a parameter in the PUT request to /movie/{id}


Result:


Then, let's doublecheck the movie is really updated

Get a single movie

We need a GET request to /movie/{id} with the movie id:


Result:


It works!

Delete a movie

Let's first get all the movies with a GET request to /movie:


We decide to delete "The force awakens", so let's send a DELETE request to /movie/{id} with the right id:


Result:


Finally, let's double check whether it was really deleted or not by sending a GET request to /movie/{id}:


The NOCONTENT response confirms it all worked.

Conclusions

In this tutorial we have seen how Swagger helps to develop a robust RESTful API while at the same time providing an elegant documentation that can boost up cooperation among developers (for example backend and frontend developers).

Creating a RESTful Web API with Node.js and Express.js from scratch

Creating a RESTful Web API with Node.js and Express.js from scratch

In this article, I’ll show you step by step how to create a RESTful Web API with Node.js and Express.js by building a simple and useful Todo API. This article assumes you have basic javascript knowledge and terminal using capabilities.

In this article, I’ll show you step by step how to create a RESTful Web API with Node.js and Express.js by building a simple and useful Todo API. This article assumes you have basic javascript knowledge and terminal using capabilities.

You can also build a Web API in Node.js by using another framework except Express.js but Express.js is one of the most popular web framework for Node.js.

You can found the final source code of this Web API in this github repository.

Let’s start to create our mentioned Web API.

Before start

If you have never used Node.js or npm package manager you should install them.

To check whether the Node.js is already installed on your computer, open your terminal and run node -v command. If you see your Node.js version it's installed. Otherwise go to below link.

Click here to download and install Node.js (You can choose LTS version)

And if you don’t have any IDE or text editor for writing javascript I advice you Visual Studio Code.

Click here to download VS Code (Optional)

About express-generator

In fact we could use <a href="https://expressjs.com/en/starter/generator.html" target="_blank">express-generator</a> tool which designed to creating an Express Web API quickly but I want to create this API from scratch because of that tool puts some extra files and folder structures that we don't need them now. But you can use this useful tool next time on creating new Web API. I won't use it now due to keep article simple.

Creating Project

Go to your workspace root folder and create a new folder there named "todo-api".

Then create "package.json" and "server.js" files into "todo-api" folder like below.

package.json

{
    "name": "todo-api",
    "version": "1.0.0",
    "scripts": {
        "start": "node server.js"
    },
    "dependencies": {
        "express": "^4.16.4"
    }
}

server.js

const http = require('http');
const express = require('express');
const app = express();
app.use(express.json());
app.use('/', function(req, res) {
    res.send('todo api works');
});
const server = http.createServer(app);
const port = 3000;
server.listen(port);
console.debug('Server listening on port ' + port);

After creating above files open your terminal in the "todo-api" folder and run npm installcommand.

This command will be install your project dependencies which pointed at the "package.json" file.

After finished package download process, downloaded dependency files will be installed into"node_modules" folder at the root of the "todo-api" folder.

After finished package installing then run npm start to start our Web API.

Now our Web API listening. To see result open your web browser then write localhost:3000 to address bar and press enter.

As result you’ll see our request handler response in your browser: “todo api works”.

This is a dead simple Express.js Web API. And it needs the some development. For example we need to an api endpoint to get todo items. So let’s add a new API endpoint for this.

Create a new folder named "routes" in the root of the "todo-api" folder.

Then create a "items.js" file inside of "routes" folder and put following codes inside it.

Your final folder structure should be like below;

/todo-api
/node_modules
/routes
    items.js
package.json
server.js

items.js

const express = require('express');
const router = express.Router();
const data = [
    {id: 1, title: 'Finalize project', order: 1, completed: false, createdOn: new Date()},
    {id: 2, title: 'Book ticket to London', order: 2, completed: false, createdOn: new Date()},
    {id: 3, title: 'Finish last article', order: 3, completed: false, createdOn: new Date()},
    {id: 4, title: 'Get a new t-shirt', order: 4, completed: false, createdOn: new Date()},
    {id: 5, title: 'Create dinner reservation', order: 5, completed: false, createdOn: new Date()},
];
router.get('/', function (req, res) {
    res.status(200).json(data);
});
router.get('/:id', function (req, res) {
    let found = data.find(function (item) {
        return item.id === parseInt(req.params.id);
    });
    if (found) {
        res.status(200).json(found);
    } else {
        res.sendStatus(404);
    }
});
module.exports = router;

Initial code of "items.js" file contains two endpoints. First one gets all todo items and second one gets one item which matches given id parameter.

Before testing items routes we should register it in the "server.js" file.

Modify "server.js" file like below to register new item routes.

server.js

const http = require('http');
const express = require('express');
const itemsRouter = require('./routes/items');
const app = express();
app.use(express.json());
app.use('/items', itemsRouter);
app.use('/', function(req, res) {
    res.send('todo api works');
});
const server = http.createServer(app);
const port = 3000;
server.listen(port);
console.debug('Server listening on port ' + port);

Now run npm start to start our Web API.

Then open your web browser and write localhost:3000/items to address bar and press enter.

You’ll see todo items json array in the response body.

And write localhost:3000/items/3 to address bar and press enter.

You’ll see the todo item which has id 3 in the response body.

But not finished up yet.

CRUD Operations and HTTP methods

I think we’ll need CRUD operations to Create, Read, Update and Delete todo items.

We have already two endpoints for getting items. So we need Create, Update and Delete endpoints.

Let’s add also these endpoints into the items.js file.

Our final "items.js" file and endpoints should be like below.

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

const data = [
  {id: 1, title: 'Finalize project',          order: 1, completed: false, createdOn: new Date()},
  {id: 2, title: 'Book ticket to London',     order: 2, completed: false, createdOn: new Date()},
  {id: 3, title: 'Finish last article',       order: 3, completed: false, createdOn: new Date()},
  {id: 4, title: 'Get a new t-shirt',         order: 4, completed: false, createdOn: new Date()},
  {id: 5, title: 'Create dinner reservation', order: 5, completed: false, createdOn: new Date()},
];

router.get('/', function (req, res) {
  res.status(200).json(data);
});

router.get('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    res.status(200).json(found);
  } else {
    res.sendStatus(404);
  }
});

router.post('/', function (req, res) {
  let itemIds = data.map(item => item.id);
  let orderNums = data.map(item => item.order);

  let newId = itemIds.length > 0 ? Math.max.apply(Math, itemIds) + 1 : 1;
  let newOrderNum = orderNums.length > 0 ? Math.max.apply(Math, orderNums) + 1 : 1;

  let newItem = {
    id: newId,
    title: req.body.title,
    order: newOrderNum,
    completed: false,
    createdOn: new Date()
  };

  data.push(newItem);

  res.status(201).json(newItem);
});

router.put('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    let updated = {
      id: found.id,
      title: req.body.title,
      order: req.body.order,
      completed: req.body.completed
    };

    let targetIndex = data.indexOf(found);

    data.splice(targetIndex, 1, updated);

    res.sendStatus(204);
  } else {
    res.sendStatus(404);
  }
});

router.delete('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    let targetIndex = data.indexOf(found);

    data.splice(targetIndex, 1);
  }

  res.sendStatus(204);
});

module.exports = router;

Short Explanation

I wanna explain shortly some points of our last codes.

First of all you must have noticed that our api works on a static data and keeps it on memory. All of our GET, POST, PUT and DELETE http methods just manipulate a json array. The purpose of this is to keep article simple and draw attention to the Web API structure.

Due to this situation our POST method has some extra logic such as calculating next item ids and order numbers.

So you can modify logic and data structures in these http methods to use a database or whatever you want.

Testing API with Postman

We have tested the GET methods of our Web API in our web browser and seen responses. But we can’t test directly POST, PUT and DELETE http methods in web browser.

If you want to test also other http methods you should use Postman or another http utility.

Now I’ll show you how to test the Web API with Postman

Before we start click here and install Postman.

When you first launch Postman after installing you’ll see start window. Close this start window by clicking close button on top right corner. Then you must see following screen.

An empty Postman request

Sending GET Request

Before sending a request to API we should start it by running npm startcommand as we do before.

After start the Web API and seeing “Server listening on…” message write localhost:3000/itemsto address bar as seen below and click Send button. You'll see todo items array as API response like below.

Sending a GET request with Postman

You can try similarly by giving an item id in request url like this localhost:3000/items/3

Sending POST Request

To sending a POST request and create a new todo item write localhost:3000/items to address bar and change HTTP verb to POST by clicking arrow at front of the address bar as seen below.

Sending a POST request with Postman

Before sending the POST request you should add request data to body of the request by clicking body tab and selecting raw and JSON as seen below.

Attaching a JSON body to POST request in Postman

Now click Send button to send POST request to the Web API. Then you must get “201 Created” http response code and seeing created item in the response body.

To see the last status of todo items send a get request to localhost:3000/itemsaddress. You must see newly created item at the end of the list.

Sending PUT Request

Sending PUT request is very similar to sending POST request.

The most obvious difference is request url should be pointed specific item like this localhost:3000/items/3

And you should choose PUT as http verb instead of POST and send all of the required data in the request body unlike POST.

For example you could send a JSON body in the PUT request as below.

An example JSON body for PUT request

{
    "title": "New title of todo item",
    "order": 3,
    "completed": false
}

When you click Send button you must get “204 No Content” http response code. You can check item you updated by sending a get request.

Sending DELETE Request

To send a DELETE request, change the request url to address a specific item id like this localhost:3000/items/3

And select DELETE as http verb and click Send button.

You must get “204 No Content” http response code as result of the DELETE operation.

Send a get request and see the last status of list.

About the DELETE Http Request

I want to say a few words about DELETE http request. You must have noticed something in our delete code. DELETE request returns “204 No Content” every situation.

Http DELETE requests are idempotent. So what that mean? If you delete a resource on server by sending DELETE request, it’s removed from the collection. And every next DELETE request on the same resource won’t change outcome. So you won’t get “404 Not Found” in the second request. Each request returns same response whether succeed or not. That’s mean idempotent operation.

Conclusion

Finally we’ve tested all http methods of our Web API.

As you can see, it works just fine.

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

Use FeathersJS to build a RESTful API server in Node.js

Use FeathersJS to build a RESTful API server in Node.js

In this article, you’ll learn how to build a RESTful API server in Node.js using Feathers.js

An API server, also known as an Application Server, is a program that provides data to front-end applications. It also handles business logic in the back end and provides restricted access to an organization’s database. It doesn’t just prevent unauthorized persons from accessing the data; it can also restrict logged-in users from accessing or altering data if they don’t have permission to do so.

Every application you build will need to provide a service to its end users. For that, your application will need data to process. You can use remote APIs to create a new service. For most applications, though, you’ll need to manage your own data store. A popular option is to use online data storage services such as Firebase. This way, you don’t have to deal with the nitty gritty details of running a distributed database server. However, your project needs may require the use of a full-fledged, in-house database management system such as MongoDB or Oracle. For your front-end application to access the data stored in the database, you’ll need a server application that sits between the database and the front-end application.

As illustrated in the diagram above, the work of an application server is to access data from a database using SQL or NoSQL commands and convert into a format that front-end applications (client browser) can understand — such as JSON. In addition, the application server can use various security protocols — such as HTTPS encryption and token authorization — to ensure that communication between the database and the client application is safe and secure. One main advantage of using such an architecture is that you can deploy applications that target different platforms — desktop, mobile, web, and so on — using the same application server. It’s also very easy to scale your application horizontally in order to serve more users efficiently with fast response times.

We’re going to build a simple API server and demonstrate the various features that Feathers provides.

Prerequisites

Before you begin following this tutorial, you’ll need to have a good foundation in the following topics:

Feathers is built on top of Express, a minimalist web framework for Node.js. If you’ve completed the tutorials demonstrated in the links, you’ll realize that it’s quite tiring building RESTful APIs using just Express. With Feathers, most of the repetitive work is already done for you. You only need to focus on configuring and customizing code. Let’s dive into the code and learn how this web framework works.

Project Creation

To get started with Feathers, you’ll need to install its command line application globally:

npm install -g @feathersjs/cli 

Next, create a new API project using the commands below:

mkdir contacts-api 
cd contacts-api 
feathers generate app 

Below are the options I chose. Feel free to choose any testing framework. Unfortunately, testing is beyond the focus of this article, so it won’t be covered here. Personally, I like simplicity, and that’s why I went with Jest.

Once the installation is complete, you can open you favorite code editor to look at the project files.

If you’ve completed the Express tutorials I listed in the prerequisites section, you shouldn’t be intimidated by the generated code. Here’s a brief summary that describes the folders and files.

Don’t be too concerned with what each file does right now. You’ll come to understand how they work in the course in this tutorial. For now, let’s confirm that the tests are working.

Linting

To ensure our project is compliant with the defined ESLint rules, just run the command npm test. If you’re on a Unix or Linux platform, this should run fine. If you’re on Windows, there are few things you need to adjust for the tests to run successfully.

First, go to package.json and look at the scripts section. Change the test line to this:

"scripts": {

  "test": "npm run eslint && SET NODE_ENV= npm run jest",

},

Next, if you’ve installed Prettier in Visual Studio Code, you’ll need to change the single quote setting to true in the Workspace settings tab:

{
  "prettier.singleQuote": true
}

Finally, make sure that, when you create or edit any file, the line ending is LF. If you’re using Visual Studio Code or a similar editor, you can check the current line ending style at the status bar. If it says CRLF, change to LF. Making those changes will help you pass the lint tests. Unfortunately, to make the tests pass will require a bit more work, which won’t be covered here.

Let’s look at how we can generate a CRUD RESTful interface.

Generate Service

Building a Restful CRUD API interface in Express requires a bit of work. In Feathers, all you have to do is execute a single command, answer a few questions and have the code generated for you:

$ feathers generate service
? What kind of service is it? NeDB
? What is the name of the service? contacts
? Which path should the service be registered on? /contacts
? What is the database connection string? nedb://../data
    force config\default.json
   create src\services\contacts\contacts.service.js
    force src\services\index.js
   create src\models\contacts.model.js
   create src\services\contacts\contacts.hooks.js
   create test\services\contacts.test.js

We’ll be using NeDB database for this tutorial. Feathers does support both SQL databases such as MySQL and NoSQL databases such as MongoDB. However, installing a database system — whether on your machine or on a cloud server — requires a certain amount of time configuring it. NeDB, on the other hand, is an in-memory database that’s 100% JavaScript and supports a subset of MongoDB API. There’s no configuration needed; you just install it. It’s a great database for prototyping and testing new applications. This is what we’ll use in this tutorial.

Let’s briefly look at some of the files that have been generated using this command:

  • services/contacts/contact.service.js. This is a Feathers service that provides the CRUD API endpoints for /contacts. Pretty small, isn’t it? This is because Feathers does the heavy lifting for us. It saves us from writing boilerplate CRUD code.
  • services/contacts/contact.hooks.js. This is where we customize how the CRUD logic behaves. We have the before section, where we can check or change data before Feathers reads or writes to the database. We also have an after section, where we can check or change the results from the database before it’s sent to the client application. We can do things like restricting access, data validation, performing join operations and calculating values for additional fields or columns.
  • models/contacts.model.js. This where we define a model and attach it to a database table. This is also where we define a schema which can be used to validate fields when a new record is inserted or updated. Unfortunately, NeDB doesn’t support schemas. However, I’ve provided an example of a model that’s connected to MongoDB, which supports the schema feature via the mongoose adapter:
"use strict";
 
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
require("mongoose-type-email");
 
const contactsSchema = new Schema({
  name: {
    first: { type: String, required: [true, "First Name is required"] },
    last: { type: String, required: false }
  },
  email: {
    type: mongoose.SchemaTypes.Email,
    required: [true, "Email is required"]
  },
  phone: {
    type: String,
    required: [true, "Phone is required"],
    validate: {
      validator: function(v) {
        return /^\+(?:[0-9] ?){6,14}[0-9]$/.test(v);
      },
      message: "{VALUE} is not a valid international phone number!"
    }
  },
  createdAt: { type: Date, default: Date.now },
  updatedAt: { type: Date, default: Date.now }
});
 
const contactsModel = mongoose.model("contacts", contactsSchema);
 
module.exports = contactsModel;

Despite the limitations of using NeDB, it’s still a great database for prototyping. Most NoSQL databases will allow you to submit data using any structure without having to define a schema first. It’s wiser to implement a schema once the project requirements have been realized. With a schema in place, Feathers will perform field validation for you using the rules you’ve defined. You’ll need a production-ready database such as MongoDB to be able to define a schema. Do note the configuration for the development database is defined at config/default.json:

"nedb": "../data" 

This is where database credentials are provided. We also have another config file called config/production.json. This is the production database configuration that’s used when you deploy your Feathers app. It’s important to use a separate database during development. Otherwise, you run the risk of deleting or corrupting business operational data on the production database.

Now that we have our CRUD service for contacts set up, it’s time to take it for a spin. You can start the Feather server using the command npm start. Do note that this server doesn’t support hot reloading. So you’ll need to restart it every time you make a change to the code. In order to interact with our Feathers app, we’ll need an API browser tool such as Postman or Insomnia. I’ll be using Insomnia in this tutorial, but you can follow along easily with Postman or any other tool.

Create a new GET request (press Ctrl + N) and give it the title “List Contacts”. In the URL section, enter http://localhost:3030/contacts. When you hit the Send button, you should have the following view:

Nothing! Our database is currently empty, so we need to create some new contacts. Create a new request called Create Contact. Fill in the rest of the fields as shown below:

In case you forgot to change the METHOD to POST in the above form, you can do so later. Change the method to POST and change the Body tab to JSON. Copy the following data in the JSON tab:

{
  "name": {
    "first": "Jack",
    "last": "Bauer"
  },
  "email": "[email protected]",
  "phone": "+1234567"
}

When you hit the Send button, you should get the following response. Notice that an _id has been generated for your new contact.

Go back to List Contacts and hit the Send button again. You should get the following result:

{
  "total": 1,
  "limit": 10,
  "skip": 0,
  "data": [
    {
      "name": {
        "first": "Jack",
        "last": "Bauer"
      },
      "email": "[email protected]",
      "phone": "+1234567",
      "_id": "ybnRxL6s2QEGhj4i"
    }
  ]
}

Go back to Create Contact and post a couple of new records:

{
  "name": {
    "first": "Chloe",
    "last": "O'Brian"
  },
  "email": "[email protected]",
  "phone": "+1987654"
}

{
  "name": {
    "first": "Renee",
    "last": "Walker"
  },
  "email": "[email protected]",
  "phone": "+150505050"
}

Let’s now perform an update. For this, we won’t use the UPDATE HTTP method. This method will completely overwrite a record. What we want to do is just overwrite a single field, not the the whole record. For that, we’ll use PATCH. Create a new request, Update Contact as illustrated below:

In the URL field, put http://localhost:3030/contacts/{_id}. Replace {_id} with the ID of the first record. Place the following data into the JSON tab:

{
  "email": "[email protected]"
}

Hit the Send button. You should get the following result:

Notice how the the rest of the fields remain intact. Next, we’re going to delete a record. This one is easy. Just create a new DELETE request and name it Delete Contact. In the URL field, use the format http://localhost:3030/contacts/{_id}. Just like before, replace {_id} with the ID of the record you want to delete. Hitting Send will delete that record for you. You can confirm by running the List Contact request again.

We’ve just verified that all CRUD operations are running okay. In the next section, we’ll learn how to set up authentication.

Authentication

Right now, our /contacts API endpoint is unprotected. If we were to deploy our app to a cloud server, anyone with the URL can access and manipulate our records. To restrict access, we need to set up authentication. We’ll use the JSON Web Token to implement authentication to our API application. Run the following command to set it up:

feathers generate authentication 

As you can see below, Feathers does support different ways of authenticating users. The easiest one to set up is the “Local Username + Password” option.

Pick the following options for the rest of the questions.

You can review the files that have been generated by the command you just executed:

The next step is to create a new user. We can do this using Insomnia or any other API browser tool. Create a new request and call it Create User:

In the JSON tab, send the following data:

{
  "email": "[email protected]",
  "password": "secret"
}

You should get a similar response as below:

We now have a user. Let’s confirm this by creating a new request List Users and sending the URL http://localhost:3030/users. Unfortunately, you’ll get the following response:

We need to get authenticated in order to access this data. Since we haven’t developed a front-end application that we can use to log in, we’re going to continue using the API browser. Create a new request and call it “Get JWT Token”. Fill in the form as illustrated below:

This request uses the POST method. Perhaps you can rename the request “Login” for better clarity. In the JSON tab, copy the following data:

{
  "strategy": "local",
  "email": "[email protected]",
  "password": "secret"
}

You should get the following response after you hit the send button:

Copy the token code (without the double quotation marks). Go to List Users request page, select the Auth tab and pick Bearer. Paste this token code in the TOKEN field.

When you hit the Send button, you should get a list of users. Do note that our authentication system is not completely secure. Anyone with the /users URL endpoint can create a new account and gain access to our system. To prevent unauthorized creation of new accounts, we need to restrict that endpoint as well. Open the file services/users/users.hooks.js and update the following piece of code:

module.exports = {
  before: {
    //...
    create: [ hashPassword(), authenticate('jwt') ],
    //...

This will ensure only authenticated users can create new accounts. The next step is to protect the /contacts endpoint as well. Simply open the file services/contacts/contacts.hooks.js and update accordingly:

const { authenticate } = require('@feathersjs/authentication').hooks;
 
module.exports = {
  before: {
    all: [authenticate('jwt')],
    //...
  },

Restart the Feathers server for the code changes to take effect. If you try running the List Contacts request, you’ll get the following response:

{
  "name": "NotAuthenticated",
  "message": "No auth token",
  "code": 401,
  "className": "not-authenticated",
  "data": {},
  "errors": {}
}

To get authenticated, you’ll need to set the Bearer token just like you did before. Once you’ve done this, you can send your request and you should receive your list of contacts. Do note that the token you acquired earlier will expire after a day. For efficiency’s sake, it’s better to use environment variables so that it becomes easier to update all your API request parameters at once. When building a front-end application, you’ll need to store this token inside local storage. Don’t use cookies. Otherwise, your app will be susceptible to CSRF attacks. Check out the Feathers documentation on security to learn more about other security risks you should be aware of.

Now that you’ve set up authentication, any new service you create after this will give you the option of protecting your new endpoint. Let’s now look at the final topic for this tutorial in the next section.

Hooks

Hooks are middleware functions that are attached to before, after or on errors of a service method. They are commonly used to handle things like logging, restricting access, protecting fields, populating related entities, sending notifications, and so on. If you look at services/users/users.hooks.js, you can see some built-in Feathers hooks in use. We’re going to create our own custom hook. First, stop the server and delete the data/contacts.db database table. Next, create a new hook by generating it using this command:

feathers generate hooks 

Use the following options to create the custom hook process-contact:

What we want to do in this hook is to inject two new fields right before the Create Contact request is processed.

  • createdBy: link to currently logged in user by _id
  • createdOn: add creation date

Open the file hooks/process-contact.js and update the file as follows:

module.exports = function(options = {}) {
  return async context => {
    return context;
  };
};

module.exports = function(options = {}) {
  return async context => {
    // Get authenticated user
    const user = context.params.user;
 
    //Extract Submitted Data
    const { data } = context;
 
    // Add new Fields
    context.data = {
      ...data, // Preserve submitted data
      createdBy: user._id,
      createdOn: new Date()
    };
    return context;
  };
};

Next, create another hook, populate-user, that will attach a user object to each contact record when requested. Follow the instructions as per the below screenshot:

Open the file hooks/populate-user and insert the following code:

// eslint-disable-next-line no-unused-vars
module.exports = function(options = {}) {
  return async context => {
    const { app, method, result, params } = context;
 
    // Ensure contacts is an array. If it's a single contact, wrap it into an array
    const contacts = method === "find" ? result.data : [result];
 
    // Fetch user object from each contact's createdBy
    await Promise.all(
      contacts.map(async contact => {
        contact.user = await app
          .service("users")
          .get(contact.createdBy, params);
      })
    );
 
    return context;
  };
}; 

Read the comments to understand how it works. You can now start the server. Create the three contacts again using the Create Contact request. Set the bearer token, if you haven’t already. Otherwise, you’ll receive an authorization error. This is the kind of response you should be getting when you create a new contact:

Summary

I hope you’ve now learned how to quickly build your own RESTful API server. We’ve only touched the basics and you should go through the full guide to discover more features that Feathers can provide in helping you implement advanced features with minimal effort. You should also check out the Awesome Feathers page, which contains a treasure trove of resources. Whether you need a plugin, project example or a tutorial, you’ll probably find a link there. You should also check out Feathers-plus CLI, which is Feathers on steroids. It adds new features on top of what the Feathers CLI already provides, such as generating code for seeding and GraphQL support.

If you’d like to further advance the contacts-api project, I’d encourage you to create a new front-end application using a framework of your choice. Build a log-in screen and CRUD pages for the /contacts and /users endpoints. Have fun implementing the challenge.