Serverless GraphQL API in Node mit Express und Netlify erstellen

Avatar of Matthew Ström
Matthew Ström on

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Ich wollte schon immer eine API erstellen, war aber eingeschüchtert von der Komplexität. Ich habe viele Tutorials gelesen, die mit „installiere zuerst diese Bibliothek und diese Bibliothek und diese Bibliothek“ beginnen, ohne zu erklären, warum das wichtig ist. Ich bin in solchen Dingen eher ein Luddit.

Nun, ich habe kürzlich die Ärmel hochgekrempelt und mich an die Arbeit gemacht. Ich wollte eine einfache Lese-API erstellen und bereitstellen, und verdammt noch mal, ich werde mich nicht von einschüchternden Abhängigkeitslisten und schicken, hochmodernen Diensten aufhalten lassen¹.

Was ich entdeckt habe, ist, dass sich hinter vielen der vorhandenen Tutorials und Projekte ein kleiner, leicht verständlicher Satz von Werkzeugen und Techniken verbirgt. In weniger als einer Stunde und mit nur 30 Codezeilen kann meiner Meinung nach jeder seine eigene Lese-API schreiben und bereitstellen. Du musst kein erfahrener Full-Stack-Entwickler sein – ein grundlegendes Verständnis von JavaScript und etwas Erfahrung mit npm sind alles, was du brauchst.

Am Ende dieses Artikels wirst du deine eigene API bereitstellen können, ohne den Aufwand der Serververwaltung. Ich werde jede Abhängigkeit auflisten und erklären, warum wir sie einbeziehen. Ich gebe dir auch eine Einführung in einige der neueren Konzepte und stelle Links zu Ressourcen für vertiefende Informationen bereit.

Legen wir los!

Eine Übersicht über API-Konzepte

Es gibt ein paar gängige Wege, mit APIs zu arbeiten. Aber lass uns zunächst (ganz kurz) erklären, worum es bei einer API geht: Daten lesen und aktualisieren.

In den letzten 20 Jahren haben sich einige Standardmethoden zum Erstellen von APIs herausgebildet. REST (kurz für **RE**presentational **S**tate **T**ransfer) ist eine der häufigsten. Um eine REST-API zu nutzen, rufst du einen Server über eine URL auf – sagen wir api.example.com/rest/books – und erwartest eine Liste von Büchern in einem Format wie JSON oder XML. Um ein einzelnes Buch zu erhalten, würden wir uns mit einer ähnlichen, zweckbestimmten URL wieder an den Server wenden – z. B. api.example.com/rest/books/123 – und die Daten für Buch Nr. 123 erwarten. Das Hinzufügen eines neuen Buches oder das Aktualisieren der Daten eines bestimmten Buches bedeutet weitere Anfragen an den Server unter ähnlichen, zweckbestimmten URLs.

Das ist die Grundidee zweier Konzepte, die wir uns hier ansehen werden: **GraphQL** und **Serverless**.

GraphQL

Anwendungen, die viele Daten abrufen und aktualisieren, machen viele API-Aufrufe. Komplexe Software wie Twitter könnte Hunderte von Aufrufen tätigen, um die Daten für eine einzelne Seite abzurufen. Das Sammeln der richtigen Daten aus einer Handvoll URLs und deren Formatierung kann eine echte Kopfzerbrechen sein. Im Jahr 2012 begannen Facebook-Entwickler, nach neuen Wegen zu suchen, um Daten effizienter abzurufen und zu aktualisieren.

Ihre wichtigste Erkenntnis war, dass Daten in komplexen Anwendungen größtenteils *Beziehungen* zu anderen Daten haben. Ein Benutzer hat Follower, die selbst Benutzer sind, die jeweils ihre eigenen Follower haben, und diese Follower haben Tweets, die Antworten von anderen Benutzern haben. Das Zeichnen der Beziehungen zwischen Daten ergibt einen Graphen, und dieser Graph kann einem Server helfen, viele clevere Arbeiten bei der Formatierung und dem Senden (oder Aktualisieren) von Daten zu leisten und Front-End-Entwicklern Zeit und Frustration zu ersparen. Graph Query Language, auch bekannt als GraphQL, war geboren.

