DEV Community

Bernhard Häussermann
Bernhard Häussermann

Posted on • Edited on

Building a Node.js REST API

This article is the first of a series outlining the steps to build a REST API from scratch that runs in Node.js using the Express web application framework. In this article, we will show how to set up the project. The following articles will build on this by adding features such as request / response validation and a Swagger UI page for online documentation.

Project setup

The configuration needed for following along in your own project is minimal. All that is required to get started is a package.json file generated using npm init.

Ensure that "type": "module" is set within the package.json file. This declares our package as an ES 6 module so we can use import syntax to import packages in our source code.

Add a basic web server

Add the Express package as a run-time dependency:

npm install --save express
Enter fullscreen mode Exit fullscreen mode

Then create a new file server.js in a folder named src with the following contents:

And just like that we have a working web endpoint listening at port 3000!

In the code above, we have defined a single route which we can use to test that the service is running.

Testing that our endpoint works is easy as:

  1. Run node src/server.js
  2. Using your favourite REST API testing tool (I recommend Postman), request GET localhost:3000/greeting

We should get a 200-response containing some text as a JSON string.

Adding automatic restarts

Running our server script as above means that whenever a change is made to a source file, we need to manually stop and start the program for the changes to take effect. This is easy to fix, thanks to a simple tool called nodemon. We can easily add a script that will restart our application whenever a source file is changed.

First, we add nodemon as a development dependency to the project:

npm install --save-dev nodemon
Enter fullscreen mode Exit fullscreen mode

We then define the following set of scripts in package.json:

"scripts": {
  "run": "node src/server.js",
  "run:watch": "nodemon src/server.js --watch src",
  "start": "npm run run:watch"
},
Enter fullscreen mode Exit fullscreen mode

The run script will run the API without automatic restarts as before if we execute npm run run

The run:watch script will run the API, restarting it whenever any file inside the src folder changes.

The start script will simply run the run:watch script but can be executed merely as npm start

Structuring the code based on REST resources

Most REST APIs have their routes arranged based on a number of resources. We will define employees as a REST resource with CRUD (create, retrieve, update, delete) operations. Keeping with REST conventions, we will define the following routes:

  • GET /employees: Return the list of all employees.
  • GET /employees/{employee-id}: Gets the single employee having the ID {employee-id}. Return a 404 (Not Found) response code if no employee with the specified ID was found.
  • POST /employees: Add a new employee entry.
  • PUT /employees/{employee-id}: Update the details of the employee having the ID {employee-id}.
  • DELETE /employees/{employee-id}: Delete the employee having the ID {employee-id}.

If we keep on defining all our routes and the code implementing them directly in server.js, the code will quickly become unmanageable. To help keep the code organized, I recommend defining each REST resource's routes in one file and implementing them in another. We call the file defining the routes the "controller" and the file containing the implementation the "service".

Implementing the employees resource leads to the following folder structure:

src
   controllers
      employees-controller.js
   services
      employees-service.js
   server.js
Enter fullscreen mode Exit fullscreen mode

Here is a simple implementation of employees-service.js.

Whereas in a typical application the objects would be persisted in some kind of a database, we store the list of employees in memory for simplicity's sake.

The EmployeeNotFoundError class is defined in a file named employee-not-found-error.js as:

Note that EmployeesService does not contain any logic that relates to REST notions like query parameters, response statuses etc. The EmployeesService is concerned solely with the details of how employees are persisted. This is in adherence to the Single-responsibility principle. It also makes the class easier to test using some testing framework.

The EmployeesController class deals with the REST-related specifics and hooks up the REST routes to their respective implementations in the employees service:

Note the block-comment before the registerRoutes() method. This is a JSDoc comment that specifies descriptions to use when generating documentation using JSDoc. However, in this case, we add the block-comment only to inform our IDE of the expected types of the method's parameters. Visual Studio Code, for instance, has built-in support for JSDoc and will interpret the type-declarations of the app and controller parameters inside the block-comment to inform its IntelliSense and code completion functionality.

We define the ExpressError class to represent a REST error which is to be handled by a generic error route handler function in server.js:

Finally, we make the following changes to server.js:

  1. To register the routes, we now simply call registerRoutes() passing in the Express application and a new instance of EmployeesService.
  2. We also add a route handler for returning the proper response when an error is thrown.
  3. To parse the request body of the POST and PUT operations as JSON payloads, we add the statement app.use(express.json())

We can now use our favourite REST client to test the different routes to verify the behaviour is as expected:

  1. Get all employees

    GET localhost:3000/employees
    
  2. Get employee 1

    GET localhost:3000/employees/1
    
  3. Get employee 2 (doesn't exist)

    GET localhost:3000/employees/2
    
  4. Update employee 1's first name

    PUT localhost:3000/employees/1
    {
      "firstName": "André"
    }
    
  5. Add a new employee

    POST localhost:3000/employees
    {
      "lastName": "King",
      "firstName": "Robert",
      "title": "Sales Representative"
    }
    
  6. Delete employee

    DELETE localhost:3000/employees/2
    

In conclusion

Getting a REST API off the ground using Node.js and Express is relatively straightforward to do, and by defining separate controller and service classes for each type of API resource, we keep the REST-specific details separate from the underlying implementation details of each operation.

Future posts will show how we can quickly add middleware such as request / response validation and Swagger documentation thanks to Node.js packages that are available for this.

The code for the API developed in this article is available on GitHub here.

Top comments (1)

Collapse
 
lexiebkm profile image
Alexander B.K.

Everytime someone posts about REST, esp Restful, APIs, I want to know whether it really implements the principles of REST. One concept that I have never found to be implemented in articles/examples is HATEOAS.
I want to see how HateOAs is implemented in a real example, considering that from what have read about it in some sources, it is a complex concept that seems hard to implement in a real application.
And I have just read, people tend to avoid it for its complexity.
If I don't really need it, then I think I have sufficient knowledge of REST to implement in my application.