Passer au contenu

Scalars

Un type d’objet GraphQL a un nom et des champs, mais à un moment donné, ces champs doivent se résoudre en des données concrètes. C’est là que les types scalars entrent en jeu: ils représentent les feuilles de la requête (lisez-en plus ici). GraphQL inclut les types par défaut suivants: Int, Float, String, Boolean et ID. En plus de ces types intégrés, vous pouvez avoir besoin de prendre en charge des types de données atomiques personnalisés (par exemple, Date).

Code first

L’approche code-first propose cinq scalars dont trois d’entre eux sont des alias simples pour les types GraphQL existants.

  • ID (alias pour GraphQLID) - représente un identifiant unique, souvent utilisé pour récupérer à nouveau un objet ou comme clé pour un cache
  • Int (alias pour GraphQLInt) - un entier signé de 32 bits
  • Float (alias pour GraphQLFloat) - une valeur à virgule flottante double précision signée
  • GraphQLISODateTime - une chaîne de date-heure en UTC (utilisée par défaut pour représenter le type Date)
  • GraphQLTimestamp - un entier signé qui représente la date et l’heure sous forme de nombre de millisecondes écoulées depuis le début de l’époque UNIX

Le GraphQLISODateTime (par exemple, 2019-12-03T09:54:33Z) est utilisé par défaut pour représenter le type Date. Pour utiliser le GraphQLTimestamp, définissez dateScalarMode de l’objet buildSchemaOptions à 'timestamp' comme suit:

Configurer le mode de scalar date
GraphQLModule.forRoot({
buildSchemaOptions: {
dateScalarMode: 'timestamp',
},
})

De même, le GraphQLFloat est utilisé par défaut pour représenter le type number. Pour utiliser le GraphQLInt, définissez numberScalarMode de l’objet buildSchemaOptions à 'integer' comme suit:

Configurer le mode de scalar nombre
GraphQLModule.forRoot({
buildSchemaOptions: {
numberScalarMode: 'integer',
},
})

En outre, vous pouvez créer des scalars personnalisés.

Override a default scalar

Pour créer une implémentation personnalisée pour le scalar Date, créez simplement une nouvelle classe:

Créer un scalar Date personnalisé
import { Scalar, CustomScalar } from '@nestjs/graphql';
import { Kind, ValueNode } from 'graphql';
@Scalar('Date', (type) => Date)
export class DateScalar implements CustomScalar<number, Date> {
description = 'Type scalar personnalisé Date';
parseValue(value: number): Date {
return new Date(value); // valeur du client
}
serialize(value: Date): number {
return value.getTime(); // valeur envoyée au client
}
parseLiteral(ast: ValueNode): Date {
if (ast.kind === Kind.INT) {
return new Date(ast.value);
}
return null;
}
}

Avec cela en place, enregistrez DateScalar en tant que fournisseur.

Enregistrer le scalar Date
@Module({
providers: [DateScalar]
})
export class CommonModule {}

Maintenant, nous pouvons utiliser le type Date dans nos classes.

Utiliser le scalar Date
@Field()
creationDate: Date;

Import a custom scalar

Pour utiliser un scalar personnalisé, importez-le et enregistrez-le en tant que résolveur. Nous utiliserons le package graphql-type-json à des fins de démonstration. Ce package npm définit un type scalar JSON GraphQL.

Commencez par installer le package:

Fenêtre de terminal
$ npm i --save graphql-type-json

Une fois le package installé, nous passons un résolveur personnalisé à la méthode forRoot():

Utiliser le scalar JSON personnalisé
import GraphQLJSON from 'graphql-type-json';
@Module({
imports: [
GraphQLModule.forRoot({
resolvers: { JSON: GraphQLJSON },
}),
],
})
export class AppModule {}

Maintenant, nous pouvons utiliser le type JSON dans nos classes.

Utiliser le scalar JSON
@Field(() => GraphQLJSON)
info: JSON;

Pour un ensemble de scalars utiles, jetez un œil au package graphql-scalars.

Create a custom scalar

Pour définir un scalar personnalisé, créez une nouvelle instance de GraphQLScalarType. Nous allons créer un scalar personnalisé UUID.

