Movie Web Service

Express project

We gaan voor onze webapplicatie een web service bouwen aan de hand van node en express. We plaatsen uit gemak gewoon de code van onze api in de src folder van onze web applicatie zodat we toegang hebben tot alle bestanden van onze react applicatie. Zo kunnen we de types hergebruiken en moeten we deze dus niet dubbel definiëren.

In deze api folder voer je het volgende commando uit om de tsconfig.json file aan te maken.

tsc --init

door dit commando wordt de volgende tsconfig.json file aangemaakt

{
  "compilerOptions": {
    "target": "es5",                                
    "module": "commonjs",                           
    "allowJs": true,                             
    "strict": true,                                 
    "esModuleInterop": true,                        
    "skipLibCheck": true,                           
    "forceConsistentCasingInFileNames": true        
  }
}

vervolgens doe je npm init om een package.json file te genereren.

Voor onze kleine API moeten we de volgende libraries installeren

npm install --save express cors
npm install --save-dev @types/node @types/express
  • De Express library laat toe om eenoudig en declaratief een web service te bouwen in node.

  • De cors library laat toe een bepaalde security check voor je browser te deactiveren.

  • Omdat we typescript gebruiken moeten we @types/express en @types/node installeren als dev dependency zodat we de types kunnen gebruiken voor node en express

Voor we alle routes beginnen opstellen zetten we de simpele express applicatie op. We maken een index.ts bestand aan met de volgende inhoud.

import { Request, Response } from 'express';
import express from 'express';

const app = express();                           // A

var cors = require('cors')                       // B

app.use(cors())                                  // B
app.use(express.json());                         // C
app.use(express.urlencoded({ extended: true })); // C

app.listen(3001, async() => {                    // D        
    console.log('The application is listening on port 3001!');
})

export {};

De express applicatie wordt aangemaakt aan de hand van de express() functie (A).

(B) Je browser laat standaard nooit toe om web service calls te doen vanuit een javascript bestand naar een domein of poort dat verschilt van het domein of poort van waar het javascript bestand is gehosted.

Bv. als je javascript bestand gehosted is op http://localhost:3000/index.js kan je bijvoorbeeld geen API calls doen naar http://localhost:3001/ omdat de poort verschillend is. Je zal een error krijgen die sterk lijkt op:

om deze error te vermijden moeten we de cors package installeren. Deze laat toe om de Access-Control-Allow-Origin *header te zetten voor elke request zodat je browser dit wel toelaat.

var cors = require('cors');

app.use(cors());

(C) zorgt ervoor dat de body van http requests worden omgezet naar json objecten zodat die rechtstreeks kunnen aangesproken worden vanuit je code.

(D) Deze lijnen code zorgen ervoor dat er geluisterd wordt naar de poort 3001 en dat requests kunnen afgehandeld worden.

Data inlezen

We maken een bestand games.json aan die de begin dataset zal voorstellen voor onze API.

[
  {
    "id": 0,
    "name": "World of Warcraft",
    "releaseYear": 2004,
    "sales": 0
  },
  {
    "id": 1,
    "name": "Valheim",
    "releaseYear": 2021,
    "sales": 0
  },
  {
    "id": 2,
    "name": "Minecraft",
    "releaseYear": 2011,
    "sales": 0
  }
]

We kunnen deze dan eenvoudig inlezen door na het starten van onze express server dit bestand in te lezen:

import { promises as fs } from 'fs';
import { Game } from '../types';

...

app.listen(3001, async() => {
    games = JSON.parse(await fs.readFile('games.json', 'utf8')) as Game[];
    console.log('The application is listening on port 3001!');
});

We moeten dan nog wel een globale variabele games moeten voorzien. We gebruiken hier de types die we voor onze React applicatie hebben gemaakt.

let games: Game[] = [];

Routes

De eerste route die we gaan aanmaken is een GET route voor/games Deze route gaat de array van games teruggeven als json.

app.get('/games', (req:Request, res:Response) => {
    res.json(games);
});

We maken nu ook een route aan voor /games/:id zodat we ook een game kunnen opvragen met een id. Eerst halen we de id uit de parameters en zetten we deze om naar een getal aan de hand van de parseInt functie. We gebruiken de find methode op de games array om de juiste game te vinden. Als deze gevonden wordt geven we deze terug met de res.json() methode. Als deze niet gevonden wordt geven we een 404 Not Found terug.

app.get('/games/:id', (req:Request, res:Response) => {
    let id = parseInt(req.params.id);
    let game = games.find((g: Game) => g.id === id);
    if (game !== undefined) {
        res.json(game);
    } else {
        res.status(404).json();
    }
});

Om de mogelijkheid toe te staan om ook games toe te voegen maken we een POST methode voor /games om een game toe te voegen. We zoeken eerst de hoogste id in de games array, want deze kan de gebruiker van de API niet zelf bepalen. Daarna gebruiken we het Game object in de body en vervangen we de id van dit object. Vervolgens voegen we het object toe aan de array. Na het toevoegen geven we de volledige array terug mee als json zodat de client geen extra request moet doen om de aangepaste lijst terug te krijgen.

app.post('/games', (req:Request, res:Response) => {
    let newId : number = Math.max(...games.map((g: Game) => g.id!)) + 1;
    let game : Game = req.body as Game;
    game.id = newId;
    games.push(game);
    res.json(games);
});

Voor de eenvoudigheid gaan we er vanuit dat er geen foutieve data aanwezig kan zijn dus er is geen server side input validatie aanwezig.

De volgende route die we gaan aanbieden is er een die het mogelijk maakt om een Game aan te passen. Daarvoor moeten we een PUT op /games/:id aanmaken. We gebruiken hier weeral de id van de parameters die worden doorgeven. Vervolgens gebruiken we de map functie om de games array om te zetten naar een nieuwe array maar waar het element vervangen is.

app.put('/games/:id', (req: Request, res: Response) => {
    let id = parseInt(req.params.id);
    let game : Game = req.body as Game;

    games = games.map((g: Game) => (g.id === id) ? { ...g, ...game } : g);

    res.json(games);
});

We konden in plaats van de map functie ook gewoon een eenvoudige for loop schrijven maar dit is veel uitgebreider.

for (let i=0;i<games.length;i++) {
    if (games[i].id === id) {
        games[i] = {...games[i], ...game}
    }
}

Heel gelijkaardig kunnen we een POST methode maken voor /games/:id/sell om de sales counter te verhogen voor een game.

app.post('/games/:id/sell', (req: Request, res: Response) => {
    let id = parseInt(req.params.id);
    games = games.map((g: Game) => (g.id === id) ? { ...g, sales: g.sales+1 } : g);
    res.json(games);
});

Nu kunnen we de API opstarten aan de hand van

ts-node index.ts

Je kan via een tool zoals Postman nagaan of de routes werken zoals het hoort.

Last updated

Was this helpful?