NestJS — Node.js للمؤسسات

أساسيات GraphQL بنهج Code-First

16 دقيقة الدرس 33 من 48

أساسيات GraphQL بنهج Code-First

يدعم NestJS نهجَين لبناء واجهة GraphQL: schema-first (تكتب SDL وتولّد الأنواع) وcode-first (تكتب مُزخرفات TypeScript ويُولَّد المخطط تلقائيًا). يحتفظ نهج code-first بمصدر حقيقة واحد — كلاسات TypeScript — ويُزيل الصداع الناتج عن مزامنة ملفات SDL مع أنواع TypeScript. يغطّي هذا الدرس المكوّنات الأساسية: أنواع الكائنات، والحلّالات، والاستعلامات، والطفرات، وأنواع المدخلات.

التثبيت وإعداد الوحدة

npm install @nestjs/graphql @nestjs/apollo @apollo/server graphql

سجّل GraphQLModule في AppModule الجذري باستخدام خيار autoSchemaFile الذي يُشير إلى المسار الذي يجب أن يكتب فيه NestJS ملف SDL المُولَّد (أو مرّر true للاحتفاظ به في الذاكرة فقط):

import { GraphQLModule } from '@nestjs/graphql'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { join } from 'path'; @Module({ imports: [ GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, autoSchemaFile: join(process.cwd(), 'src/schema.gql'), sortSchema: true, }), ], }) export class AppModule {}

تعريف نوع كائن بـ @ObjectType و @Field

أي كلاس مُزخرَف بـ @ObjectType() يصبح نوع GraphQL. كل خاصية تريد كشفها في المخطط يجب أن تُعلَّم بـ @Field(). يتولّى استنتاج TypeScript المقياسات البدائية تلقائيًا، لكن يجب أن تكون صريحًا مع الحقول القابلة للقيمة الفارغة والمصفوفات:

import { ObjectType, Field, Int, ID } from '@nestjs/graphql'; @ObjectType() export class Post { @Field(() => ID) id: string; @Field() title: string; @Field({ nullable: true }) body?: string; @Field(() => Int) viewCount: number; @Field() published: boolean; }
استخدم دائمًا محلّلات الأنواع بصيغة الدالة السهمية للمقياسات غير البدائية. اكتب @Field(() => Int) بدلًا من الاعتماد على الانعكاس مع الأرقام — إذ يُعيَّن number في TypeScript إلى كلٍّ من Int وFloat، لذا يجب أن تكون صريحًا لتجنّب الغموض.

إنشاء حلّال بـ @Query و @Mutation

الكلاس المُزخرَف بـ @Resolver() (أو @Resolver(() => Post) لتحديد النطاق) يكشف معالجات الاستعلامات والطفرات. تُعلن @Query() عملية قراءة؛ وتُعلن @Mutation() عملية كتابة. تُحقَن الوسيطات بـ @Args():

import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql'; import { Post } from './post.type'; import { PostsService } from './posts.service'; import { CreatePostInput } from './create-post.input'; @Resolver(() => Post) export class PostsResolver { constructor(private readonly postsService: PostsService) {} @Query(() => [Post], { name: 'posts' }) findAll(): Promise<Post[]> { return this.postsService.findAll(); } @Query(() => Post, { name: 'post', nullable: true }) findOne(@Args('id', { type: () => Int }) id: number): Promise<Post | null> { return this.postsService.findOne(id); } @Mutation(() => Post) createPost(@Args('input') input: CreatePostInput): Promise<Post> { return this.postsService.create(input); } }

أنواع المدخلات بـ @InputType

تتلقّى الطفرات بيانات منظّمة عبر أنواع المدخلات. زخرف كلاسًا بـ @InputType() وحقوله بـ @Field() — يُولّد NestJS كتلة input في مخطط GraphQL. تتكامل أنواع المدخلات بسلاسة مع مُزخرفات class-validator:

import { InputType, Field } from '@nestjs/graphql'; import { IsString, IsOptional, MinLength } from 'class-validator'; @InputType() export class CreatePostInput { @Field() @IsString() @MinLength(3) title: string; @Field({ nullable: true }) @IsOptional() @IsString() body?: string; }
اقرن أنواع المدخلات بـ ValidationPipe. فعّل ValidationPipe بشكل عام (أو على سياق GraphQL) حتى تُطبَّق مُزخرفات class-validator على كلاسات @InputType تلقائيًا قبل تشغيل الحلّال.

توليد المخطط التلقائي

بمجرد إعداد الوحدة، يعكس NestJS جميع الحلّالات وأنواع الكائنات/المدخلات المُسجَّلة عند البدء ويكتب ملف schema.gql. تشغيل التطبيق يُنتج شيئًا كالتالي:

type Post { body: String id: ID! published: Boolean! title: String! viewCount: Int! } input CreatePostInput { body: String title: String! } type Query { post(id: Int!): Post posts: [Post!]! } type Mutation { createPost(input: CreatePostInput!): Post! }
لا تحرّر schema.gql يدويًا أبدًا. إنّه قطعة مُولَّدة — أي تعديلات يدوية تُستبدَل عند بدء التطبيق التالي. يجب أن تحدث جميع تغييرات المخطط في ملفات مصدر مُزخرفات TypeScript.

الخلاصة

يحتفظ نهج code-first لـ GraphQL في NestJS بـ TypeScript كمصدر حقيقة وحيد. زخرف كلاسات النماذج بـ @ObjectType/@Field، واكتب كلاسات الحلّالات بـ @Query/@Mutation/@Args، وعرّف أشكال المدخلات بـ @InputType/@Field. يُولّد GraphQLModule.forRoot مع autoSchemaFile SDL ويُديره تلقائيًا، لذا لن تكتب أو تزامن ملفات مخطط GraphQL خام يدويًا.