Eigene API mit NodeJS und Express

Du willst dir eine eigene API für deine Dienste bauen? Wir machen es zusammen 👇

Eigene API mit NodeJS und Express
Photo by Chris Ried / Unsplash / Image

In der heutigen Zeit sind APIs das neue "Gold". Darüber lassen sich viele Dinge automatisieren, abrufen oder verändern. Sehr viele Dienste bieten hier APIs an. Manche verstecken Ihre APIs hinter Verifizierungen (Meta I look at you 🥴) oder sind direkt frei verfügbar. Möchte man nun eine eigene API für seine Dienste erstellen, steht man schnell vor der Frage, wie man es macht. In diesem Artikel möchte ich mit Dir eine kleine und simple API bauen.


Wenn du noch nicht weißt, was eine API ist, solltest du dir unbedingt mal meinen Artikel und APIs im Ganzen ansehen 👇

REST-APIs verstehen und nutzen
APIs: Allgegenwärtig und jeder nutzt sie , wenn auch in direkt. Aber wofür sind sie da und wie funktionieren sie? Wir klären auf 💡

Was ist NodeJS?

NodeJS ist vereinfacht gesagt JavaScript, was als Anwendung läuft und nicht nur auf Webseiten beschränkt ist. Dadurch ist es möglich Anwendungen oder ganze Server-Systeme auf Basis von JavaScript zu bauen. Das ist für versierte Webentwickler natürlich ein Segen und führt dazu, dass sie sich bedeutend wohler fühlen in Serverumgebungen als wenn sie C# oder PHP schreiben müssten. Zudem ist NodeJS plattformunabhängig.

NodeJS eignet sich aber auch für kleine Skripte, die man man "früher" in Powershell, Bash/Batch oder Python gemacht hat.

Ich für meinen Teil liebe NodeJS und bin sehr froh, dass es JavaScript mittlerweile geschafft hat, sich als Anwendungs- und Serversprache zu etablieren. Wenn du mehr über NodeJS erfahren möchtest, dann empfehle ich dir die Doku zu NodeJS.

Grundlegende Überlegungen 🤔

Zu Erst musst du dir darüber Gedanken machen, was du genau machen möchtest. In diesem Guide werden wir nur einen kleinen Anwendungsfall abdecken. Express stellt in dem Falle unseren Webserver zur Verfügung.

Wir nehmen erst einmal an, wir wollen einfach einen zufälligen Namen zurückgeben, von einer festen Liste. Um das zu erreichen, setzen wir erst einmal ein neues NodeJS-Projekt auf.

Unser NodeJS-Projekt

Damit du auch NodeJS-Projekte erstellen kannst, benötigst du natürlich NodeJS. Das sollte vorab installiert sein (ich empfehle die neuste Version, alternativ die letzte LTS [long term service]).

Ist alles soweit installiert, können wir einen neuen Folder irgendwo erstellen und den nachfolgenden Befehl ausführen:

npm init

NPM

NPM ist in dem Falle nichts anderes, als der Node Package Manager, mit dem man eigene Pakete erzeugen und zur Verfügung stellen kann, als auch andere Libraries installieren und nutzen kann. In dem Falle erzeugen wir ein neues Paket. Der Befehl führt dich dann durch das Setup durch:

Schaust du dir jetzt das Projekt an, siehst du, dass nur eine package.json-Datei erzeugt wurde. Sie ist der Dreh- und Angelpunkt für dein NodeJS-Projekt:

Express installieren und nutzen 🚂

Da es sich bei Express um eine Dritthersteller-Webserver-Bibliothek handelt, müssen wir diese nun installieren. Auch dafür bietet NPM einen Befehl an:

npm install express
{
  "name": "nodejs-express-simple-api",
  "version": "0.0.1",
  "description": "Simple NodeJS and Express REST-API",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Marco Franke",
  "license": "MIT",
  "dependencies": {
    "express": "^4.18.2"
  }
}

Das sind deine Projektabhängigkeiten. Diese werden, sofern du dein Projekt auch auf NPM veröffentlichen möchtest, auch installiert, wenn der Nutzer dein Projekt installiert (mit npm install). Es gibt auch devDependencies. Das sind Pakete die nur benötigt werden zur Entwicklungszeit, z.B. Linter oder andere Tools. Auch siehst du, dass es nun einen Order gibt node_modules. Hier werden alle deine installierten Pakete von NPM abgelegt. Dieser Ordner kann tendenziell groß sind und wird nicht mit allen anderen Projekten geteilt!

Was uns jetzt noch fehlt ist eine Datei, die ausgeführt werden soll. Im Setup von npm initwurde dir auch auch eine Datei vorgeschlagen, die als Einstiegspunkt dienen soll. In Standardfall ist das index.js. Diese Datei erzeugen wir nun und Öffnen das Projekt mit Visual Studio Code (nicht notwendig, aber ich liebe VS Code ❤️).

Wir schreiben deinen ersten Webserver

Wenn das Projekt nun geöffnet ist, dann schnappst du dir die index.jsund importierst zu erst einmal das Paket express:

const express = require('express');

