Generating the server architecture

mkdir back
cd back
npm init -y
npm install express
touch server.js

Express.js is a mimimalist framework for node.js. It allows creating a backend application that can be used, for example, by a React.js app.

Building the server with node and express.js

The server.js file

Copy the following code into your server.js file:

const express = require('express');
const app = express();
const port = process.env.PORT || 8000;

app.get('/', (req, res) => {
    res.set('Content-Type', 'text/html');
    res.send('Hello world !!');
});

app.listen(port, () => {
    console.log('Server app listening on port ' + port);
});

Start the server with node: node server.js. Then in your web browser, type the following command:

http://localhost:8000

The message “Hello world!!” should appear. Your server is working.

Try the following URL :

http://localhost:8000/xx

The message “cannot GET /xx” appears, indicating that your server is not able to reply to this request

HTTP verbs

The HTTP protocol defines the requests a server is able to reply to. We only need the following so-called HTTP verbs:

  • POST : to insert data
  • PUT/PATCH : to modify/replace data.
  • GET : to get data.
  • DELETE : to delete data.

An HTTP verb always comes with an action telling what the server has to GET, PUT, …

A command like http://localhost:8000 corresponds to a GET / request. ‘/’ is the action coming with the verb.

Refining the server

  1. Stop the server (ctrl-C)

  2. Type the following command npm install cors morgan nodemon.

  3. Create a router.js file

  4. Modify the server.js file as follows:

    const express = require("express");
    const router = require('./router');
    
    const cors = require('cors');
    const morgan = require('morgan');
    const bodyParser = require("body-parser");
    const app = express();
    
    const port = process.env.PORT || 8000;
    
    app.use(morgan('combined')); 
    app.use(cors()); 
    app.use(bodyParser.json()); 
    app.use(bodyParser.urlencoded({
            extended: true
        }));
    app.use(router); // Requests processing will be defined in the file router
    app.listen(port, () => console.log('Server app listening on port ' + port));
  5. Modify the scripts entry in file package.json as follows:

        "scripts": {
            "start": "nodemon server.js",
        }

    Type npm start. This launch the command nodemon server.js. Nodemon is a utility processing automatically restart when your code changes.

  6. Before restarting the server, you have to define the router. Copy the following code into the router.js file:

    const express = require("express");
    const router = express.Router();
    
    module.exports = router;
  7. Restart your server. Note that the request http://localhost:8000 get an error message.

  8. Type your first route in the file router.js:

    router
       .get("/", (req, res) => {
           res.json("Hello world!!");
       });

    You can now define successively a list of routes. The first route corresponding to the request is executed by the server. Note that, if more than one route correspond to a request, only the first one will be executed on the server.

  9. Note that http://localhost:8000/xx always get an error message. Add the following entry to get an appropriate HTTP status:

    router
        .use((req, res) => {
            res.status(400);
            res.json({
                error: "Bad request"
            });
        });

    The term use means that this route is executed whatever the HTTP verb. So, this route must always be the last one : if no route corresponds to a request, the server sends a Bad request error message.

Note that HTTP requests can be defined by using a REST API

Managing data with SQLite

  1. Install the SQLite database on your server with the following command:

    npm install sqlite3

    The npm sqlite3 documentation can be found here

  2. Create a sql script to generate tables and, eventually, insert some data, as shown in the example below.

    DROP TABLE IF EXISTS persons;
    
    CREATE TABLE persons (
       id INTEGER PRIMARY KEY,
       name TEXT
    );
    
    INSERT INTO persons VALUES('Jane'),('John'),('Jack');
  3. Suppose this code is written in a file db/persons.sql. Type the following command :

    sqlite3 -init persons.sql persons .exit

    A file persons appears. This is your SQLite database!!

  4. In the router.js file, establish the connexion to the database:

    const sqlite3 = require('sqlite3').verbose();
    const db = new sqlite3.Database('db/persons');
  5. To get persons from the table persons, type the following route:

    .get('/persons',
            (req, res) => {
                db.all(
                    "select * from persons",
                    (err, rows) => res.json(rows)
                );
            })
  6. To get a unique person:

    .get('/persons/:id',
        (req, res) => {
            db.get(
                "select * from persons where id=?",
                req.params.id,
                (err, row) => {
                    res.json(row)
                }
            );
        })
  7. To insert a new person into the table persons:

    .post('/persons',
        (req, res) => {
            db.run("insert into persons(name) values(?)",[name]);
            res.redirect(303, '/persons');
        })
  8. To update a person identified by its id:

    .patch('/persons/:id',
        (req, res) => {
            db.run("update persons set name=? where id=?",[req.body,req.params.id]);
            res.status(200).json(req.body);
    })