Passer au contenu

Exploitation de la puissance de TypeScript et GraphQL

GraphQL est un langage de requête puissant pour les API et un environnement d’exécution pour satisfaire ces requêtes avec vos données existantes. C’est une approche élégante qui résout de nombreux problèmes typiquement rencontrés avec les API REST. Pour plus de contexte, nous vous recommandons de lire cette comparaison entre GraphQL et REST. GraphQL combiné avec TypeScript vous aide à développer une meilleure sécurité de type avec vos requêtes GraphQL, vous offrant une typage de bout en bout.

Dans ce chapitre, nous supposons une compréhension de base de GraphQL et nous nous concentrons sur la façon de travailler avec le module intégré @nestjs/graphql. Le GraphQLModule peut être configuré pour utiliser le serveur Apollo (avec le pilote @nestjs/apollo) et Mercurius (avec le @nestjs/mercurius). Nous fournissons des intégrations officielles pour ces packages GraphQL éprouvés afin de fournir un moyen simple d’utiliser GraphQL avec Nest (voir plus d’intégrations ici).

Vous pouvez également construire votre propre conducteur dédié (lisez-en plus ici).

Installation

Commencez par installer les packages requis :

Fenêtre de terminal
# Pour Express et Apollo (par défaut)
$ npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql
# Pour Fastify et Apollo
# npm i @nestjs/graphql @nestjs/apollo @apollo/server @as-integrations/fastify graphql
# Pour Fastify et Mercurius
# npm i @nestjs/graphql @nestjs/mercurius graphql mercurius

Vue d’ensemble

Nest offre deux façons de construire des applications GraphQL, les méthodes code first et schema first. Vous devriez choisir celle qui fonctionne le mieux pour vous. La plupart des chapitres de cette section GraphQL sont divisés en deux parties principales : celle que vous devez suivre si vous adoptez code first, et l’autre à utiliser si vous adoptez schema first.

Avec l’approche code first, vous utilisez des décorateurs et des classes TypeScript pour générer le schéma GraphQL correspondant. Cette approche est utile si vous préférez travailler exclusivement avec TypeScript et éviter de passer d’une syntaxe de langage à l’autre.

Avec l’approche schema first, la source de vérité est les fichiers SDL (Schema Definition Language) GraphQL. SDL est une manière indépendante du langage de partager des fichiers de schéma entre différentes plateformes. Nest génère automatiquement vos définitions TypeScript (en utilisant soit des classes, soit des interfaces) basées sur les schémas GraphQL pour réduire la nécessité d’écrire du code boilerplate redondant.

Une fois les packages installés, nous pouvons importer le GraphQLModule et le configurer avec la méthode statique forRoot().

Configuration de GraphQLModule
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
}),
],
})
export class AppModule {}

La méthode forRoot() prend un objet d’options en argument. Ces options sont passées à l’instance de conducteur sous-jacente (lisez-en plus sur les paramètres disponibles ici : Apollo et Mercurius). Par exemple, si vous souhaitez désactiver le playground et désactiver le mode debug (pour Apollo), passez les options suivantes :

Configuration d'Apollo avec options
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
playground: false,
}),
],
})
export class AppModule {}

Dans ce cas, ces options seront transférées au constructeur ApolloServer.

GraphQL Playground

Le playground est un IDE GraphQL interactif et graphique disponible par défaut à la même URL que le serveur GraphQL lui-même. Pour accéder à l’environnement de développement, vous avez besoin d’un serveur GraphQL configuré et en cours d’exécution. Pour le voir maintenant, vous pouvez installer et construire l’exemple fonctionnel ici. Alternativement, si vous suivez ces exemples de code, une fois que vous avez complété les étapes dans le chapitre des Resolvers, vous pourrez accéder au playground.

Avec cela en place, et avec votre application en cours d’exécution en arrière-plan, vous pouvez ensuite ouvrir votre navigateur web et naviguer vers http://localhost:3000/graphql (l’hôte et le port peuvent varier en fonction de votre configuration). Vous verrez alors le playground GraphQL.

playground

L’intégration @nestjs/mercurius n’expédie pas avec l’intégration du GraphQL Playground intégrée. Au lieu de cela, vous pouvez utiliser GraphiQL (définissez graphiql: true).

Points de terminaison multiples

Une autre fonctionnalité utile du module @nestjs/graphql est la possibilité de servir plusieurs points de terminaison à la fois. Cela vous permet de décider quels modules doivent être inclus dans quel point de terminaison. Par défaut, GraphQL recherche des resolvers dans toute l’application. Pour limiter cette recherche à un sous-ensemble de modules, utilisez la propriété include.

Configuration des points de terminaison multiples
GraphQLModule.forRoot({
include: [CatsModule],
}),