Créer un scalar UUID personnalisé
const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
function validate(uuid: unknown): string | never {
if (typeof uuid !== "string" || !regex.test(uuid)) {
throw new Error("invalid uuid");
}
return uuid;
}
export const CustomUuidScalar = new GraphQLScalarType({
name: 'UUID',
description: 'Un simple parseur UUID',
serialize: (value) => validate(value),
parseValue: (value) => validate(value),
parseLiteral: (ast) => validate(ast.value),
});

Nous passons un résolveur personnalisé à la méthode forRoot():

Enregistrer le scalar UUID
@Module({
imports: [
GraphQLModule.forRoot({
resolvers: { UUID: CustomUuidScalar },
}),
],
})
export class AppModule {}

Maintenant, nous pouvons utiliser le type UUID dans nos classes.

Utiliser le scalar UUID
@Field(() => CustomUuidScalar)
uuid: string;

Schema first

Pour définir un scalar personnalisé (lisez-en plus sur les scalars ici), créez une définition de type et un résolveur dédié. Ici (comme dans la documentation officielle), nous utiliserons le package graphql-type-json à des fins de démonstration. Ce package npm définit un type scalar JSON GraphQL.

Commencez par installer le package:

Fenêtre de terminal
$ npm i --save graphql-type-json

Une fois le package installé, nous passons un résolveur personnalisé à la méthode forRoot():

Configurer AppModule avec JSON
import GraphQLJSON from 'graphql-type-json';
@Module({
imports: [
GraphQLModule.forRoot({
typePaths: ['./**/*.graphql'],
resolvers: { JSON: GraphQLJSON },
}),
],
})
export class AppModule {}

Maintenant, nous pouvons utiliser le scalar JSON dans nos définitions de types:

scalar JSON
type Foo {
field: JSON
}

Une autre méthode pour définir un type scalar est de créer une classe simple. Supposons que nous voulons améliorer notre schéma avec le type Date.

Définir le scalar Date dans la classe
import { Scalar, CustomScalar } from '@nestjs/graphql';
import { Kind, ValueNode } from 'graphql';
@Scalar('Date')
export class DateScalar implements CustomScalar<number, Date> {
description = 'Type scalar personnalisé Date';
parseValue(value: number): Date {
return new Date(value); // valeur du client
}
serialize(value: Date): number {
return value.getTime(); // valeur envoyée au client
}
parseLiteral(ast: ValueNode): Date {
if (ast.kind === Kind.INT) {
return new Date(ast.value);
}
return null;
}
}

Avec cela en place, enregistrez DateScalar en tant que fournisseur.

Enregistrer le scalar Date
@Module({
providers: [DateScalar]
})
export class CommonModule {}

Maintenant, nous pouvons utiliser le scalar Date dans les définitions de types:

scalar Date

Par défaut, la définition TypeScript générée pour tous les scalars est any - ce qui n’est pas particulièrement sûr en termes de types. Mais, vous pouvez configurer la manière dont Nest génère des typings pour vos scalars personnalisés lorsque vous spécifiez comment générer des types:

Configurer la génération de types pour les scalars personnalisés
import { GraphQLDefinitionsFactory } from '@nestjs/graphql';
import { join } from 'path';
const definitionsFactory = new GraphQLDefinitionsFactory();
definitionsFactory.generate({
typePaths: ['./src/**/*.graphql'],
path: join(process.cwd(), 'src/graphql.ts'),
outputAs: 'class',
defaultScalarType: 'unknown',
customScalarTypeMapping: {
DateTime: 'Date',
BigNumber: '_BigNumber',
},
additionalHeader: "import _BigNumber from 'bignumber.js'",
});

Maintenant, étant donné les types scalars personnalisés GraphQL suivants:

scalar DateTime
scalar BigNumber
scalar Payload

Nous verrons maintenant les définitions TypeScript générées suivantes dans src/graphql.ts:

Définitions TypeScript générées
import _BigNumber from 'bignumber.js';
export type DateTime = Date;
export type BigNumber = _BigNumber;
export type Payload = unknown;

Ici, nous avons utilisé la propriété customScalarTypeMapping pour fournir une map des types que nous voulons déclarer pour nos scalars personnalisés. Nous avons également fourni une propriété additionalHeader afin que nous puissions ajouter toutes les importations nécessaires pour ces définitions de type. Enfin, nous avons ajouté un defaultScalarType de 'unknown', de sorte que tous les scalars personnalisés non spécifiés dans customScalarTypeMapping seront aliased à unknown au lieu de any (ce que TypeScript recommande d’utiliser depuis 3.0 pour une sécurité de type accrue).