Blog

27 Dec Get to know the tool of Node.js for the creation of REST APIs

We are going to develop a small RESTfull API with Node.js. The project will be attached node-rest-shop.zip.

Introduction

Node.js is an execution environment for JavaScript built with the Chrome V8 JavaScript engine. It works in runtime, open source, multi-platform, which allows developers to create all kinds of server-side tools and applications in JavaScript. Real-time execution is intended to be used outside the context of a web browser (that is, running directly on a computer or server operating system).

Node.js changes the JavaScript paradigm, where the code was hosted on the client side and only the web server executed it.

Technologies

For the explanation of how to create a small REST application we will use the following tools / technologies:

NPM is the package manager of Node.js (NPM of English: Node Packet Manager) provides access to hundreds or thousands of reusable packages. In addition, dependency resolution is best in class and can be used to automate compilation tools.

Express.js is the most popular web framework of Node.js, and it is the underlying library for a large number of other Node.js web frameworks.

MongoDB is a non-relational database with the desirable scalability and flexibility in queries and indexing. It also stores data in flexible documents, similar to JSON, fields can vary from one document to another as well as the structure itself. The collection in MongoDB is correlated with the objects in the application code, facilitating the work of the data. Mongoose is a framework to work with MongoDB.

Morgan is a logging package for Node.js.

Jest is a testing framework released for the first time in 2014, and although initially aroused great interest, the project was inactive for a while. However, Facebook intervened in 2016 to improve Jest, and published some releases with impressive changes that make it worthwhile to reconsider. The only similarity of Jest compared to the initial open source version is the name and logo. Everything else has been changed and rewritten.

Supertest is a library for unit testing of node.js.

Technical aggregates

Characteristics of NPM 1) The document where all the required information about the packages required in the application is package.json. It must be in JSON format, not just JavaScripts. 2) If the application is going to be published, you must add a name and version in this file, for example:

package.json

1

2

3

4

{

“name”:“api-example”,

“version”:“1.0.0”

}

Express has the following features and / or functionalities. Writing request handlers with different HTTP verbs in different URL paths (routes). Integration with “view” rendering engines to generate responses by entering data in templates. Set web application settings such as which port to use to connect, and the location of the templates that are used to render the response.

The server proposes an event-based principle, which is central to the internal operation between Express and NodeJS. It is characterized by the lack of data blocking during the processing of any request received. And it is achieved through the parallel processing of data to avoid delaying the flow of data. Therefore, NodeJS can process a greater number of requests in a more efficient manner.

Creating a RESTFull API

The development will contain a Javascript module called server.js in which the api publication (about node.js) is configured as a port and protocol (for example https). This will be responsible for the deployment of the app.js which will receive orders and delegate to who (router) will take care of such an order, in addition to dealing with errors of unsupported orders or internal server errors.

When the order arrives at one of the routers (in our case, products.js) and depending on the method invoked, the implementation of the same will be accessed. And which will require a model of the schema in the database for the manipulation of the data (product.js).

To create a project with Node.js we execute the command npm init, it will ask us for information about our api as the name and version. This command will create the necessary file (package.json) for the execution of the app in Node.js.

Then we started to install the frameworks that we will use npm install -save express, which will add the libraries of that framework to our project.

Going directly to the implementation we create a RESTFull API and accept requests / products. At first we will only accept get and post orders without using database and then the code will be increased for the use of data.

The structure of the project will look like this:

For the reception of orders we develop an app.js module which will use the Express.js framework to delegate the routing of the orders. In this case, it will receive ‘/ products’ in addition to handling two types of errors: unsupported and internal orders.

app.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

constexpress=require(‘express’);

constapp=express();

constproductRoutes=require(‘./api/products/routes/products’);

app.use(‘/products’,productRoutes);