Damit können wir nun auf alles von Express zugreifen und es nutzen. Nun instanziieren wir uns eine neue Instanz von Express und weisen dem Webserver einen Port zu (3000 ist so der Standard NodeJS-Port, per Konvention, kann aber beliebig verändert werden):

const app = express();
const port = 3000;

Nun fehlt uns eigentlich nur noch dass wir den Server starten und eine Route angeben, auf der der Server "lauscht" und Inhalte bereitstellt. Das können wir recht einfach machen mit:

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})

Nun haben wir einen Webserver, der auf den Port 3000hört und uns auf der Stammroute /den Inhalt Hello World!zurück liefert.

Den Webserver kannst du dann einfach starten mit dem Befehl:

node index.js
💡
In Visual Studio Code kannst du einfach F5 drücken und NodeJS als Projekt auswählen. Danach startet das Projekt automatisch und du kannst es sogar mit Breakpoints debuggen.

Surfst du nun deine Seite mit http://localhost:3000/ an, solltest du "Hello World" sehen.

Damit hast du jetzt deinen ersten Webserver geschrieben 🎉

Der Weg zur eigenen API 🛣️

Nun möchten wir aber eine REST-API bereitstellen, die uns random irgendwelche Namen zurückgibt.

app.get('/generate/names', (req, res) => {
    res.send('Hello Names!');
});

Als Rückgabe möchten wir JSON nutzen und keine Daten direkt an den Browser (als Website) bereitstellen. Dazu müssen wir nun Express beibringen, dass er uns bitte die Rückgabe als application/json geben soll. Dafür bietet Express auch eine Funktion an res.json(). Resist die Response die an den Browser geschickt wird. Der können wir einfach das Objekt übergeben und es wird als JSON an den Browser gesendet.

app.get('/generate/names', (req, res) => {

    // list of random names
    names = ["John", "Paul", "George", "Ringo"];
    
    // get random index
    let randomIndex = Math.floor(Math.random() * names.length);

    // return random name
    res.json({ randomName: names[randomIndex]});
});

Öffnen wir also nun http://localhost:3000/generate/namesbekommen wir unseren random Namen als JSON von unserer API:

Parameter an die API übergeben

Aktuell öffnen wir die API via Browser, also der HTTP-Methode GET.


Diese HTTP-Methoden habe ich schon in meinem Artikel über REST-APIs angesprochen. Falls du den Artikel noch nicht kennst, schau einfach mal rein 👇

REST-APIs verstehen und nutzen
APIs: Allgegenwärtig und jeder nutzt sie , wenn auch in direkt. Aber wofür sind sie da und wie funktionieren sie? Wir klären auf 💡

Möchten wir der API nun Parameter mitgeben, haben wir nur zwei Möglichkeiten:

  • Routen-Parameter
  • Query-String-Parameter

Beide Arten werden über die Adresse mit angegeben, werden jedoch anders verankert.

Routen-Parameter

Routen-Parameterwerden auch über die Adresszeile übergeben, werden aber nicht an die Adresse hinten angefügt, sondern sind integraler Bestandteil der Adresse selbst:

GET http://test.xyz/[parameter]/

Ändern wir nun unseren Code so ab, dass wir unterscheiden möchten zwischen männlichen und weiblichen Namen, könnten wir das darüber nun machen. Auch dafür bietet Express eine Lösung an, denn wir können den Requestan die API auslesen. Routen-Parameter werden bei app.get()im ersten Parameter mit
:[Parametername]angegeben:

app.get('/generate/names/:gender', (req, res) => {
    const gender = req.params.gender;

    // list of random names with gender
    const names = [
        { name: 'John', gender: 'male' },
        { name: 'Emma', gender: 'female' },
        { name: 'Michael', gender: 'male' },
        { name: 'Sophia', gender: 'female' },
        // Add more names with their genders here
    ];

    // get all names for a specific gender
    const genderNames = names.filter(name => name.gender == gender)
  
     // get random index of named for specific gender
    let randomIndex = Math.floor(Math.random() * genderNames.length);

    // return random name object
    res.json({ randomName: genderNames[randomIndex]});
});

Auf die Parameter können wir mit req.paramszugreifen und dann den benannten Parameter herausziehen. Konsequenterweise müssten wir noch prüfen, ob die Übergabe maleoder femaleist und, wenn nicht, mit einem Fehler aussteigen:

if(!gender.includes('male', 'female')){
    res.status(500).send("Gender must be `male` or `female`")
}

Surfen wir nun also http://localhost:3000/generate/names/malean, bekommen wir diese Rückgabe:

Query-String-Parameter

Query-String-Parameterwerden der Adresse abschließend angefügt. Vermutlich hast du auch schon mal so eine Adresse gesehen:

GET http://google.com/?q=searchTerm

Alles was nach dem ? steht, signalisiert dem Browser/Server, dass ab hier nun Parameter kommen. Diese Parameter bestehen aus Key/Values, die mit = angegeben werden. Mehrere Parameter selbst werden mit &separiert.

Möchten wir nun eine Suche einbauen, die uns nur männliche oder weibliche Namen zurückgibt, die ein bestimmtes Zeichen enthalten, können wir das über diese Query-String-Parameter tun. Für Suchen hat sich der Parametername qals Standard eingebürgert. Den Code ändern wir wie folgt ab:

