GraphQL5 min read

Scalarlar

GraphQL object type ning nomi va fieldlari bor, ammo oxir-oqibat bu fieldlar aniq ma'lumotga yechilishi kerak. Bu yerda scalar turlar kerak bo'ladi: ular queryning barglarini ifoda

GraphQL object type ning nomi va fieldlari bor, ammo oxir-oqibat bu fieldlar aniq ma'lumotga yechilishi kerak. Bu yerda scalar turlar kerak bo'ladi: ular queryning barglarini ifodalaydi (batafsil bu yerda). GraphQL quyidagi default turlarni o'z ichiga oladi: Int, Float, String, Boolean va ID. Bu built-in turlardan tashqari, sizga custom atomic data turlarini (masalan, Date) qo'llab-quvvatlash kerak bo'lishi mumkin.

Code first

Code-first yondashuvi besh ta scalar bilan keladi, ulardan uchtasi mavjud GraphQL turlari uchun oddiy aliaslardir.

  • ID (GraphQLID uchun alias) - noyob identifikatorni ifodalaydi, ko'pincha obyektni qayta fetch qilish yoki cache kaliti sifatida ishlatiladi
  • Int (GraphQLInt uchun alias) - signed 32-bit integer
  • Float (GraphQLFloat uchun alias) - signed double-precision floating-point qiymat
  • GraphQLISODateTime - UTCdagi date-time satri (default holatda Date turini ifodalash uchun ishlatiladi)
  • GraphQLTimestamp - UNIX epoch boshidan millisekundlar soni sifatida sana va vaqtni ifodalovchi signed integer

GraphQLISODateTime (masalan, 2019-12-03T09:54:33Z) default holatda Date turini ifodalash uchun ishlatiladi. Buning o'rniga GraphQLTimestampdan foydalanish uchun buildSchemaOptions obyektida dateScalarModeni 'timestamp' qilib o'rnating:

TypeScript
1GraphQLModule.forRoot({
2  buildSchemaOptions: {
3    dateScalarMode: 'timestamp',
4  }
5}),

Xuddi shuningdek, GraphQLFloat default holatda number turini ifodalash uchun ishlatiladi. Buning o'rniga GraphQLIntdan foydalanish uchun buildSchemaOptions obyektida numberScalarModeni 'integer' qilib o'rnating:

TypeScript
1GraphQLModule.forRoot({
2  buildSchemaOptions: {
3    numberScalarMode: 'integer',
4  }
5}),

Bundan tashqari, custom scalarlar yaratishingiz mumkin.

Default scalarni override qilish

Date scalarining custom implementatsiyasini yaratish uchun yangi klass yarating.

TypeScript
1import { Scalar, CustomScalar } from '@nestjs/graphql';
2import { Kind, ValueNode } from 'graphql';
3
4@Scalar('Date', () => Date)
5export class DateScalar implements CustomScalar<number, Date> {
6  description = 'Date custom scalar type';
7
8  parseValue(value: number): Date {
9    return new Date(value); // value from the client
10  }
11
12  serialize(value: Date): number {
13    return value.getTime(); // value sent to the client
14  }
15
16  parseLiteral(ast: ValueNode): Date {
17    if (ast.kind === Kind.INT) {
18      return new Date(ast.value);
19    }
20    return null;
21  }
22}

Shu bilan, DateScalarni provider sifatida ro'yxatdan o'tkazing.

TypeScript
1@Module({
2  providers: [DateScalar],
3})
4export class CommonModule {}

Endi klasslarimizda Date turidan foydalanishimiz mumkin.

TypeScript
1@Field()
2creationDate: Date;

Custom scalarni import qilish

Custom scalardan foydalanish uchun uni import qilib resolver sifatida ro'yxatdan o'tkazing. Namoyish uchun graphql-type-json paketidan foydalanamiz. Bu npm paket JSON GraphQL scalar turini belgilaydi.

Avval paketni o'rnating:

Terminal
1$ npm i --save graphql-type-json

Paket o'rnatilgach, forRoot() metodiga custom resolver uzatamiz:

TypeScript
1import GraphQLJSON from 'graphql-type-json';
2
3@Module({
4  imports: [
5    GraphQLModule.forRoot({
6      resolvers: { JSON: GraphQLJSON },
7    }),
8  ],
9})
10export class AppModule {}

Endi klasslarimizda JSON turidan foydalanishimiz mumkin.

TypeScript
1@Field(() => GraphQLJSON)
2info: JSON;

Foydali scalarlar to'plami uchun graphql-scalars paketiga qarang.

Custom scalar yaratish

Custom scalarni aniqlash uchun yangi GraphQLScalarType instansiyasini yarating. Biz UUID custom scalarini yaratamiz.

TypeScript
1const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2
3function validate(uuid: unknown): string | never {
4  if (typeof uuid !== 'string' || !regex.test(uuid)) {
5    throw new Error('invalid uuid');
6  }
7  return uuid;
8}
9
10export const CustomUuidScalar = new GraphQLScalarType({
11  name: 'UUID',
12  description: 'A simple UUID parser',
13  serialize: (value) => validate(value),
14  parseValue: (value) => validate(value),
15  parseLiteral: (ast) => validate(ast.value),
16});

