GraphQL ist das neue Modewort in der API-Entwicklung. RESTful-APIs sind zwar nach wie vor der beliebteste Weg, um Daten aus Anwendungen zu veröffentlichen, aber sie haben viele Einschränkungen, die mit GraphQL gelöst werden sollen.

GraphQL ist eine von Facebook entwickelte Abfragesprache, die 2015 in ein Open-Source-Projekt umgewandelt wurde. Sie bietet eine intuitive und flexible Syntax für die Beschreibung von und den Zugriff auf Daten in einer API.

In diesem Leitfaden erfährst du, wie du ein GraphQL-Node.js-Projekt erstellen kannst. Wir werden GraphQL verwenden, um eine Todo-Anwendung im Express.js Web-Framework für Node zu erstellen.

Was ist GraphQL?

Aus der offiziellen Dokumentation: „GraphQL ist eine Abfragesprache für APIs und eine Laufzeitumgebung, um diese Abfragen mit deinen vorhandenen Daten zu erfüllen. GraphQL liefert eine vollständige und verständliche Beschreibung der Daten in deiner API, gibt den Kunden die Möglichkeit, genau das abzufragen, was sie brauchen, und nicht mehr, erleichtert die Weiterentwicklung von APIs im Laufe der Zeit und ermöglicht leistungsstarke Entwicklerwerkzeuge.“

GraphQL ist eine serverseitige Laufzeitumgebung für die Ausführung von Abfragen unter Verwendung des Typsystems, das du für deine Daten definiert hast. Außerdem ist GraphQL nicht an eine bestimmte Datenbank oder Speicher-Engine gebunden. Stattdessen wird es von deinem bestehenden Code und Datenspeicher unterstützt. Einen detaillierten Vergleich dieser Technologien findest du in der Anleitung GraphQL vs. RESTful API.

Um einen GraphQL-Dienst zu erstellen, definierst du zunächst Schematypen und erstellst Felder, die diese Typen verwenden. Als Nächstes stellst du einen Funktionsresolver bereit, der für jedes Feld und jeden Typ ausgeführt wird, wenn Daten von der Client-Seite angefordert werden.

GraphQL-Terminologie

Das GraphQL-Typensystem wird verwendet, um zu beschreiben, welche Daten abgefragt werden können und welche Daten du manipulieren kannst. Es ist der Kern von GraphQL. Im Folgenden werden die verschiedenen Möglichkeiten zur Beschreibung und Manipulation von Daten in GraphQL erläutert.

Objekttypen

GraphQL-Objekttypen sind Datenmodelle, die stark typisierte Felder enthalten. Es sollte eine 1:1-Zuordnung zwischen deinen Modellen und GraphQL-Typen geben. Unten findest du ein Beispiel für einen GraphQL-Typ:

type User {
  id: ID! # The "!" means required
  firstname: String
  lastname: String
  email: String
  username: String
  todos: [Todo] # Todo is another GraphQL type
}

Abfragen

GraphQL Query definiert alle Abfragen, die ein Client über die GraphQL API ausführen kann. Du solltest eine RootQuery definieren, die per Konvention alle bestehenden Abfragen enthält.

Im Folgenden definieren wir die Abfragen und ordnen sie der entsprechenden RESTful-API zu:

type RootQuery {
  user(id: ID): User           # Corresponds to GET /api/users/:id
  users: [User]                # Corresponds to GET /api/users
  todo(id: ID!): Todo    # Corresponds to GET /api/todos/:id
  todos: [Todo]          # Corresponds to GET /api/todos
}

Mutationen

Wenn GraphQL-Abfragen GET Anfragen sind, sind Mutationen POST, PUT, PATCH und DELETE Anfragen, die die GraphQL-API manipulieren.

Zur Veranschaulichung fügen wir alle Mutationen in eine einzige RootMutation ein:

type RootMutation {
  createUser(input: UserInput!): User             # Corresponds to POST /api/users
  updateUser(id: ID!, input: UserInput!): User    # Corresponds to PATCH /api/users
  removeUser(id: ID!): User                       # Corresponds to DELETE /api/users
  createTodo(input: TodoInput!): Todo
  updateTodo(id: ID!, input: TodoInput!): Todo
  removeTodo(id: ID!): Todo
}