GraphQL unterscheidet sich vom REST-API-Ansatz in der Verwendung von URLs und Abfragen. Um eine Liste von Büchern aus unserer API mit GraphQL abzurufen, müssen wir keine bestimmte URL aufrufen (wie unser api.example.com/graphql/books example). Stattdessen rufen wir die API auf der obersten Ebene auf – das wäre in unserem Beispiel api.example.com/graphql – und teilen ihr mit, welche Art von Informationen wir mit einem JSON-Objekt zurückhaben möchten.

{
  books {
    id
    title
    author
  }
}

Der Server sieht diese Anfrage, formatiert unsere Daten und sendet sie in einem weiteren JSON-Objekt zurück.

{
  "books" : [
    {
      "id" : 123
      "title" : "The Greatest CSS Tricks Vol. I"
      "author" : "Chris Coyier"
    }, {
      // ...
    }
  ]
}

Sebastian Scholl vergleicht GraphQL mit REST anhand einer fiktiven Cocktailparty, die den Unterschied sehr deutlich macht. Die Quintessenz: GraphQL ermöglicht es uns, genau die Daten anzufordern, die wir wollen, während REST uns einen Dump von allem an der URL gibt.

Konzept 2: Serverless

Immer wenn ich das Wort „serverless“ sehe, denke ich an das berühmte Sticker von Chris Watterston.

Ähnlich gibt es keine wirklich „serverless“ Anwendung. Chris Coyier fasst es schön in seinem „Serverless“ Beitrag zusammen.

Was serverless bedeuten soll, scheint mir, ist eine neue Art, Server zu verwalten und zu bezahlen. Man kauft keine einzelnen Server. Man verwaltet sie nicht. Man skaliert sie nicht. Man balanciert sie nicht. Man ist nicht wirklich für sie verantwortlich. Man bezahlt nur für das, was man nutzt.

Der serverless Ansatz erleichtert die Erstellung und Bereitstellung von Back-End-Anwendungen. Er ist besonders einfach für Leute wie mich, die keinen Hintergrund in der Back-End-Entwicklung haben. Anstatt meine Zeit damit zu verbringen, zu lernen, wie man einen Server bereitstellt und wartet, lagere ich die harte Arbeit oft an jemand anderen (oder vielleicht sogar an etwas) ab.

Es lohnt sich, den CSS-Tricks Leitfaden zu allen Dingen rund um serverless anzusehen. Auf der Ideenseite gibt es sogar einen Link zu einem Tutorial über die Erstellung einer serverless API!

Auswahl unserer Werkzeuge

Wenn du den serverless Leitfaden durchblätterst, wirst du sehen, dass es keinen Mangel an Werkzeugen und Ressourcen gibt, die uns beim Erstellen einer API helfen. Aber genau welche wir verwenden, erfordert einige anfängliche Gedanken und Planung. Ich werde zwei spezifische Werkzeuge behandeln, die wir für unsere Lese-API verwenden werden.

Werkzeug 1: NodeJS und Express

Auch hier habe ich nicht viel Erfahrung mit der Back-End-Webentwicklung. Aber eines der wenigen Dinge, auf die ich gestoßen bin, ist Node.js. Viele von euch sind wahrscheinlich damit vertraut und wissen, was es tut, aber es ist im Grunde JavaScript, das auf einem Server läuft, anstatt in einem Webbrowser. Node.js ist perfekt für jemanden, der von der Front-End-Entwicklung kommt, da wir direkt in JavaScript arbeiten können – mit allen Eigenheiten – ohne auf eine Back-End-Sprache zurückgreifen zu müssen.

Express ist eines der beliebtesten Frameworks für Node.js. Bevor React der König war (How Do You Do, Fellow Kids?), war Express das Mittel der Wahl für die Erstellung von Webanwendungen. Es bietet alle möglichen praktischen Funktionen wie Routing, Templating und Fehlerbehandlung.

Ich bin ehrlich: Frameworks wie Express intimidieren mich. Aber für eine einfache API ist Express extrem einfach zu benutzen und zu verstehen. Es gibt einen offiziellen GraphQL-Helfer für Express und eine Plug-and-Play-Bibliothek für die Erstellung einer serverless Anwendung namens serverless-http. Nett, oder?!

Werkzeug 2: Netlify Functions

Die Idee, eine Anwendung ohne Serververwaltung zu betreiben, klingt zu gut, um wahr zu sein. Aber schau dir das an: Du kannst diese moderne Zauberei nicht nur vollbringen, sondern das auch noch *kostenlos*. Umwerfend.

