# Express Beers

Ce tutoriel est basé sur le tutoriel de LostInBrittany Express-Beers

# Présentation

# Les objectifs

Ce tutoriel va vous permettre d'apprendre à construire des APIs en Javascript rapidement et facilement. Vous utiliserez le framework ExpressJS, avec des touches de base de données NoSQL (avec MongoDB).

# Organistion du tutoriel

Le tutoriel est divisé en 5 étapes qui sont les suivantes :

  1. Hello world
  2. Routage basique
  3. JSON beers
  4. Lancer la webapp
  5. Mongo beers

# Step 01 - Hello World !

# Initialize npm

First, create the directory for the project express-beers

mkdir express-beers
cd express-beers

Let's begin by initializing npm

npm init

Now we add ExpressJS to your dependencies by installing it with the --save flag

npm install --save express

A suitable package.json file is generated including the ExpressJS dependency:

{
  "name": "express-beers",
  "version": "1.0.0",
  "description": "first ExpressJS API",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "@Steffy29",
  "license": "MIT",
  "dependencies": {
    "express": "^4.17.1"
  }
}

# Create a minimal Hello World express

Now we are going to create our first API server in the index.js file.

Let's begin by requiring the express module, who has all the routing defining functions we need for our API server, and using it to create an app object.

var express = require('express');
var app = express();

On this app object we can now define routes. For each route we define the HTTP method and the URL path it answers to, and the callback function the application will call when a request with matching method and matching path is received.

app.get('/', function (req, res) {
    console.log('Received request from', req.ip)
    res.send('Hello World !');
});

So here we are saying that all the GET requests to the base URL of our server (path '/') are answered by this route, and the answer is given by the callback function: we send "Hello World !" back to the client.

Now we create a web server serving our application on the port 3000, and login the starting of the server.

var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('Listening at http://%s:%s', host, port);
});

# Let's test it

Let's start the API server:

node index.js
Listening at http://:::3000

And now go to 127.0.0.1:3000 in your browser and see the nice response.

Hello World!

Meanwhile in the console you should get something like:

node index.js
Listening at http://:::3000
Received request from 127.0.0.1

# Step 02 - Basic routing

# Getting the image files

Now we need to copy the img folder from the root of this tutorial into the app folder, as express will look for them there.

When done, you can proceed.

# Defining the routes

We are going to define the routes we need for our API application.

As we want to build a backend for front Beers applications, we need to define:

  • GET /beers: the list of beers, with name, description, alcohol content and image URL for each beers
  • GET /beer/<beerId>: to get the detail of a beer

And we also want to serve as static files all the content of the public folder, and all the content of the img folder at the /img path.

Let's begin by defining the routes on Express:

app.get('/beers', function (req, res) {
    console.log('Received request for beers from', req.ip)
    res.send('Hello beers');
});
app.get('/beer/:beerId', function (req, res) {
    console.log('Received request for '+req.params['beerId']+' from', req.ip)
    res.send('Hello beer '+req.params['beerId']);
});

And the two static files folders:

app.use('/img', express.static('img'));
app.use(express.static('public'));

# Step 03 - JSON beers

# Getting the beers files

Now we need to copy the beers folder from the root of this tutorial into the app folder, as express will look for them there.

When done, you can proceed.

# Reading beer list from JSON file

With node you read a JSON file simply by using require:

var myData = require('./data.json');

We are going to use this technique to get the beer list info needed in the /beers route:

var beerList = require('./beers/beers.json');
console.log("Beers", beerList)
var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('Listening at http://%s:%s', host, port);
});

So we have a beerList variable that contains the info needed for the /beers route. Now we can modify the route to send back the beer list:

app.get('/beers', function (req, res) {
    console.log('Received request for beers from', req.ip)
    res.json(beerList);
});

Beer list

# Getting beer details

Now we want to be able to serve the beer details using the /beer/:beerId route. The easiest way would be to load the right JSON file at each request and send it back to the client:

app.get('/beer/:beerId', function(req, res) {
    console.log('Received request for ' + req.params['beerId'] + ' from', req.ip);
    var beerDetails = require('./beers/' + req.params['beerId'] + '.json');
    res.json(beerDetails);
});