Sie haben die Verwendung von -input-Typen für die Mutationen bemerkt, wie z.B. UserInput, TodoInput. Es ist immer die beste Praxis, Input-Typen für das Erstellen und Aktualisieren deiner Ressourcen zu definieren.

Du kannst die Eingabearten wie die folgende definieren:

input UserInput {
  firstname: String!
  lastname: String
  email: String!
  username: String!
}

Resolver

Resolver sagen GraphQL, was zu tun ist, wenn eine Abfrage oder Mutation angefordert wird. Es handelt sich um eine grundlegende Funktion, die die harte Arbeit erledigt, indem sie die Datenbankschicht anspricht, um die CRUD-Operationen (Erstellen, Lesen, Aktualisieren, Löschen) auszuführen, einen internen RESTful API-Endpunkt anspricht oder einen Microservice aufruft, um die Anfrage des Kunden zu erfüllen.

Du kannst eine neue resolvers.js-Datei erstellen und den folgenden Code einfügen:

import sequelize from '../models';
export default function resolvers () {
  const models = sequelize.models;
  return {
    // Resolvers for Queries
    RootQuery: {
      user (root, { id }, context) {
        return models.User.findById(id, context);
      },
      users (root, args, context) {
        return models.User.findAll({}, context);
      }
    },
    User: {
      todos (user) {
        return user.getTodos();
      }
    },
  }
  // Resolvers for Mutations
  RootMutation: {
    createUser (root, { input }, context) {
      return models.User.create(input, context);    
    },
    updateUser (root, { id, input }, context) {
      return models.User.update(input, { ...context, where: { id } });
    },
    removeUser (root, { id }, context) {
      return models.User.destroy(input, { ...context, where: { id } });
    },
    // ... Resolvers for Todos go here
  }
}

Schema

Das GraphQL-Schema ist das, was GraphQL der Welt offenbart. Deshalb werden die Typen, Abfragen und Mutationen in das Schema aufgenommen, um der Welt offenbart zu werden.

Im Folgenden wird beschrieben, wie du Typen, Abfragen und Mutationen der Welt zugänglich machst:

schema {
  query: RootQuery
  mutation: RootMutation
}

Im obigen Skript haben wir RootQuery und RootMutation, die wir zuvor erstellt haben, für die Welt zugänglich gemacht.

Wie funktioniert GraphQL mit Node.js und Express.js?

GraphQL bietet eine Implementierung für alle wichtigen Programmiersprachen, auch Node.js ist davon nicht ausgenommen. Auf der offiziellen GraphQL-Website gibt es einen Abschnitt für die Unterstützung von JavaScript, und es gibt auch andere Implementierungen von GraphQL, die das Schreiben und Kodieren erleichtern.

GraphQL Apollo bietet eine Implementierung für Node.js und Express.js und macht den Einstieg in GraphQL leicht.

Im nächsten Abschnitt erfährst du, wie du deine erste GraphQL-Anwendung in Node.js und dem Backend-Framework Express.js mit GraphQL Apollo erstellst und entwickelst.

Einrichten von GraphQL mit Express.js

Der Aufbau eines GraphQL-API-Servers mit Express.js ist ein einfacher Einstieg. In diesem Abschnitt erfahren wir, wie man einen GraphQL-Server aufbaut.

Projekt mit Express initialisieren

Zuerst musst du ein neues Express.js Projekt installieren und einrichten. Erstelle einen Ordner für dein Projekt und installiere Express.js mit diesem Befehl:

cd <project-name> && npm init -y
npm install express

Der obige Befehl erstellt eine neue package.json-Datei und installiert die Express.js-Bibliothek in deinem Projekt.

Als Nächstes strukturieren wir unser Projekt wie in der Abbildung unten gezeigt. Es wird verschiedene Module für die Funktionen des Projekts enthalten, wie z. B. Benutzer, ToDos usw.

Eine Liste der Dateien in graphql-todo
Dateien für graphql-todo