Netlify bietet einen kostenlosen Plan mit serverless Functions, der dir bis zu 125.000 API-Aufrufe pro Monat ermöglicht. Amazon bietet einen ähnlichen Dienst namens Lambda. Wir bleiben bei Netlify für dieses Tutorial.

Netlify enthält Netlify Dev, ein CLI für die Netlify-Plattform. Im Wesentlichen ermöglicht es uns, eine Simulation unserer Anwendung in einer voll funktionsfähigen Produktionsumgebung auszuführen, alles sicher auf unserem lokalen Rechner. Wir können es verwenden, um unsere serverless Functions zu erstellen und zu testen, ohne sie bereitstellen zu müssen.

An dieser Stelle denke ich, dass es erwähnenswert ist, dass nicht jeder zustimmt, dass die Ausführung von Express in einer serverless Funktion eine gute Idee ist. Wie Paul Johnston erklärt, ist es für die Skalierung am besten, jedes Funktionsstück in seine eigene Einzelfunktion aufzuteilen. Die Verwendung von Express, wie ich es getan habe, bedeutet, dass jedes Mal, wenn eine Anfrage an die API geht, der gesamte Express-Server von Grund auf neu gestartet werden muss – nicht sehr effizient. Bereitstellung in der Produktion auf eigene Gefahr.

Lasst uns mit dem Bauen beginnen!

Jetzt, da wir unsere Werkzeuge bereit haben, können wir mit dem Projekt loslegen. Lasst uns mit der Erstellung eines neuen Ordners beginnen, uns im Terminal dorthin begeben und dann npm init darauf ausführen. Sobald npm eine package.json-Datei erstellt hat, können wir die benötigten Abhängigkeiten installieren. Diese Abhängigkeiten sind:

  1. Express
  2. GraphQL und express-graphql. Diese ermöglichen uns, GraphQL-Anfragen zu empfangen und darauf zu antworten.
  3. Bodyparser. Dies ist eine kleine Schicht, die die von uns empfangenen Anfragen in und aus JSON übersetzt, was GraphQL erwartet.
  4. Serverless-http. Dies dient als Wrapper für Express und stellt sicher, dass unsere Anwendung auf einer serverless Plattform wie Netlify genutzt werden kann.

Das ist alles! Wir können sie alle mit einem einzigen Befehl installieren:

npm i express express-graphql graphql body-parser serverless-http

Wir müssen auch Netlify Dev als globale Abhängigkeit installieren, damit wir es als CLI verwenden können.

npm i -g netlify-cli

Dateistruktur

Es gibt ein paar Dateien, die für das korrekte Funktionieren unserer API erforderlich sind. Die erste ist netlify.toml, die im Stammverzeichnis des Projekts erstellt werden sollte. Dies ist eine Konfigurationsdatei, die Netlify mitteilt, wie unser Projekt zu handhaben ist. Hier ist, was wir in der Datei benötigen, um unseren Startbefehl, unseren Build-Befehl und den Speicherort unserer serverless Functions zu definieren.

[build]


  # This command builds the site
  command = "npm run build"


  # This is the directory that will be deployed
  publish = "build"


  # This is where our functions are located
  functions = "functions"

Die Zeile functions ist sehr wichtig; sie teilt Netlify mit, wo wir unseren API-Code platzieren werden.

Erstellen wir als Nächstes diesen /functions-Ordner im Stammverzeichnis des Projekts und erstellen darin eine neue Datei namens api.js. Öffnen Sie sie und fügen Sie die folgenden Zeilen oben hinzu, damit unsere Abhängigkeiten verfügbar sind und in den Build einbezogen werden.

const express = require("express");
const bodyParser = require("body-parser");
const expressGraphQL = require("express-graphql");
const serverless = require("serverless-http");

Die Einrichtung von Express erfordert nur wenige Codezeilen. Zuerst initialisieren wir Express und verpacken es in die serverless-http serverless Function.

const app = express();
module.exports.handler = serverless(app);

Diese Zeilen initialisieren Express und verpacken es in die serverless-http Funktion. module.exports.handler teilt Netlify mit, dass unsere serverless Funktion die Express-Funktion ist.

Nun konfigurieren wir Express selbst.

app.use(bodyParser.json());
app.use(
  "/",
  expressGraphQL({
    graphiql: true
  })
);

