# 2026'da NestJS ve GraphQL: Şemalar, Resolver'lar ve Mülakat Soruları > NestJS GraphQL entegrasyonu hakkında kapsamlı rehber: schema-first ve code-first yaklaşımları, resolver'lar, DataLoader ve 2026 mülakat soruları. - Published: 2026-07-25 - Updated: 2026-07-25 - Author: SharpSkill - Tags: nestjs, graphql, node.js, typescript, api - Reading time: 5 min --- NestJS GraphQL entegrasyonu, TypeScript dekoratörleri ve GraphQL sorgu dilini kullanan tip güvenli API'ler oluşturmak için yapılandırılmış bir yaklaşım sunar. Bu rehber, schema-first ve code-first yaklaşımlarını, resolver kalıplarını ve 2026 yılında işe alım müdürlerinin sorduğu mülakat sorularını kapsar. > **Code-First vs Schema-First** > > NestJS 11 varsayılan olarak code-first GraphQL kullanır ve şemayı TypeScript sınıflarından oluşturur. Schema-first yaklaşımı, mevcut `.graphql` dosyaları veya SDL tabanlı iş akışları olan ekipler için hala kullanılabilir durumdadır. ## Apollo Server ile NestJS GraphQL Kurulumu NestJS, `@nestjs/graphql` paketi aracılığıyla [Apollo Server](https://www.apollographql.com/docs/apollo-server/) ile entegre olur. Kurulum, seçilen yaklaşıma göre farklılık gösterir — code-first dekoratörlerden SDL oluşturur, schema-first ise `.graphql` dosyalarını doğrudan ayrıştırır. ```typescript // app.module.ts import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { join } from 'path'; @Module({ imports: [ GraphQLModule.forRoot({ driver: ApolloDriver, autoSchemaFile: join(process.cwd(), 'src/schema.gql'), // Code-first: şema oluşturur sortSchema: true, // Okunabilirlik için alfabetik sıralama playground: process.env.NODE_ENV !== 'production', // Üretimde devre dışı bırak introspection: process.env.NODE_ENV !== 'production', }), ], }) export class AppModule {} ``` `autoSchemaFile` seçeneği code-first modunu etkinleştirir. `true` olarak ayarlamak şemayı diske yazmadan bellekte oluşturur — dosya sistemi erişiminin kısıtlı olabileceği serverless dağıtımları için kullanışlıdır. ## Code-First Dekoratörleri ile GraphQL Tiplerini Tanımlama Code-first, GraphQL tiplerini `@ObjectType()` ile dekore edilmiş TypeScript sınıfları kullanarak tanımlar. Her alan, GraphQL tipini ve nullable özelliğini belirtmek için `@Field()` kullanır. ```typescript // user.entity.ts import { ObjectType, Field, ID, Int } from '@nestjs/graphql'; @ObjectType({ description: 'Uygulama kullanıcısı' }) // Açıklama şema belgelerinde görünür export class User { @Field(() => ID) // GraphQL ID skalerine eşler id: string; @Field() email: string; @Field({ nullable: true }) // GraphQL'de isteğe bağlı alan displayName?: string; @Field(() => Int, { defaultValue: 0 }) postCount: number; @Field(() => [Post], { nullable: 'itemsAndList' }) // Hem liste hem de öğeler null olabilir posts?: Post[]; // @Field() olmayan alanlar GraphQL şemasından hariç tutulur passwordHash: string; } ``` `nullable` seçeneği üç değer kabul eder: `true` (alan isteğe bağlı), `'items'` (liste öğeleri null olabilir) ve `'itemsAndList'` (hem liste hem de öğeler null olabilir). Bu ayrıntı düzeyi, [GraphQL'in nullability semantiği](https://graphql.org/learn/schema/#lists-and-non-null) ile tam olarak eşleşir. ## Sorgular ve Mutasyonlar için Resolver Oluşturma Resolver'lar gelen GraphQL işlemlerini yönetir. NestJS, bir sınıfı resolver olarak işaretlemek için `@Resolver()` kullanır ve metot dekoratörleri işlem tiplerini belirtir. ```typescript // users.resolver.ts import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql'; import { User } from './user.entity'; import { UsersService } from './users.service'; import { CreateUserInput } from './dto/create-user.input'; @Resolver(() => User) // Alan çözümlemesi için resolver'ı User tipine bağlar export class UsersResolver { constructor(private readonly usersService: UsersService) {} @Query(() => [User], { name: 'users' }) // Açık sorgu adı findAll(): Promise { return this.usersService.findAll(); } @Query(() => User, { nullable: true }) user(@Args('id', { type: () => ID }) id: string): Promise { return this.usersService.findOne(id); } @Mutation(() => User) createUser(@Args('input') input: CreateUserInput): Promise { return this.usersService.create(input); } } ``` `@Resolver(() => User)` dekoratörü, `@ResolveField()` dekoratörleri için bağlam oluşturur ve hesaplanmış veya ilişkili veriler için alan düzeyinde çözümlemeyi mümkün kılar. ## Input Tipleri ve class-validator ile Doğrulama GraphQL input tipleri mutasyon payload'larını tanımlar. `@InputType()`'ı [class-validator](https://github.com/typestack/class-validator) dekoratörleri ile birleştirmek, şema düzeyinde ve çalışma zamanı doğrulamasını etkinleştirir. ```typescript // dto/create-user.input.ts import { InputType, Field } from '@nestjs/graphql'; import { IsEmail, MinLength, IsOptional, Matches } from 'class-validator'; @InputType() export class CreateUserInput { @Field() @IsEmail({}, { message: 'Geçersiz e-posta formatı' }) email: string; @Field() @MinLength(8, { message: 'Şifre en az 8 karakter olmalıdır' }) @Matches(/[A-Z]/, { message: 'Şifre büyük harf içermelidir' }) password: string; @Field({ nullable: true }) @IsOptional() @MinLength(2) displayName?: string; } ``` `main.ts` dosyasına `ValidationPipe` ekleyerek doğrulamayı global olarak etkinleştirin. GraphQL hataları, istemci uyumluluğunu koruyarak `extensions` alanında doğrulama mesajlarını içerir. ## @ResolveField ve DataLoader ile İlişkili Verileri Çözümleme Alan resolver'ları tipler arasındaki ilişkileri yönetir. Optimizasyon olmadan, kullanıcı listesini gönderileriyle birlikte getirmek N+1 sorgu tetikler — kullanıcılar için bir sorgu, ardından her kullanıcı için gönderiler için bir sorgu. ```typescript // users.resolver.ts import { Resolver, ResolveField, Parent } from '@nestjs/graphql'; import { User } from './user.entity'; import { Post } from '../posts/post.entity'; import { PostsLoader } from '../posts/posts.loader'; @Resolver(() => User) export class UsersResolver { constructor(private readonly postsLoader: PostsLoader) {} @ResolveField(() => [Post]) async posts(@Parent() user: User): Promise { // DataLoader istekleri gruplar: tüm kullanıcı ID'leri için tek sorgu return this.postsLoader.batchByUserId.load(user.id); } @ResolveField(() => Int) async postCount(@Parent() user: User): Promise { const posts = await this.postsLoader.batchByUserId.load(user.id); return posts.length; } } ``` DataLoader, tek bir GraphQL işlemi içinde istekleri gruplar ve önbelleğe alır. NestJS'de DataLoader, `@Injectable({ scope: Scope.REQUEST })` kullanılarak istek kapsamına alınmalıdır. ```typescript // posts/posts.loader.ts import { Injectable, Scope } from '@nestjs/common'; import * as DataLoader from 'dataloader'; import { PostsService } from './posts.service'; import { Post } from './post.entity'; @Injectable({ scope: Scope.REQUEST }) // Her istek için yeni örnek export class PostsLoader { constructor(private readonly postsService: PostsService) {} public readonly batchByUserId = new DataLoader( async (userIds: readonly string[]) => { // Tek sorgu: SELECT * FROM posts WHERE user_id IN (...) const posts = await this.postsService.findByUserIds([...userIds]); // Sonuçları giriş sırasına göre eşle const postsMap = new Map(); posts.forEach(post => { const existing = postsMap.get(post.userId) || []; postsMap.set(post.userId, [...existing, post]); }); return userIds.map(id => postsMap.get(id) || []); } ); } ``` DataLoader, N+1 sorgularını tek bir toplu sorguya indirger — istemcilerin sorgu derinliğini kontrol ettiği GraphQL API'leri için kritik öneme sahiptir. NestJS mimari kalıpları hakkında daha fazla bilgi için [NestJS Modülleri ve Dependency Injection mülakat soruları](/technologies/node-nestjs/interview-questions/nestjs-modules-di) modülüne bakılabilir. ## Gerçek Zamanlı Veri için Subscription'lar GraphQL subscription'ları, WebSocket bağlantıları üzerinden istemcilere veri iletir. NestJS, [GraphQL over WebSocket protokolünü](https://github.com/enisdenjo/graphql-ws) uygulayan `graphql-ws` kütüphanesini kullanır. ```typescript // app.module.ts - Subscription'ları etkinleştir GraphQLModule.forRoot({ driver: ApolloDriver, autoSchemaFile: true, subscriptions: { 'graphql-ws': true, // Modern protokol 'subscriptions-transport-ws': false, // Kullanımdan kaldırılmış eski protokol }, }), ``` ```typescript // posts.resolver.ts import { Resolver, Subscription } from '@nestjs/graphql'; import { PubSub } from 'graphql-subscriptions'; import { Post } from './post.entity'; const pubSub = new PubSub(); // Çoklu örnek dağıtımları için Redis PubSub kullanın @Resolver(() => Post) export class PostsResolver { @Subscription(() => Post, { filter: (payload, variables) => payload.postCreated.userId === variables.userId, // İstemci tarafı filtreleme }) postCreated() { return pubSub.asyncIterableIterator('postCreated'); } @Mutation(() => Post) async createPost(@Args('input') input: CreatePostInput): Promise { const post = await this.postsService.create(input); pubSub.publish('postCreated', { postCreated: post }); // Subscription'ı tetikle return post; } } ``` Birden fazla örnek üzerinde üretim dağıtımları için, bellek içi `PubSub` yerine olayları küme genelinde yayınlamak üzere `graphql-redis-subscriptions` kullanılmalıdır. ## GraphQL'de Kimlik Doğrulama ve Yetkilendirme NestJS Guard'ları GraphQL resolver'ları ile sorunsuz çalışır. Yürütme bağlamı REST'ten farklıdır — isteği çıkarmak için `GqlExecutionContext` kullanılmalıdır. ```typescript // guards/gql-auth.guard.ts import { Injectable, ExecutionContext } from '@nestjs/common'; import { GqlExecutionContext } from '@nestjs/graphql'; import { AuthGuard } from '@nestjs/passport'; @Injectable() export class GqlAuthGuard extends AuthGuard('jwt') { getRequest(context: ExecutionContext) { const ctx = GqlExecutionContext.create(context); return ctx.getContext().req; // GraphQL bağlamından isteği çıkar } } ``` Guard'lar resolver veya metot düzeyinde uygulanır. Alan düzeyinde yetkilendirme, kullanıcı rollerine dayalı koşullu mantık ile `@ResolveField()` kullanır. ```typescript // users.resolver.ts @UseGuards(GqlAuthGuard) @Resolver(() => User) export class UsersResolver { @Query(() => User) me(@CurrentUser() user: User): User { return user; // Kimliği doğrulanmış kullanıcıyı döndür } @ResolveField(() => String, { nullable: true }) email(@Parent() user: User, @CurrentUser() currentUser: User): string | null { // E-postayı yalnızca kendi profili görüntülenirken veya admin ise döndür if (user.id === currentUser.id || currentUser.role === 'ADMIN') { return user.email; } return null; } } ``` Karmaşık yetkilendirme için [GraphQL Shield](https://github.com/dimatill/graphql-shield) veya NestJS'in yerleşik CASL entegrasyonu değerlendirilebilir. [NestJS Guard'lar ve Interceptor'lar](/blog/node-nestjs/nestjs-guards-interceptors-modular-architecture) makalesi bu kalıpları derinlemesine ele alır. ## Yaygın NestJS GraphQL Mülakat Soruları NestJS pozisyonları için teknik mülakatlar sıklıkla GraphQL'e özgü sorular içerir. İşe alım müdürlerinin değerlendirdiği kalıplar şunlardır. **S: NestJS GraphQL'deki N+1 problemini nasıl çözer?** DataLoader, tek bir istek içinde alan resolver çağrılarını gruplar. Birden fazla üst nesne aynı alanı istediğinde, DataLoader tüm anahtarları toplar, tek bir toplu sorgu yürütür ve sonuçları dağıtır. Loader, istekler arası önbellek sorunlarını önlemek için istek kapsamında olmalıdır. **S: NestJS GraphQL'de code-first ve schema-first arasındaki fark nedir?** Code-first, GraphQL şemasını çalışma zamanında TypeScript dekoratörlerinden oluşturur ve tipleri ile şemayı otomatik olarak senkronize tutar. Schema-first, `.graphql` SDL dosyalarını ayrıştırır ve manuel tip tanımları gerektirir. Code-first, TypeScript-native ekipler için uygundur; schema-first, şemanın frontend ve backend ekipleri arasında sözleşme olduğu durumlarda daha iyi çalışır. **S: Alan düzeyinde izinleri nasıl uygularsınız?** Üç yaklaşım mevcuttur: (1) Kullanıcı bağlamına dayalı koşullu dönüşlerle `@ResolveField()`, (2) Alan çözümlemesinden önce izinleri kontrol eden özel dekoratörler, (3) Direktif dönüştürücü tarafından işlenen `@auth(requires: ADMIN)` gibi şema direktifleri. İlk yaklaşım en fazla esnekliği sunar; direktifler en temiz şema belgelerini sağlar. **S: NestJS'de GraphQL bağlamını açıklayın.** Bağlam nesnesi, bir istek içindeki tüm resolver'lardan geçer. NestJS varsayılan olarak HTTP isteği ile doldurur. Özel bağlam, `GraphQLModule.forRoot()` içinde `context` seçeneği ile yapılandırılır — DataLoader örnekleri, kimliği doğrulanmış kullanıcı veya veritabanı bağlantıları eklemek için kullanışlıdır. **S: Subscription'lar birden fazla sunucu örneğinde nasıl ölçeklenir?** Bellek içi PubSub yalnızca tek örnekli dağıtımlar için çalışır. Çok örnekli mimariler harici bir aracı gerektirir — Redis PubSub standarttır. Her sunucu örneği Redis kanallarına abone olur; herhangi bir örnek bir olay yayınladığında, tüm örnekler bunu alır ve bağlı WebSocket istemcilerine iletir. Ek NestJS mülakat hazırlığı için, istek yaşam döngüsü kalıplarını kapsayan [Middleware ve Interceptor'lar](/technologies/node-nestjs/interview-questions/middleware-interceptors) modülüne bakılabilir. ## Sonuç - NestJS GraphQL hem code-first hem de schema-first yaklaşımlarını destekler — code-first TypeScript projelerini basitleştirir, schema-first SDL odaklı iş akışlarına uygundur - DataLoader, tek bir işlem içinde alan resolver isteklerini gruplayarak N+1 sorgularını ortadan kaldırır - İstek kapsamlı DataLoader örnekleri, eşzamanlı istekler arasında önbellek kirliliğini önler - `GqlExecutionContext`, NestJS Guard'larını GraphQL resolver bağlamı ile köprüler - Üretim subscription'ları, çok örnekli olay dağıtımı için Redis PubSub gerektirir - Alan düzeyinde yetkilendirme, `@ResolveField()` ile kullanıcı bağlam kontrollerini birleştirir --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/node-nestjs/nestjs-graphql-schemas-resolvers-tutorial