GraphQL initialisieren

Beginnen wir mit der Installation der GraphQL Express.js-Abhängigkeiten. Führe den folgenden Befehl aus, um sie zu installieren:

npm install apollo-server-express graphql @graphql-tools/schema --save

Schemas und Typen erstellen

Als Nächstes erstellen wir eine index.js-Datei im modules-Ordner und fügen den folgenden Codeschnipsel hinzu:

const { gql } = require('apollo-server-express');
const users = require('./users');
const todos = require('./todos');
const { GraphQLScalarType } = require('graphql');
const { makeExecutableSchema } = require('@graphql-tools/schema');
const typeDefs = gql`
  scalar Time
  type Query {
    getVersion: String!
  }
  type Mutation {
    version: String!
  }
`;
const timeScalar = new GraphQLScalarType({
  name: 'Time',
  description: 'Time custom scalar type',
 serialize: (value) => value,
});
const resolvers = {
  Time: timeScalar,
  Query: {
    getVersion: () => `v1`,
  },
};
const schema = makeExecutableSchema({
  typeDefs: [typeDefs, users.typeDefs, todos.typeDefs],
  resolvers: [resolvers, users.resolvers, todos.resolvers],
});
module.exports = schema;

Code-Walkthrough

Gehen wir den Codeschnipsel durch und schlüsseln ihn auf:

Schritt 1

Zuerst haben wir die erforderlichen Bibliotheken importiert und Standardabfrage- und Mutationstypen erstellt. Die Abfrage und die Mutation legen vorerst nur die Version der GraphQL API fest. Im weiteren Verlauf werden wir die Abfrage und die Mutation jedoch erweitern, um andere Schemata einzubeziehen.

Eine Kommandozeilenschnittstelle, die den "const"-Code für den Import von GraphQL und anderen Erweiterungen zeigt
GraphQL und Erweiterungen importieren
Schritt 2:

Dann haben wir einen neuen skalaren Typ für Zeit und unseren ersten Resolver für die oben erstellte Abfrage und Mutation erstellt. Außerdem haben wir mit der Funktion makeExecutableSchema ein Schema erstellt.

Das generierte Schema enthält alle anderen Schemata, die wir importiert haben, und wird auch weitere enthalten, wenn wir sie erstellen und importieren.

Eine Kommandozeilenschnittstelle, die den "const"-Code für die Erstellung unseres skalaren Typs und unseres ersten Resolvers zeigt
Wir erstellen einen skalaren Typ für die Zeit und unseren ersten Resolver

Der obige Codeschnipsel zeigt, dass wir verschiedene Schemata in die Funktion makeExecutableSchema importiert haben. Dieser Ansatz hilft uns dabei, die Anwendung komplexer zu gestalten. Als Nächstes erstellen wir die Schemata Todo und User, die wir importiert haben.

Todo-Schema erstellen

Das Todo-Schema zeigt einfache CRUD-Operationen, die die Benutzer der Anwendung durchführen können. Im Folgenden siehst du das Schema, das die Todo-CRUD-Operation implementiert.

const { gql } = require('apollo-server-express');
const createTodo = require('./mutations/create-todo');
const updateTodo = require('./mutations/update-todo');
const removeTodo = require('./mutations/delete-todo');
const todo = require('./queries/todo');
const todos = require('./queries/todos');
const typeDefs = gql`
  type Todo {
    id: ID!
    title: String
    description: String
    user: User
  }
  input CreateTodoInput {
    title: String!
    description: String
    isCompleted: Boolean
  }
  input UpdateTodoInput {
    title: String
    description: String
    isCompleted: Boolean
  }  extend type Query {
    todo(id: ID): Todo!
    todos: [Todo!]
  }
  extend type Mutation {
    createTodo(input: CreateTodoInput!): Todo
    updateTodo(id: ID!, input: UpdateTodoInput!): Todo
    removeTodo(id: ID!): Todo
  }
`;
// Provide resolver functions for your schema fields
const resolvers = {
  // Resolvers for Queries
  Query: {
    todo,
    todos,
  },
  // Resolvers for Mutations
  Mutation: {
    createTodo,
    updateTodo,
    removeTodo,
  },
};
module.exports = { typeDefs, resolvers };