Diese beiden Deklarationen sagen Express, welche **Middleware** wir ausführen. Middleware ist das, was wir *zwischen Anfrage und Antwort* passieren lassen wollen. In unserem Fall möchten wir JSON mit bodyparser parsen und es mit express-graphql verarbeiten. Die Konfiguration graphiql:true für express-graphql gibt uns eine schöne Benutzeroberfläche und einen Spielplatz zum Testen.

Definieren des GraphQL-Schemas

Um Anfragen zu verstehen und Antworten zu formatieren, muss GraphQL wissen, wie unsere Daten aussehen. Wenn du mit Datenbanken gearbeitet hast, weißt du, dass diese Art von Daten-Blueprint als **Schema** bezeichnet wird. GraphQL kombiniert dieses klar definierte Schema mit **Typen** – das heißt, Definitionen verschiedener Datenarten – um seine Magie zu wirken.

Das Allererste, was unser Schema benötigt, nennt man eine **Root Query**. Diese wird alle eingehenden Datenanfragen an unsere API bearbeiten. Sie wird als „Root“-Query bezeichnet, weil sie am Ursprung unserer API aufgerufen wird – sagen wir, api.example.com/graphql.

Für diese Demonstration erstellen wir ein „Hallo Welt“-Beispiel; die Root-Query sollte eine Antwort wie „Hallo Welt“ ergeben.

Unsere GraphQL-API benötigt also ein Schema (bestehend aus Typen) für die Root-Query. GraphQL stellt einige vorgefertigte Typen bereit, darunter ein schema, ein generisches object² und ein string.

Holen wir uns diese, indem wir dies unter den Imports hinzufügen.

const {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString
} = require("graphql");

Dann definieren wir unser Schema wie folgt:

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'HelloWorld',
    fields: () => ({ /* we'll put our response here */ })
  })
})

Das erste Element im Objekt mit dem Schlüssel query teilt GraphQL mit, wie eine Root-Query behandelt werden soll. Sein Wert ist ein GraphQL-Objekt mit der folgenden Konfiguration:

  • name – Ein Referenz, die für Dokumentationszwecke verwendet wird.
  • fields – Definiert die Daten, mit denen unser Server antworten wird. Es mag seltsam erscheinen, hier eine Funktion zu haben, die nur ein Objekt zurückgibt, aber dies ermöglicht uns, Variablen und Funktionen zu verwenden, die anderswo in unserer Datei definiert sind, ohne sie zuerst definieren zu müssen³.
const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: "HelloWorld",
    fields: () => ({
      message: {
        type: GraphQLString,
        resolve: () => "Hello World",
      },
    }),
  }),
});

Die Funktion fields gibt ein Objekt zurück, und unser Schema hat bisher nur ein einziges Feld message. Die message, mit der wir antworten wollen, ist ein String, also geben wir ihren Typ als GraphQLString an. Die Resolve-Funktion wird von unserem Server ausgeführt, um die gewünschte Antwort zu generieren. In diesem Fall geben wir nur „Hallo Welt“ zurück, aber in einer komplexeren Anwendung würden wir diese Funktion wahrscheinlich verwenden, um zu unserer Datenbank zu gehen und einige Daten abzurufen.

Das ist unser Schema! Wir müssen unseren Express-Server darüber informieren, also öffnen wir api.js und stellen sicher, dass die Express-Konfiguration wie folgt aktualisiert wird:

app.use(
  "/",
  expressGraphQL({
    schema: schema,
    graphiql: true
  })
);

Den Server lokal ausführen

Glaube es oder nicht, wir sind bereit, den Server zu starten! Führe netlify dev im Terminal aus dem Stammverzeichnis des Projekts aus. Netlify Dev liest die netlify.toml-Konfiguration, bündelt deine api.js-Funktion und macht sie von dort lokal verfügbar. Wenn alles nach Plan läuft, siehst du eine Meldung wie „Server now ready on https://:8888.“ 

Wenn du, wie ich das erste Mal, zu localhost:8888 gehst, bist du vielleicht ein wenig enttäuscht, eine 404-Fehlermeldung zu erhalten.

Aber keine Angst! Netlify führt die Funktion aus, nur in einem anderen Verzeichnis als erwartet, nämlich unter /.netlify/functions. Wenn du also zu localhost:8888/.netlify/functions/api gehst, solltest du wie erwartet die GraphiQL-Oberfläche sehen. Erfolg!

Das ist doch mal was!