Beer details

# Step 04 - Serving the webapp

# Getting the webapp files

Now we need to copy the Angular Beers or the Polymer Beers web application into the app/public folder, to get it served from the static express route.

For Angular Beers, you can simply get the step 09 of Angular Beers and copy it into public. Then you go to localhost:3000/index.html to see the main page of your app.

For the Polymer version copy the step 8 of Polymer Beers into public/app and then copy the bower_components folder into public. Then you go to localhost:3000/app/index.html to see the main page of your app.

In both cases, in order to be sure you're calling the Express server, delete the data folder of the webapp, so you don't feel tempted to simply read your JSON files...

# Modify the webapp to call the express server

# Angular Beers

Now we need to modify the controllers of Angular Beers to call our new server instead of simply requesting the JSON files.

beers.service.ts

import {HttpClient} from "@angular/common/http";
@Injectable()
export class BeerService {
    constructor(private httpClient : HttpClient) {
    }
    getBaseUrl() {
        return 'http://localhost:3000/';
    }
    getBeers(): Observable<any> {
        return this.httpClient.get<Beer>(this.getBaseUrl() + 'beers');
    }
    getBeer(beerId: String): Observable<any> {
        return this.httpClient.get<Beer>(this.getBaseUrl() + 'beer/' + beerId);
    }
}

# Polymer Beers

In beer-list element, modify the iron-ajaxto to call our new server using the routes defined for express in index.js instead of simply requesting the JSON files:

    <iron-ajax
      auto
      url="/beers"
      method='get'
      params='{}'
      handle-as="json"
      on-response="gotBeers"
      debounce-duration="300"></iron-ajax>

Same thing in beer-details:

    getUrl: function(id) {
      return "/beer/"+id;
    },

# Where are my pics ?

We still have a problem with the images' path. We could correct it by modifying the JSON... but let's do it in the express way, by adding a route specifically for that:

app.use('/beers/img', express.static('img'));

And now we can see our work on the browser:

Beer list

# Step 05 - Mongo Beers

Let's say you already have your beers in a MongoDB database. Now we are going to replace our local JSON files with calls to MongoDB.

TIP

In order to do this step you need to have your beer data in a MongoDB database. How to do it is outside the scope of this tutorial, but if you only want to do a quicktest, you could:

  • Install MongoDB (see http://mongodb.com/)
  • Start the MongoDB daemon (usually with the command mongo)
  • Create new collection beers on database test
$ mongo
> db
test
> db.createCollection("beers")
{ "ok" : 1 }
> exit
bye
  • Use mongoimport command line tool to import the detailed JSON datafiles
mongoimport --db test --collection beers beers/AffligemBlond.json
mongoimport --db test --collection beers beers/AffligemDubbel.json
...

# Adding the MongoDB driver dependency

For this step, we are going to use the official MongoDB NodeJS driver.

Use npm to install the MongoDB Node.js driver:

npm install --save mongodb

And we get it added to the package.json:

{
  "name": "express-beers",
  "version": "1.0.0",
  "description": "first ExpressJS API",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "@Steffy29",
  "license": "MIT",
  "dependencies": {
    "express": "^4.17.1",
    "mongodb": "^3.2.6"
  }
}

# Connecting to Mongo

Now in our index.js we are going to get a MongoClient variable:

var MongoClient = require('mongodb').MongoClient;

Connect using the MongoClient to your running mongod instance by specifying the MongoDB URI. For example, the following code connects to a MongoDB instance that runs on the localhost interface on port 27017 and switch to the beers database.

var url = 'mongodb://localhost:27017/test';
MongoClient.connect(url, function(err, client) {
    console.log("Connected correctly to MongoDB server.");
    client.close();
});

# Ask for the beer list

Let's add a /beers route, with a callback that connects to Mongo and recovers the beer list. We are heavily using the power of async/await to make asynchronous code simple:

app.get("/beers", async function(req, res) {
  console.log("Received request for beers from", req.ip);
  let client;
  try {
    client = await MongoClient.connect(url);
    const db = client.db(dbName);
    var beerList = await db
      .collection("beers")
      .find()
      .toArray();
    res.json(beerList);
  } catch (err) {
    console.log(err.stack);
  }
  client.close();
});

Beer list

# And about the beer details ?

Like for the beer list, we begin by adding a /beer/:beerId route with an async callback. In the callback we connect to Mongo and find the beer corresponding to beerId:

app.get("/beer/:beerId", async function(req, res) {
  console.log(`Received request for ${req.params.beerId} from ${req.ip}`);
  let client;
  try {
    client = await MongoClient.connect(url);
    const db = client.db(dbName);
    let beerId = req.params.beerId;
    let beerList = await db
      .collection("beers")
      .find({ id: beerId })
      .toArray();
    let beer = beerList[0];
    console.log(beer);
    res.json(beer);
  } catch (err) {
    console.log(err.stack);
  }
  client.close();
});

# Solutions

# Step 01

index.js file

var express = require('express');
var app = express();
app.get('/', function (req, res) {
  console.log('Received request from', req.ip)
  res.send('Hello World!');
});
var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Listening at http://%s:%s', host, port);
});

