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.
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.
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.
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.
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.
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:
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.
Schreibe einen Kommentar