Custom resolverni forRoot() metodiga uzatamiz:

TypeScript
1@Module({
2  imports: [
3    GraphQLModule.forRoot({
4      resolvers: { UUID: CustomUuidScalar },
5    }),
6  ],
7})
8export class AppModule {}

Endi klasslarimizda UUID turidan foydalanishimiz mumkin.

TypeScript
1@Field(() => CustomUuidScalar)
2uuid: string;

Schema first

Custom scalarni aniqlash uchun (scalarlarga oid batafsil ma'lumot bu yerda), type ta'rifi va maxsus resolver yarating. Bu yerda (rasmiy hujjatlardagi kabi) namoyish uchun graphql-type-json paketidan foydalanamiz. Bu npm paket JSON GraphQL scalar turini belgilaydi.

Avval paketni o'rnating:

Terminal
1$ npm i --save graphql-type-json

Paket o'rnatilgach, forRoot() metodiga custom resolver uzatamiz:

TypeScript
1import GraphQLJSON from 'graphql-type-json';
2
3@Module({
4  imports: [
5    GraphQLModule.forRoot({
6      typePaths: ['./**/*.graphql'],
7      resolvers: { JSON: GraphQLJSON },
8    }),
9  ],
10})
11export class AppModule {}

Endi type ta'riflarimizda JSON scalaridan foydalanamiz:

Graphql
1scalar JSON
2
3type Foo {
4  field: JSON
5}

Scalar turini aniqlashning yana bir usuli - oddiy klass yaratish. Masalan, schemamizni Date turi bilan boyitmoqchi bo'lsak.

TypeScript
1import { Scalar, CustomScalar } from '@nestjs/graphql';
2import { Kind, ValueNode } from 'graphql';
3
4@Scalar('Date')
5export class DateScalar implements CustomScalar<number, Date> {
6  description = 'Date custom scalar type';
7
8  parseValue(value: number): Date {
9    return new Date(value); // value from the client
10  }
11
12  serialize(value: Date): number {
13    return value.getTime(); // value sent to the client
14  }
15
16  parseLiteral(ast: ValueNode): Date {
17    if (ast.kind === Kind.INT) {
18      return new Date(ast.value);
19    }
20    return null;
21  }
22}

Shu bilan, DateScalarni provider sifatida ro'yxatdan o'tkazing.

TypeScript
1@Module({
2  providers: [DateScalar],
3})
4export class CommonModule {}

Endi type ta'riflarimizda Date scalaridan foydalanamiz.

Graphql
1scalar Date

Default holatda barcha scalarlar uchun generatsiya qilingan TypeScript ta'rifi any bo'ladi - bu unchalik typesafe emas. Biroq, turlarni qanday generatsiya qilishni belgilayotganda custom scalarlar uchun Nest typingsni qanday generatsiya qilishini sozlashingiz mumkin:

TypeScript
1import { GraphQLDefinitionsFactory } from '@nestjs/graphql';
2import { join } from 'path';
3
4const definitionsFactory = new GraphQLDefinitionsFactory();
5
6definitionsFactory.generate({
7  typePaths: ['./src/**/*.graphql'],
8  path: join(process.cwd(), 'src/graphql.ts'),
9  outputAs: 'class',
10  defaultScalarType: 'unknown',
11  customScalarTypeMapping: {
12    DateTime: 'Date',
13    BigNumber: '_BigNumber',
14  },
15  additionalHeader: "import _BigNumber from 'bignumber.js'",
16});
Hint

Muqobil ravishda type reference ham berishingiz mumkin, masalan: DateTime: Date. Bu holatda GraphQLDefinitionsFactory ko'rsatilgan tur (Date.name)ning name xossasini ajratib olib TS ta'riflarini generatsiya qiladi. Eslatma: built-in bo'lmagan turlar (custom turlar) uchun import bayonotini qo'shish kerak.

Endi quyidagi GraphQL custom scalar turlarini olamiz:

Graphql
1scalar DateTime
2scalar BigNumber
3scalar Payload

Endi src/graphql.ts faylida quyidagi generatsiya qilingan TypeScript ta'riflarini ko'ramiz:

TypeScript
1import _BigNumber from 'bignumber.js';
2
3export type DateTime = Date;
4export type BigNumber = _BigNumber;
5export type Payload = unknown;

Bu yerda customScalarTypeMapping xossasidan foydalanib custom scalarlar uchun istalgan turlar xaritasini berdik. Shuningdek, bu ta'riflar uchun kerakli importlarni qo'shish uchun additionalHeader xossasini ham berdik. Nihoyat, defaultScalarTypeni 'unknown' qilib o'rnatdik, shunda customScalarTypeMappingda ko'rsatilmagan har qanday custom scalar any o'rniga unknownga map qilinadi (TypeScript 3.0 dan beri TypeScript tavsiyasi type safety uchun).

Hint

_BigNumberni bignumber.jsdan import qilganimizga e'tibor bering; bu circular type referencesdan qochish uchun.