Building REST service with Express.js

REST services became a de-facto standard of data exchange for public APIs. Twitter, GitHub, Parse are just few examples that rely on REST to exchange data with the clients. Before we start to write our first REST service with Node, let's see what exactly is REST and how it works. REST stands for 'REpresentational State Transfer'. It leverages the semantics of HTTP protocol to express operations over a some collection of items (data). REST typically uses JSON to send and receive data. NOTE: This is a common misconception to call every HTTP-based JSON service REST. This is not correct RESTful service can exchange data using any format: JSON, XML, YAML or anything else. JSON turned out to be the most popular format but it is in no way a dogma. REST relies on HTTP methods to express what you do with data. GET to retrieve an item (or collection), POST to add new item to collection, PUT to update the whole item, DELETE to remove an item. Sometimes PATCH is also used to update *part* of an item but this is rather rare. Now let's implement a simple REST service with Node.js and Express.js framework. Our goal is to expose a collection of books. In the new folder run npm init, and create package.json as you usually do for every fresh Node project. Now let's install the dependencies: > npm install --save express body-parser Now we are ready to build our main.js file. Let's start from initializing express, and adding body-parser middleware.
  1. const express = require('express');
  2. const bodyParser = require('body-parser');
  3.  
  4. const app = express();
  5. app.use(bodyParser.json());
  6.  
  7.  
  8. app.listen(3000, () => {
  9.   console.log('Book REST is on 0.0.0.0:3000!');
  10. });
This app is not doing much, so let's add a route to get the whole collection of books. Also, I'll create an array to hold book objects. In a real-world scenario, REST services are backed up by databases (or other upstream services) but for the sake of this example, an array will be a perfect fit.
  1. const books = [
  2.   {
  3.     id: 1,
  4.     title: 'The Time Machine'
  5.   },
  6.   {
  7.     id: 2,
  8.     title: 'Atlas Shrugged'
  9.   }
  10. ];
  11.  
  12. app.get('/books', (req, res) => {
  13.   res.json(books);
  14. });
Now we can test the service with curl - a command line HTTP client (download curl if it is not available on your system). $ curl localhost:3000/books If everything is set correctly, the server should respond with: [{"id":1,"title":"The Time Machine"},{"id":2,"title":"Atlas Shrugged"}] Now let's see how to add a new book. Besides making a new route, we'll have to create a function that generates the new ID for a book each time you add one. This is a great place to use EcmaScript-2016 generators feature. Let's write a `getId()` generator function and then use in places where we need to get new ID.
  1.   const idGenerator = function* () {
  2.   let max = 0;
  3.   while(true) {
  4.     yield ++max;
  5.   }
  6. };
  7.  
  8. const gen = idGenerator();
  9.  
  10. const books = [
  11.   { id: gen.next().value, title: 'The Time Machine' },
  12.   { id: gen.next().value, title: 'Atlas Shrugged' }
  13. ];
Our code has just become much cleaner as we are not using pre-defined "magic" IDs, but instead relying on a function to generate and ID each time it is needed. We could also swap the implementation to use UUID instead of sequential integers, for example. Now its time to add a route:
  1.   app.post('/books', (req, res) => {
  2.   const book = Object.assign({ id: nextId() }, req.body);
  3.   books.push(book);
  4.   res.json(book);
  5. });
And test it: curl -X POST -H "Content-Type: application/json" -d "{\"title\": \"Three Comrades\"}" localhost:3000/books Next, we'll make the books editable. To do that, we have to implement PUT route:
  1.   app.put('/books/:id', (req, res) => {
  2.   const book = books.find((b) => b.id === +req.params.id);
  3.   Object.assign(book, req.body);
  4.   res.json(book);
  5. });
Here's how you would test the route: curl -X PUT -H "Content-Type: application/json" -d "{\"title\": \"Edited Title\"}" localhost:3000/books/1 Finally, to complete the set of basic operations, we need to create DELETE route:
  1.   app.delete('/books/:id', (req, res) => {
  2.   const idx = books.findIndex((b) => b.id === +req.params.id);
  3.   const book = books.splice(idx, 1);
  4.   res.json(book);
  5. });
This route is also trivial, it finds the index of the book in array by its ID and then removes it from the array. Let's test if we can really delete books: curl -X DELETE localhost:3000/books/1 The returned book is the one that you just deleted! Here's the full source code of our node.js application:
  1. const express = require('express');
  2. const bodyParser = require('body-parser');
  3.  
  4. const idGenerator = function* () {
  5.   let max = 0;
  6.   while(true) {
  7.     yield ++max;
  8.   }
  9. };
  10.  
  11. const gen = idGenerator();
  12.  
  13. const books = [
  14.   { id: gen.next().value, title: 'The Time Machine' },
  15.   { id: gen.next().value, title: 'Atlas Shrugged' }
  16. ];
  17.  
  18. const app = express();
  19. app.use(bodyParser.json());
  20.  
  21. app.get('/books', (req, res) => {
  22.   res.json(books);
  23. });
  24.  
  25. app.post('/books', (req, res) => {
  26.   const book = Object.assign({ id: nextId() }, req.body);
  27.   books.push(book);
  28.   res.json(book);
  29. });
  30.  
  31. app.put('/books/:id', (req, res) => {
  32.   const book = books.find((b) => b.id === +req.params.id);
  33.   Object.assign(book, req.body);
  34.   res.json(book);
  35. });
  36.  
  37. app.delete('/books/:id', (req, res) => {
  38.   const idx = books.findIndex((b) => b.id === +req.params.id);
  39.   const book = books.splice(idx, 1);
  40.   res.json(book);
  41. });
  42.  
  43. app.listen(3000, () => {
  44.   console.log('Book REST is on 0.0.0.0:3000!');
  45. });
As you see, it took less than 40 lines of code to create a simple REST service with express.js. For production use you would add more features like error checking and reporting, logging, authentication, integration with database. However this code will be a good starting point for your experiments.

Add new comment