2026年版 NestJSとGraphQL完全ガイド:スキーマ設計、リゾルバ実装、面接対策
NestJS 11でのGraphQL統合について解説します。コードファーストとスキーマファーストの設計手法、リゾルバパターン、DataLoaderによるN+1問題の解決、認証・認可の実装を学びます。

NestJSのGraphQL統合は、TypeScriptデコレータとGraphQLのクエリ言語を活用した型安全なAPI構築のための体系的なアプローチを提供します。本チュートリアルでは、スキーマファーストとコードファーストの両方のアプローチ、リゾルバパターン、そして2026年の技術面接で採用担当者から問われる質問について解説します。
NestJS 11ではコードファーストGraphQLがデフォルトとなっており、TypeScriptクラスからスキーマを自動生成します。既存の.graphqlファイルやSDLベースのワークフローを持つチーム向けに、スキーマファーストも引き続き利用可能です。
Apollo ServerによるNestJS GraphQLのセットアップ
NestJSは@nestjs/graphqlパッケージを通じてApollo Serverと統合します。セットアップは選択したアプローチによって異なります。コードファーストではデコレータからSDLを生成し、スキーマファーストでは.graphqlファイルを直接パースします。
import { Module } from '@nestjs/common';
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'), // Code-first: generates schema
sortSchema: true, // Alphabetical ordering for readability
playground: process.env.NODE_ENV !== 'production', // Disable in prod
introspection: process.env.NODE_ENV !== 'production',
}),
],
})
export class AppModule {}autoSchemaFileオプションはコードファーストモードを有効にします。trueに設定すると、ディスクに書き込まずにメモリ内でスキーマを生成します。これはファイルシステムへのアクセスが制限される可能性があるサーバーレス環境で有用です。
コードファーストデコレータによるGraphQL型の定義
コードファーストでは、@ObjectType()でデコレートされたTypeScriptクラスを使用してGraphQL型を定義します。各フィールドは@Field()を使用してGraphQL型とnull許容性を指定します。
import { ObjectType, Field, ID, Int } from '@nestjs/graphql';
@ObjectType({ description: 'Application user' }) // Description appears in schema docs
export class User {
@Field(() => ID) // Maps to GraphQL ID scalar
id: string;
@Field()
email: string;
@Field({ nullable: true }) // Optional field in GraphQL
displayName?: string;
@Field(() => Int, { defaultValue: 0 })
postCount: number;
@Field(() => [Post], { nullable: 'itemsAndList' }) // Both list and items can be null
posts?: Post[];
// Fields without @Field() are excluded from GraphQL schema
passwordHash: string;
}nullableオプションは3つの値を受け付けます:true(フィールドがオプショナル)、'items'(リストアイテムがnull可能)、'itemsAndList'(リストとアイテムの両方がnull可能)。この粒度はGraphQLのnull許容セマンティクスに正確に対応しています。
クエリとミューテーションのためのリゾルバ構築
リゾルバは受信したGraphQL操作を処理します。NestJSでは@Resolver()を使用してクラスをリゾルバとしてマークし、メソッドデコレータで操作タイプを指定します。
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) // Binds resolver to User type for field resolution
export class UsersResolver {
constructor(private readonly usersService: UsersService) {}
@Query(() => [User], { name: 'users' }) // Explicit query name
findAll(): Promise<User[]> {
return this.usersService.findAll();
}
@Query(() => User, { nullable: true })
user(@Args('id', { type: () => ID }) id: string): Promise<User | null> {
return this.usersService.findOne(id);
}
@Mutation(() => User)
createUser(@Args('input') input: CreateUserInput): Promise<User> {
return this.usersService.create(input);
}
}@Resolver(() => User)デコレータは@ResolveField()デコレータのコンテキストを確立し、計算されたデータや関連データのフィールドレベル解決を可能にします。
class-validatorを使用した入力型とバリデーション
GraphQL入力型はミューテーションのペイロードを定義します。@InputType()とclass-validatorデコレータを組み合わせることで、スキーマレベルとランタイムの両方でバリデーションが可能になります。
import { InputType, Field } from '@nestjs/graphql';
import { IsEmail, MinLength, IsOptional, Matches } from 'class-validator';
@InputType()
export class CreateUserInput {
@Field()
@IsEmail({}, { message: 'Invalid email format' })
email: string;
@Field()
@MinLength(8, { message: 'Password must be at least 8 characters' })
@Matches(/[A-Z]/, { message: 'Password must contain uppercase letter' })
password: string;
@Field({ nullable: true })
@IsOptional()
@MinLength(2)
displayName?: string;
}main.tsでValidationPipeをグローバルに有効にすることでバリデーションを実行できます。GraphQLエラーはextensionsフィールドにバリデーションメッセージを含み、クライアント互換性を維持します。
Node.js / NestJSの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
@ResolveFieldとDataLoaderによる関連データの解決
フィールドリゾルバは型間の関係を処理します。最適化なしでユーザーリストと投稿を取得すると、N+1クエリが発生します。ユーザー取得に1クエリ、各ユーザーの投稿取得にN個のクエリが必要になります。
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<Post[]> {
// DataLoader batches requests: one query for all user IDs
return this.postsLoader.batchByUserId.load(user.id);
}
@ResolveField(() => Int)
async postCount(@Parent() user: User): Promise<number> {
const posts = await this.postsLoader.batchByUserId.load(user.id);
return posts.length;
}
}DataLoaderは単一のGraphQL操作内でリクエストをバッチ処理およびキャッシュします。NestJSでは、@Injectable({ scope: Scope.REQUEST })を使用してDataLoaderをリクエストスコープに設定します。
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 }) // New instance per request
export class PostsLoader {
constructor(private readonly postsService: PostsService) {}
public readonly batchByUserId = new DataLoader<string, Post[]>(
async (userIds: readonly string[]) => {
// Single query: SELECT * FROM posts WHERE user_id IN (...)
const posts = await this.postsService.findByUserIds([...userIds]);
// Map results back to input order
const postsMap = new Map<string, Post[]>();
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クエリを単一のバッチクエリに削減します。これはクライアントがクエリの深さを制御するGraphQL APIにとって重要です。NestJSのアーキテクチャパターンの詳細については、NestJS モジュールと依存性注入の面接質問を参照してください。
リアルタイムデータのためのサブスクリプション
GraphQLサブスクリプションは、WebSocket接続を介してクライアントにデータをプッシュします。NestJSはGraphQL over WebSocketプロトコルを実装するgraphql-wsライブラリを使用します。
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true,
subscriptions: {
'graphql-ws': true, // Modern protocol
'subscriptions-transport-ws': false, // Deprecated legacy protocol
},
}),import { Resolver, Subscription } from '@nestjs/graphql';
import { PubSub } from 'graphql-subscriptions';
import { Post } from './post.entity';
const pubSub = new PubSub(); // Use Redis PubSub for multi-instance deployments
@Resolver(() => Post)
export class PostsResolver {
@Subscription(() => Post, {
filter: (payload, variables) =>
payload.postCreated.userId === variables.userId, // Client-side filtering
})
postCreated() {
return pubSub.asyncIterableIterator('postCreated');
}
@Mutation(() => Post)
async createPost(@Args('input') input: CreatePostInput): Promise<Post> {
const post = await this.postsService.create(input);
pubSub.publish('postCreated', { postCreated: post }); // Trigger subscription
return post;
}
}複数インスタンスにわたる本番デプロイメントでは、クラスタ全体でイベントをブロードキャストするために、インメモリのPubSubをgraphql-redis-subscriptionsに置き換えます。
GraphQLでの認証と認可
NestJSのGuardsはGraphQLリゾルバとシームレスに連携します。実行コンテキストはRESTとは異なるため、GqlExecutionContextを使用してリクエストを抽出します。
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; // Extract request from GraphQL context
}
}Guardsはリゾルバレベルまたはメソッドレベルで適用できます。フィールドレベルの認可は、ユーザーロールに基づく条件分岐ロジックと@ResolveField()を使用します。
@UseGuards(GqlAuthGuard)
@Resolver(() => User)
export class UsersResolver {
@Query(() => User)
me(@CurrentUser() user: User): User {
return user; // Return authenticated user
}
@ResolveField(() => String, { nullable: true })
email(@Parent() user: User, @CurrentUser() currentUser: User): string | null {
// Only return email if viewing own profile or admin
if (user.id === currentUser.id || currentUser.role === 'ADMIN') {
return user.email;
}
return null;
}
}複雑な認可については、GraphQL ShieldまたはNestJSの組み込みCASL統合を検討してください。NestJS GuardsとInterceptorsの記事で、これらのパターンを詳しく解説しています。
よくあるNestJS GraphQL面接質問
NestJSポジションの技術面接では、GraphQL固有の質問が頻繁に出題されます。以下は採用担当者が評価するパターンです。
Q: NestJSはGraphQLにおけるN+1問題をどのように処理しますか?
DataLoaderは単一リクエスト内でフィールドリゾルバの呼び出しをバッチ処理します。複数の親オブジェクトが同じフィールドをリクエストすると、DataLoaderはすべてのキーを収集し、1つのバッチクエリを実行して結果を分配します。ローダーはリクエストスコープでなければならず、リクエスト間のキャッシュの問題を防ぎます。
Q: NestJS GraphQLにおけるコードファーストとスキーマファーストの違いは何ですか?
コードファーストは実行時にTypeScriptデコレータからGraphQLスキーマを生成し、型とスキーマを自動的に同期させます。スキーマファーストは.graphql SDLファイルをパースし、手動の型定義が必要です。コードファーストはTypeScriptネイティブなチームに適しており、スキーマファーストはスキーマがフロントエンドとバックエンドチーム間のコントラクトである場合に適しています。
Q: フィールドレベルの権限をどのように実装しますか?
3つのアプローチがあります:(1)ユーザーコンテキストに基づく条件付きリターンを持つ@ResolveField()、(2)フィールド解決前に権限をチェックするカスタムデコレータ、(3)ディレクティブトランスフォーマーによって処理される@auth(requires: ADMIN)のようなスキーマディレクティブ。最初のアプローチが最も柔軟性を提供し、ディレクティブは最もクリーンなスキーマドキュメントを提供します。
Q: NestJSにおけるGraphQLコンテキストを説明してください。
コンテキストオブジェクトはリクエスト内のすべてのリゾルバを通過します。NestJSはデフォルトでHTTPリクエストを含めます。カスタムコンテキストはGraphQLModule.forRoot()のcontextオプションで設定します。DataLoaderインスタンス、認証済みユーザー、またはデータベース接続の追加に有用です。
Q: サブスクリプションは複数サーバーインスタンス間でどのようにスケールしますか?
インメモリPubSubは単一インスタンスのデプロイメントでのみ機能します。マルチインスタンスアーキテクチャでは外部ブローカーが必要で、Redis PubSubが標準的です。各サーバーインスタンスはRedisチャンネルをサブスクライブし、いずれかのインスタンスがイベントを発行すると、すべてのインスタンスがそれを受信して接続されたWebSocketクライアントにプッシュします。
NestJSの面接準備については、リクエストライフサイクルパターンをカバーするミドルウェアとInterceptorsモジュールも参照してください。
まとめ
- NestJS GraphQLはコードファーストとスキーマファーストの両アプローチをサポートしており、コードファーストはTypeScriptプロジェクトを簡素化し、スキーマファーストはSDL駆動のワークフローに適しています
- DataLoaderは単一操作内でフィールドリゾルバリクエストをバッチ処理することでN+1クエリを排除します
- リクエストスコープのDataLoaderインスタンスは同時リクエスト間のキャッシュ汚染を防ぎます
GqlExecutionContextはNestJS GuardsとGraphQLのリゾルバコンテキストを橋渡しします- 本番サブスクリプションではマルチインスタンスイベント配信のためにRedis PubSubが必要です
- フィールドレベルの認可は
@ResolveField()とユーザーコンテキストチェックを組み合わせます
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。
共有
関連記事

NestJSとTypeORM 2026年版:マイグレーション、リレーション、面接対策の完全ガイド
NestJSとTypeORMを組み合わせたバックエンド開発の実践的ガイド。マイグレーション管理、リレーション設計、トランザクション処理、そして技術面接で頻出する質問と回答を解説します。

Node.js 24の注目機能:URLPattern、パーミッションモデル、面接対策まで徹底解説(2026年版)
Node.js 24 LTS(Krypton)の主要な新機能であるURLPattern、パーミッションモデル、明示的リソース管理について、実践的なコード例と面接対策を交えて詳しく解説する。

2026年版 NestJS マイクロサービス:アーキテクチャ、gRPC、面接対策ガイド
NestJS マイクロサービスのアーキテクチャ設計、gRPC トランスポートの構成、ストリーミングパターン、信頼性パターン、技術面接の頻出質問を体系的に解説します。