app.get('/generate/names/:gender', (req, res) => {
    // get the gender param from the url
    const gender = req.params.gender;

    // get the search term from the query string
    const searchTerm = req.query.q;

    // check if gender is male or female, if not exit with 500 error
    if(!gender.includes('male', 'female')){
        res.status(500).send("Gender must be `male` or `female`")
    }

    // list of random names with gender
    const names = [
        { name: 'John', gender: 'male' },
        { name: 'Emma', gender: 'female' },
        { name: 'Michael', gender: 'male' },
        { name: 'Sophia', gender: 'female' },
        // Add more names with their genders here
    ];
    
    const genderNames = names.filter(name => name.gender == gender);
    const searchTermNames = genderNames.filter(name => name.name.includes(searchTerm));
     // get random index
    let randomIndex = Math.floor(Math.random() * searchTermNames.length);

    // return random name
    res.json({ randomName: searchTermNames[randomIndex]});
});

Wie du siehst, können wir die Query-String-Parameter mit req.params.qabrufen. Probieren wir es doch mal aus, in dem wir folgende URL aufrufen: http://localhost:3000/generate/names/male?q=Mi

Und tatsächlich, wir bekommen nun alle männlichen Namen die "Mi" enthalten:

Komplette Daten an die API übergeben 💾

Bis jetzt haben wir nur Daten via GETan die API übergeben. Wenn wir aber nun Daten erzeugen lassen wollen (z.B. einen Blog-Artikel in einem System), müssen wir ganze Daten übergeben, die zu groß für die Adresszeile wären. Hierfür nutzen wir POST um Daten als JSONan die API zu übertragen. Das geht nun dann nicht mehr mit dem Browser, dafür müssen wir dann cURL, Postman oder andere API-Tools verwenden.

Endpunkt vorbereiten

Damit wir in unserer API nun Daten empfangen können, müssen wir diese entsprechend vorbereiten. Nehmen wir einfach als Beispiel an, wir möchten die Liste der Namen und des Geschlechtes, an die API übergeben und die soll einfach einen wahllosen Namen für ein Geschlecht zurückgeben.

Hierfür müssen wir unsere Route von app.get()auf app.post()ändern (genau so würde es sich auch mit PUT, PATCH, DELETEusw. verhalten):

app.post('/generate/names/:gender', (req, res) => {
    // get the gender param from the url
    const gender = req.params.gender;

    // get the search term from the query string
    const searchTerm = req.query.q;
    

    // check if gender is male or female, if not exit with 500 error
    if(!gender.includes('male', 'female')){
        res.status(500).send("Gender must be `male` or `female`")
    }

    // list of random names with gender
    const names = [
        { name: 'John', gender: 'male' },
        { name: 'Emma', gender: 'female' },
        { name: 'Michael', gender: 'male' },
        { name: 'Sophia', gender: 'female' },
        // Add more names with their genders here
    ];
    
    const genderNames = names.filter(name => name.gender == gender);
    const searchTermNames = genderNames.filter(name => name.name.includes(searchTerm));
     // get random index
    let randomIndex = Math.floor(Math.random() * searchTermNames.length);

    // return random name
    res.json({ randomName: searchTermNames[randomIndex]});
});

Und den bodyParservon Express importieren, nutzen und konfigurieren:

// put this underneath "const express = require('express');"
const bodyParser = require('body-parser')

// this should be right under declaring express and the port
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

Den Body des Requests können wir dann mit req.body abfragen. Der geänderte Code würde wie folgt aussehen:

app.post('/generate/names/:gender', (req, res) => {
    // get the gender param from the url
    const gender = req.params.gender;

    // get the search term from the query string
    const searchTerm = req.query.q;


    // check if gender is male or female, if not exit with 500 error
    if(!gender.includes('male', 'female')){
        res.status(500).send("Gender must be `male` or `female`")
    }

    // get the body of the request with all names and genders
    const names = req.body;
    
    const genderNames = names.filter(name => name.gender == gender);
    const searchTermNames = genderNames.filter(name => name.name.includes(searchTerm));
     // get random index
    let randomIndex = Math.floor(Math.random() * searchTermNames.length);

    // return random name
    res.json({ randomName: searchTermNames[randomIndex]});
});

Mit const names = req.body;weisen wir nun unserer Variable nameden Body unseres Requests zu. Heißt aber auch, dass wir das übergeben müssen und vor allem auch prüfen müssen, dass der Body unserer Struktur entspricht, da sonst der Code knallen würde mit Fehlermeldungen. Den Part überlasse ich aber gerne dir 😉

Rufen wir nun unsere API so auf und schicken ihr den erwarteten Body, dann sollten wir auch unser Ergebnis bekommen, nämlich einen Namen zu einem definiertem Geschlecht mit "Mi" im Namen:

Und siehe da, es klappt 🎉

Mit den Grundlagen kannst du nun deine eigenen APIs schreiben und Dienste der Welt zur Verfügung stellen. Willkommen in der Welt der APIs 👨‍🚀