app.use((req,res,next)=>{

consterror=newError(‘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;

In order for this API to be usable and published on a server, we need a new module called server.js in which port and protocol are configured where our app will be deployed.

server.js

1

2

3

4

5

6

7

8

consthttp=require(‘http’);

constapp=require(‘./app’);

constport=process.env.port||9000;

constserver=http.createServer(app);

server.listen(port);

After the orders are received by the app.js, they are redirected to the router that will attend them. To meet these requests we create a router (products.js) which will be accepted by the server. In this example, it will only accept requests by GET and POST http methods and will override a json object with a message.

products.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

constexpress=require(‘express’);

constrouter=express.Router();

router.get(‘/’,(req,res,next)=>{

res.status(200).json({

message:‘Capturando el pedido GET sobre /products’

});

});

router.post(‘/’,(req,res,next)=>{

res.status(200).json({

message:‘Capturando el pedido POST sobre /products’

});

});

It can be seen that it captures requests ‘/’ since our API already processes the ‘/ products’ orders.

Using MongoDB

In our application only the use of the mongoose framework is required and then connect with our scheme. mongodb Objects As a first step we must create our association module between our code and a collection of data in our

mongodb

1

2

3

constmongoose=require(‘mongoose’);

mongoose.connect(‘mongodb://127.0.0.1:27017/TU_ESQUEMA’);

Objects

As a first step we must create our association module between our code and a collection of data in our MongoDB scheme.

product.js

1

2

3

4

5

6

7

8

9

constmongoose=require(‘mongoose’);

constproductSchema=mongoose.Schema({

_id:mongoose.Schema.Types.ObjectId,

name:{type:String,require:true},

price:{type:Number,require:true}

});

module.exports=mongoose.model(‘Product’,productSchema);

Manipulation of objects

Just importing the framework and the module created previously, we are already enabled to manipulate this collection in our scheme.

For this we modify the router code products.js to obtain data from our scheme. Here we will use functions provided by mongoose like ‘find’ to search for data, ‘select’ to select the data that we want to project in our query (in this case only name, price and id).

Then we execute the query with the function ‘exec’ and to manipulate the data synchronously we concatenate the ‘then’ function. products.js

products.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

router.get(‘/’,(req,res,next)=>{

Product.find()

.select(‘name price _id’)

.exec()

.then(docs=>{

constresponse={

count:docs.length,

products:docs.map(doc=>{

return{

name:doc.name,

price:doc.price,

_id:doc._id,

request:{

type:‘GET’,

url:‘http://localhost:9100/products/’+doc._id

}

}

})

};

res.status(200).json(response);

Unit test

In this simple API we only create a product router test. For this we are going to install the jest framework together with supertest. products_router.test.js

products_router.test.js

1

2

3

4

5

6

7

8

9

constrequest=require(‘supertest’);

constapp=require(‘../app’);

describe(‘Test para root’,()=>{

test(‘Debe responder el método GET’,()=>{

returnrequest(app).get(“/products”).then(response=>{

expect(response.statusCode).toBe(200)

})

});

})

Configuration and execution

To configure the execution of the test from the command line using npm test, it is necessary to modify a line in the package.json file.

package.json

1

2

“scripts”:{

“test”:“jest”

And as a result of the test we obtain an interface like the following:

To develop more complete, asynchronous tests with waits, without using supertest see reference [8].

Other snacks

Changes in execution time

Something very expected in environments like Node.js is that when modifying code in the development it is not necessary to stop the execution of the server and re-execute it.

For this there is a package called nodemon which monitors our project Node.js and restarts the server when there is a change in the files of it.

We can install it with npm install -save-dev nodemon and then add it in the package.json file under the scripts item:

package.json

1

2

“scripts”:{

“start”:“nodemon server.js”

Now to run the server, npm start is enough and the server will be lifted, which will automatically restart itself when changes are made to the code.

Logging with Morgan

To log in our application Node.js we will use Morgan.

We can install it with npm install -save-dev morgan. And to use it from the app you only need to request the package.

package.json

1

2

3

constmorgan=require(‘morgan’);

app.use(morgan(‘dev’));

To prove it, it is enough to make a request to ‘/ products’ and we can see an example of that order. In this test we observe in the console the logging of our requests and the obtaining of data from the database.

logging

1

2

3

4

GET/products/5b46238e860a4d2cd8072f9a40414.746ms–50

GET/products/2006.214ms–745

Fromdatabase{_id:5b3ced5dca946c1068547b1f}

GET/products/5b3ced5dca946c1068547b1f2003.691ms–110

Good practices

Organize the files by features, not by roles (for example: products, orders, etc. and not by routers, models, etc).

Do not add logic in index.js files (for web applications).

Place the tests near the implementation.

Use a directory for the configuration (in this project it was not used).

Place your npm scripts in a scripts directory.

Nomenclature: routers in plural, model in singular and the test ‘modulo.test.js’

Comments

In my opinion Node.js is a powerful and fast tool for the creation of APIs.

It is easy to implement since it only takes a few minutes to publish an API (comparing it with a java application with spring boot), develop unit tests and its deployment in production servers.

On the other hand, the learning curve to reach the level of ‘expertise’ necessary to develop a medium-sized project is high. Since it uses completely new frameworks and / or libraries in what confers to software development.