Der Bildschirm, den wir erhalten, ist die GraphiQL-Umgebung, und wir können sie zum Testen der API verwenden. Lösche zunächst die Kommentare im linken Bereich und ersetze sie durch Folgendes:

{
  message
}

Das mag ein wenig… nackt… erscheinen, aber du hast gerade eine GraphQL-Abfrage geschrieben! Was wir sagen, ist, dass wir das Feld message sehen möchten, das wir in api.js definiert haben. Klicke auf die Schaltfläche „Run“, und auf der rechten Seite siehst du Folgendes:

{
  "data": {
    "message": "Hello World"
  }
}

Ich weiß nicht, wie es dir geht, aber ich habe das erste Mal ein kleines Freudenbäumchen gemacht, als ich das getan habe. Wir haben eine API erstellt!

Bonus: Anfragen umleiten

Eine meiner Hürden beim Erlernen von Netlify's serverless Functions war, dass sie unter dem Pfad /.netlify/functions laufen. Es war nicht ideal, ihn einzugeben oder sich daran zu erinnern, und ich hätte fast eine andere Lösung gewählt. Aber es stellt sich heraus, dass du Anfragen bei der Ausführung und Bereitstellung auf Netlify einfach umleiten kannst. Alles, was du tun musst, ist, eine Datei im Stammverzeichnis des Projekts namens _redirects zu erstellen (keine Dateierweiterung nötig) mit der folgenden Zeile darin:

/api /.netlify/functions/api 200!

Dies sagt Netlify, dass jeglicher Verkehr, der zu yoursite.com/api geht, an /.netlify/functions/api weitergeleitet werden soll. Das 200! weist den Server an, einen Statuscode von 200 zurückzugeben (was bedeutet, dass alles in Ordnung ist).

Die API bereitstellen

Um das Projekt bereitzustellen, müssen wir den Quellcode mit Netlify verbinden. Ich hoste meinen in einem GitHub-Repository, was eine kontinuierliche Bereitstellung ermöglicht.

Nachdem das Repository mit Netlify verbunden ist, ist der Rest automatisch: Der Code wird verarbeitet und als serverless Funktion bereitgestellt! Du kannst dich im Netlify-Dashboard anmelden, um die Protokolle von jeder Funktion einzusehen.

Fazit

So einfach ist das: Wir können eine serverless API mit GraphQL mit ein paar Zeilen JavaScript und leichter Konfiguration erstellen. Und hey, wir können sie sogar bereitstellen – *kostenlos*. 

Die Möglichkeiten sind endlos. Vielleicht möchtest du deine eigene persönliche Wissensdatenbank erstellen oder ein Werkzeug, um Design-Tokens bereitzustellen. Vielleicht möchtest du versuchen, deine eigene PokéAPI zu erstellen. Oder vielleicht interessierst du dich für die Arbeit mit GraphQL.

Unabhängig davon, was du erstellst, sind es diese Art von Technologien, die jeden Tag zugänglicher werden. Es ist aufregend, mit einigen der modernsten Werkzeuge und Techniken arbeiten zu können, ohne tiefgreifende Kenntnisse im Back-End-Bereich zu benötigen.

Wenn du den vollständigen Quellcode für dieses Projekt sehen möchtest, ist er auf GitHub verfügbar.

Ein Teil des Codes in diesem Tutorial wurde aus dem Artikel „Learn GraphQL in 40 minutes“ von Web Dev Simplified übernommen. Es ist eine großartige Ressource, um tiefer in GraphQL einzudringen. Allerdings konzentriert es sich auch auf ein eher traditionelles, Server-basiertes Express.


  1. Wenn du das vollständige Ergebnis meiner Erkundungen sehen möchtest, habe ich auf meiner Website ein Begleitstück mit dem Titel „A design API in practice“ verfasst.
  2. Die Gründe, warum du ein spezielles GraphQL-Objekt anstelle eines regulären, einfachen JavaScript-Objekts in geschweiften Klammern benötigst, liegen etwas außerhalb des Rahmens dieses Tutorials. Beachte einfach, dass GraphQL eine fein abgestimmte Maschine ist, die diese spezialisierten Typen nutzt, um schnell und belastbar zu sein.
  3. Scope und Hoisting sind einige der verwirrenderen Themen in JavaScript. MDN bietet eine gute Einführung, die es wert ist, angeschaut zu werden.