Si vous utilisez le @apollo/server avec le package @as-integrations/fastify avec plusieurs points de terminaison GraphQL dans une seule application, assurez-vous d’activer le paramètre disableHealthCheck dans la configuration du GraphQLModule.

Code First

Dans l’approche code first, vous utilisez des décorateurs et des classes TypeScript pour générer le schéma GraphQL correspondant.

Pour utiliser l’approche code first, commencez par ajouter la propriété autoSchemaFile à l’objet d’options :

Configuration de GraphQLModule avec autoSchemaFile
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
}),

La valeur de la propriété autoSchemaFile est le chemin où votre schéma généré automatiquement sera créé. Alternativement, le schéma peut être généré à la volée en mémoire. Pour activer cela, définissez la propriété autoSchemaFile sur true :

Génération de schéma en mémoire
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true,
}),

Par défaut, les types dans le schéma généré seront dans l’ordre dans lequel ils sont définis dans les modules inclus. Pour trier le schéma de manière lexicographique, définissez la propriété sortSchema sur true :

Triage du schéma
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
sortSchema: true,
}),

Exemple

Un exemple fonctionnel de code first est disponible ici.

Schema First

Pour utiliser l’approche schema first, commencez par ajouter une propriété typePaths à l’objet d’options. La propriété typePaths indique où le GraphQLModule devrait chercher les fichiers de définition de schéma SDL GraphQL que vous écrivez. Ces fichiers seront combinés en mémoire ; cela vous permet de diviser vos schémas en plusieurs fichiers et de les situer près de leurs resolvers.

Configuration du GraphQLModule avec typePaths
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/*.graphql'],
}),

Vous devrez également avoir des définitions TypeScript (classes et interfaces) qui correspondent aux types SDL GraphQL. Créer les définitions TypeScript correspondantes à la main est redondant et fastidieux. Cela nous laisse sans source unique de vérité : chaque changement apporté dans SDL nous oblige à ajuster également les définitions TypeScript. Pour remédier à cela, le package @nestjs/graphql peut générer automatiquement des définitions TypeScript à partir de l’arbre de syntaxe abstraite (AST). Pour activer cette fonctionnalité, ajoutez les options definitions lors de la configuration du GraphQLModule.

Génération automatique des définitions TypeScript
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/*.graphql'],
definitions: {
path: join(process.cwd(), 'src/graphql.ts'),
},
}),

La propriété path de l’objet definitions indique où enregistrer la sortie TypeScript générée. Par défaut, tous les types TypeScript générés sont créés sous forme d’interfaces. Pour générer des classes, spécifiez la propriété outputAs avec une valeur de class.

Génération de classes
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/*.graphql'],
definitions: {
path: join(process.cwd(), 'src/graphql.ts'),
outputAs: 'class',
},
}),

L’approche ci-dessus génère dynamiquement les définitions TypeScript chaque fois que l’application démarre. Alternativement, il peut être préférable de créer un script simple pour les générer à la demande.

Intégration Mercurius

Au lieu d’utiliser Apollo, les utilisateurs de Fastify (lisez-en plus ici) peuvent utiliser le pilote @nestjs/mercurius.

Configuration du module avec Mercurius
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { MercuriusDriver, MercuriusDriverConfig } from '@nestjs/mercurius';
@Module({
imports: [
GraphQLModule.forRoot<MercuriusDriverConfig>({
driver: MercuriusDriver,
graphiql: true,
}),
],
})
export class AppModule {}

Intégrations tierces

Accéder au schéma généré

Dans certaines circonstances (par exemple, pour des tests de bout en bout), vous voudrez peut-être obtenir une référence à l’objet schéma généré.

Accéder à l'objet schéma
const { schema } = app.get(GraphQLSchemaHost);

Configuration asynchrone

Lorsque vous devez passer des options de module de manière asynchrone au lieu de statiquement, utilisez la méthode forRootAsync(). Comme pour la plupart des modules dynamiques, Nest fournit plusieurs techniques pour gérer la configuration asynchrone.

Configuration asynchrone du module GraphQL
GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
useFactory: () => ({
typePaths: ['./**/*.graphql'],
}),
}),
Utilisation d'une classe pour la configuration
GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
useClass: GqlConfigService,
}),
Exemple d'implémentation de GqlConfigService
@Injectable()
class GqlConfigService implements GqlOptionsFactory {
createGqlOptions(): ApolloDriverConfig {
return {
typePaths: ['./**/*.graphql'],
};
}
}
Réutilisation d'un fournisseur d'options existant
GraphQLModule.forRootAsync<ApolloDriverConfig>({
imports: [ConfigModule],
useExisting: ConfigService,
}),