Code-Walkthrough

Gehen wir das Codeschnipsel durch und schlüsseln es auf:

Schritt 1:

Zuerst haben wir ein Schema für unser Todo mit GraphQL type, input und extend erstellt. Das Schlüsselwort extend wird verwendet, um neue Abfragen und Mutationen an die bestehende Stammabfrage und Mutation, die wir oben erstellt haben, zu vererben und hinzuzufügen.

Eine Kommandozeilenschnittstelle, die das Schema für unser Todo-Skript zeigt, einschließlich neuer Eingaben
Erstellen des Schemas für unseren Todo
Schritt 2:

Als Nächstes haben wir einen Resolver erstellt, mit dem wir die richtigen Daten abrufen, wenn eine bestimmte Abfrage oder Mutation aufgerufen wird.

A command line interface showing the code to create a resolver for our Todo.
Einen Resolver erstellen.

Mit der Resolver-Funktion können wir einzelne Methoden für die Geschäftslogik und die Datenbankmanipulation erstellen, wie im Beispiel create-todo.js gezeigt.

Erstelle eine Datei create-user.js im Ordner ./mutations/ und füge die Geschäftslogik hinzu, um ein neues Todo in deiner Datenbank zu erstellen.

const models = require('../../../models');
module.exports = async (root, { input }, context) => {
  return models.todos.push({ ...input });
};

Das obige Codeschnipsel ist eine vereinfachte Art, ein neues Todo in unserer Datenbank mit dem Sequelize ORM zu erstellen. Du kannst hier mehr über Sequelize erfahren und wie du es mit Node.js einrichtest.

Du kannst denselben Schritt befolgen, um je nach deiner Anwendung mehrere Schemata zu erstellen, oder du kannst das komplette Projekt von GitHub klonen.

Als Nächstes werden wir den Server mit Express.js einrichten und die neu erstellte Todo-Anwendung mit GraphQL und Node.js ausführen.

Einrichten und Ausführen des Servers

Zum Schluss richten wir unseren Server mit der apollo-server-express Bibliothek ein, die wir zuvor installiert haben, und konfigurieren ihn.

apollo-server-express ist ein einfacher Wrapper von Apollo Server für Express.js. Er wird empfohlen, weil er für die Entwicklung von Express.js entwickelt wurde.

Anhand der Beispiele, die wir oben besprochen haben, konfigurieren wir den Express.js-Server so, dass er mit dem neu installierten apollo-server-express funktioniert.

Erstelle eine server.js-Datei im Stammverzeichnis und füge den folgenden Code ein:


const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const schema = require('./modules');
const app = express();
async function startServer() {
  const server = new ApolloServer({ schema });
  await server.start();
  server.applyMiddleware({ app });
}
startServer();
app.listen({ port: 3000 }, () =>
  console.log(`Server ready at http://localhost:3000`)
);

Mit dem obigen Code hast du erfolgreich deinen ersten CRUD-GraphQL-Server für Todos und Benutzer erstellt. Du kannst deinen Entwicklungsserver starten und mit http://localhost:3000/graphql auf den Playground zugreifen. Wenn alles erfolgreich war, solltest du den unten stehenden Bildschirm sehen:

Eine Entwicklungsschnittstelle, die eine einfache Abfrage als Antwort zeigt
Der Verifizierungsbildschirm

Zusammenfassung

GraphQL ist eine moderne, von Facebook unterstützte Technologie, die dir die mühsame Arbeit bei der Erstellung großer APIs mit RESTful-Architekturmustern abnehmen kann.

Dieser Leitfaden erläutert GraphQL und zeigt dir, wie du deine erste GraphQL-API mit Express.js entwickelst.

Lass uns in den Kommentaren unten wissen, was du mit GraphQL entwickelst.

Solomon Eseme

I am a Software Engineer and Content Creator who is geared toward building high-performing and innovative products following best practices and industry standards. I also love writing about it at Masteringbackend.com. Follow me on Twitter, LinkedIn, and About Me