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 ­čĹĘÔÇŹ­čÜÇ