REST-Server mit Node.js
Willemers Informatik-Ecke

Einrichtung des Projekts

Für das einfachere Routing wird Express eingesetzt. Also beginnen alle Aktivitäten mit der Einrichtung des Projekts.

mkdir rest
cd rest
npm init
npm install express

Der Server in seiner Grundstruktur

const { request, response } = require("express");
const express = require("express");
const PORT = 8080;
const HOST = "localhost";
const app = express();


app.use(express.json());

const KundenData = require("./KundenData");
const kundenData = new KundenData();

app.get("/rest/kunden", async function(request, response) {
    const kunden = await kundenData.getKunden();
    kunden.forEach( function(kunde) {
        kunde.href = "rest/kunden/"+kunde.id;
    });
    response.status(200).send(kunden);
});

app.get("/rest/kunden/:id", async function(request, response) {
    const id = parseInt(request.params.id);
    const kunde = kundenData.getKunde(id);
    if (kunde) {
      response.status(200).send(kunde);
    } else {
      response.status(404).send();
    }

});

app.post("/rest/kunden", async function(request, response) {
    const kunde = request.body;
    const id = await kundenData.addKunde(kunde);
    kunde.href = "/rest/kunden/"+id;
    response.status(200).location("/rest/kunden/"+id).send(kunde);
});

app.put("/rest/kunden/:id", async function(request, response) {
    const id = parseInt(request.params.id);
    const kunde = request.body;
    await kundenData.updateKunde(id, kunde);
    response.status(200).send();
});

app.delete("/rest/kunden/:id", async function(request, response) {
    const id = parseInt(request.params.id);
    await kundenData.deleteKunde(id);
    response.status(200).send();
});

const server = app.listen(PORT, function() {
    console.log("Server running http://"+HOST+":"+PORT);
});

Die Kundendaten im Hauptspeicher

Die Kundendaten werden in der ersten Form nur in einer Map und damit im Hauptspeicher abgelegt und gehen damit bei Neustart des Servers verloren.

Es wird eine Klasse KundenData angelegt. Mit addKunde, updateKunde, deleteKunde und den Get-Funktionen werden alle Aktivitäten realisiert, die für einen Kunden infrage kommen.

class KundenData {
    constructor() {
        this.kundenMap = new Map();
        this.counter = 0;
    }
  
    async addKunde(kunde) {
      console.log("addKunde");
        this.counter++;
        kunde.id = this.counter;
        this.kundenMap.set(this.counter, kunde);
        return this.counter;
    }
    
    async getKunde(id) {
      return this.kundenMap.get(id);
    }
  
    async updateKunde(id, kunde) {
      kunde.id = id;
      this.kundenMap.set(id, kunde);
    }
  
    async deleteKunde(id) {
      this.kundenMap.delete(id);
    }
  
    async getKunden() {
      return Array.from(this.kundenMap.values());
    }
  };
  
  module.exports = KundenData

Die Anfragen per Curl

Mit dem folgenden Curl-Befehl wird ein POST an den Server geschickt. Im Gepäck ist die JSON-Darstellung eines Kunden. Als Ergebnis liefert der Server die URL, unter der der Kunde zugreifbar ist.

curl -v --header "Content-Type: application/json"  --request POST \
    --data '{"name":"Anton", "adresse":"Holzweg", "tel":"5014"}' \
    http://localhost:8080/rest/kunden
Mit einem GET auf die API wird eine Liste aller Kunden ausgegeben.
curl -v http://localhost:8080/rest/kunden
Der Befehl PUT wird einen bestimmten Kunden (hier Kunde Nummer 2) mit neuen Daten versehen.

curl -v --header "Content-Type: application/json"  --request PUT \\
   --data '{"name":"Anton", "adresse":"Holzweg", "tel":"5014"}' \\
   http://localhost:8080/rest/kunden/2
Zu guter Letzt kann ein Kunde auch wieder entfernt werden. Auch hier muss die ID in der URL angegeben werden.

curl -v --header "Content-Type: application/json"  --request DELETE  http://localhost:8080/rest/kunden/1

Ablegen der Daten in einer Datenbank

Die Daten eines REST-Servers sollten natürlich gern den nächsten Stromausfall oder den Neustart des Servers überleben. Dazu ist es sinnvoll, eine Datenbank zu hinterlegen.

Die REST-Struktur nimmt die HTTP-Befehle GET, POST, PUT und DELETE auf und übersetzt sie in die Datenbankaufrufe SELECT, INSERT, UPDATE und DELETE.

Das folgende Listing verwendet die Datenbank sqlite3 für die Speicherung der Daten. Ohne großen Aufwand könnte natürlich auch eine große Datenbank wie PostgreSQL verwendet werden.

const sqlite3 = require("sqlite3");
const db = new sqlite3.Database("kunden.db");

class KundenSQL {
    constructor() {
        db.run(
            `CREATE TABLE IF NOT EXISTS kunden (
              id INTEGER PRIMARY KEY AUTOINCREMENT,
              name TEXT,
              adresse TEXT,
              email TEXT
             )`
        );
    }

    async addKunde(kunde) {
        console.log("addKunde");
        return new Promise( function(resolve, reject) {
            db.run(
                "INSERT INTO kunden (name, adresse, email) VALUES (?, ?, ?)",
                [kunde.name, kunde.adresse, kunde.email],
                function () {
                    resolve(this.lastID);
                }
            );
        });
    }

    async deleteKunde(id) {
        return new Promise(function(resolve, reject) {
            db.run("DELETE FROM kunden WHERE id = ?", [id], function(error, row) {
                if (error) {
                    reject(error);
                } else {
                    resolve(row);
                }
            });
        });
    }

    async getKunde(id) {
        return new Promise( function(resolve, reject) { 
            db.get("SELECT * FROM kunden WHERE id = ?", [id], function(error, row) {
                if (error) {
                     reject(error);
                } else {
                     resolve(row);
                }
            });
        });
    }

    async getKunden() {
        return new Promise(function(resolve, reject) {
            db.all("SELECT * FROM kunden", [], function(error, rows) {
                if (error) {
                    reject(error);
                } else {
                    resolve(rows);
                }
            });
        });
    }
};

module.exports = KundenSQL

Links