# Step 02

index.js file

var express = require('express');
var app = express();
app.get('/beers', function (req, res) {
  console.log('Received request for beers from', req.ip)
  res.send('Hello beers');
});
app.get('/beer/:beerId', function (req, res) {
  console.log('Received request for '+req.params['beerId']+' from', req.ip)
  res.send('Hello beer '+req.params['beerId']);
});
app.use('/img', express.static('img'));
app.use(express.static('public'));
var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Listening at http://%s:%s', host, port);
});

# Step 03

index.js file

var express = require('express');
var app = express();
app.get('/beers', function (req, res) {
  console.log('Received request for beers from', req.ip)
  res.json(beerList);
});
app.get('/beer/:beerId', function (req, res) {
  console.log('Received request for '+req.param('beerId')+' from', req.ip)
  var beerDetails = require('./beers/'+req.param('beerId')+'.json');
  res.json(beerDetails);
});
app.use('/img', express.static('img'));
app.use(express.static('public'));
var beerList = require('./beers/beers.json');
console.log("Beers", beerList)
var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Listening at http://%s:%s', host, port);
});

# Step 04

index.js file

var express = require('express');
var app = express();
app.get('/beers', function (req, res) {
  console.log('Received request for beers from', req.ip)
  res.json(beerList);
});
app.get('/beer/:beerId', function (req, res) {
  console.log('Received request for ' + req.params['beerId'] + ' from', req.ip)
  var beerDetails = require('./beers/' + req.params['beerId'] + '.json');
  res.json(beerDetails);
});
app.use('/beers/img', express.static('img'));
app.use('/img', express.static('img'));
app.use(express.static('public'));
var beerList = require('./beers/beers.json');
console.log("Beers loaded")
var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Listening at http://%s:%s', host, port);
});

# Step 05

index.js file

var express = require('express');
var app = express();
var assert = require('assert');
var MongoClient = require('mongodb').MongoClient;
app.get('/beers', async function (req, res) {
  console.log('Received request for beers from', req.ip);
  let client;
  try {
    client = await MongoClient.connect(url);
    const db = client.db(dbName);
    var beerList = await db.collection('beers').find().toArray();
    res.json(beerList);
  } catch(err) {
    console.log(err.stack);
  }
  client.close();
});
app.get('/beer/:beerId', async function (req, res) {
  console.log(`Received request for ${req.params.beerId} from ${req.ip}`);
  let client;
  try {
    client = await MongoClient.connect(url);
    const db = client.db(dbName);
    let beerId = req.params.beerId;
    let beerList = await db.collection('beers').find({id: beerId}).toArray();
    let beer = beerList[0];
    console.log(beer);
    res.json(beer);
  } catch(err) {
    console.log(err.stack);
  }
  client.close();
});
app.use('/beers/img', express.static('img'));
app.use('/img', express.static('img'));
app.use(express.static('public'));
var url = 'mongodb://localhost:27017';
var dbName = 'test'
var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Listening at http://%s:%s', host, port);
});