GraphQL API mit TypeScript: Schritt-für-Schritt-Tutorial – Teil 3
In den vorherigen Teilen des Tutorials haben wir die Grundlagen der Erstellung eines GraphQL-Servers mit TypeScript behandelt und gelernt, einfache Abfragen zu konfigurieren. In diesem letzten Teil tauchen wir in fortgeschrittene Techniken ein: Mutationen, Subscriptions und Fehlerbehandlung. Wenn Sie TypeScript für Anfänger lernen und moderne Programmierung von APIs beherrschen möchten, ist dieses Material ein hervorragender Schritt nach vorne. Das gesamte Training basiert auf praktischen Beispielen, die Sie sofort in Ihren eigenen Projekten anwenden können.
1. Implementierung von Mutationen mit TypeScript
Mutationen sind Operationen zur Datenänderung (Erstellen, Aktualisieren, Löschen). In GraphQL werden sie mit dem Schlüsselwort mutation gekennzeichnet. Lassen Sie uns eine Mutation zum Hinzufügen eines neuen Benutzers erstellen.
Zuerst definieren wir den Typ im GraphQL-Schema:
type Mutation { addUser(name: String!, email: String!): User!}Jetzt implementieren wir den Resolver in TypeScript. Beachten Sie die strenge Typisierung:
import { Resolver, Mutation, Arg } from 'type-graphql';import { User } from '../entities/User';
@Resolver()export class UserResolver { @Mutation(() => User) async addUser( @Arg('name') name: string, @Arg('email') email: string ): Promise<User> { const user = new User(); user.name = name; user.email = email; await user.save(); return user; }}Wichtig: In TypeScript verwenden wir die Decorators @Mutation und @Arg aus der Bibliothek type-graphql, was den Code sauber und typsicher macht. Dies ist besonders nützlich für die Programmierung großer Projekte, bei denen Vorhersagbarkeit wichtig ist.
2. Subscriptions für Echtzeit-Updates
Subscriptions ermöglichen es Clients, Updates in Echtzeit über WebSocket zu erhalten. In TypeScript erfordert ihre Implementierung eine zusätzliche Serverkonfiguration.
Fügen wir eine Subscription für neue Nachrichten hinzu:
import { Subscription, Root } from 'type-graphql';
@Resolver()export class MessageResolver { @Subscription(() => Message, { topics: 'NEW_MESSAGE', }) newMessage(@Root() message: Message): Message { return message; }}Zum Veröffentlichen eines Ereignisses verwenden wir pubSub.publish('NEW_MESSAGE', message). Dies ermöglicht die Erstellung eines Chats oder von Benachrichtigungen in Echtzeit. Subscriptions sind ein leistungsstarkes Werkzeug im Arsenal der Programmierung, und TypeScript macht ihre Implementierung durch statische Typisierung sicher.
3. Fehlerbehandlung und Validierung
Eine ordnungsgemäße Fehlerbehandlung ist der Schlüssel zu einer zuverlässigen API. In TypeScript können wir benutzerdefinierte Fehlerklassen verwenden:
import { ApolloError } from 'apollo-server-express';
export class UserInputError extends ApolloError { constructor(message: string) { super(message, 'USER_INPUT_ERROR'); Object.defineProperty(this, 'name', { value: 'UserInputError' }); }}In den Resolvern überprüfen wir die Eingabedaten:
@Mutation(() => User)async createUser(@Arg('email') email: string) { if (!email.includes('@')) { throw new UserInputError('Invalid email format'); } // ... Logik zum Erstellen}Wir können auch Validierungs-Decorators aus class-validator verwenden:
import { IsEmail, Length } from 'class-validator';
class CreateUserInput { @Length(2, 50) name: string;
@IsEmail() email: string;}Diesen Ansatz empfehle ich besonders für TypeScript für Anfänger, da er die Datenprüfung automatisiert und die Anzahl der Fehler reduziert.
4. Leistungsoptimierung
Für Produktionsprojekte ist es wichtig, das N+1-Problem zu vermeiden. Verwenden Sie DataLoader für