NestJS面接:Guards、Interceptors、モジュラーアーキテクチャ
Guards、Interceptors、モジュラーアーキテクチャに関するNestJS技術面接の頻出質問を、具体的なTypeScriptコード例と技術解説とともに紹介します。

NestJSの技術面接では、フレームワークの3つの柱に一貫して焦点が当てられます。Guards、Interceptors、そしてモジュラーアーキテクチャです。これらのメカニズムは、よく構造化されたNestJSアプリケーションの背骨を形成し、採用担当者はフレームワークへの実際の習熟度を評価するために用います。
Guardはアクセスを制御し(誰が入れるか)、Interceptorはデータを変換し(何が出入りするか)、モジュラーアーキテクチャはすべてを整理します。これら3つを使いこなすことは、基本的なCRUD操作を超えたNestJSへの深い理解を示します。
NestJSのGuardsの仕組みと面接の典型的なシナリオ
GuardsはCanActivateインターフェースを実装し、リクエストがハンドラーに到達するかを判定します。ミドルウェアと異なり、GuardsはNestJSの実行コンテキスト(ExecutionContext)にアクセスでき、ハンドラーのメタデータに基づいた認可判断を可能にします。
定番の質問: 「ユーザーのロールを検証するGuardを実装してください。」
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
// Retrieve roles defined via the @Roles() decorator
const requiredRoles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
// No roles required means the route is public
if (!requiredRoles) return true;
// Extract user from the HTTP request
const { user } = context.switchToHttp().getRequest();
// Check if the user holds at least one required role
return requiredRoles.some((role) => user.roles?.includes(role));
}
}Reflectorは、カスタムデコレータによって付与されたメタデータを読み取ります。対応する@Roles()デコレータは次のような形になります。
import { SetMetadata } from '@nestjs/common';
// Creates a decorator that attaches roles as metadata
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);面接で強い回答はコードを超えた説明を含みます。実行順序が重要です。Middleware → Guards → Interceptors(前)→ Pipes → Handler → Interceptors(後)→ Exception Filtersという流れは、ほぼすべてのNestJS面接で問われます。
NestJSのInterceptors:リクエストとレスポンスの変換
InterceptorsはNestInterceptorを実装し、RxJSを使ってハンドラーの前後でデータストリームを操作します。その力は、実行パイプラインを表すCallHandlerへのアクセスから生まれます。
よくある質問: 「実行時間を計測するロギングInterceptorを作成してください。」
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private readonly logger = new Logger(LoggingInterceptor.name);
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const { method, url } = request;
const now = Date.now();
// next.handle() invokes the route handler
return next.handle().pipe(
tap(() => {
// Runs AFTER the handler responds
const duration = Date.now() - now;
this.logger.log(`${method} ${url} — ${duration}ms`);
}),
);
}
}RxJSのtapオペレーターは、ストリームを変更せずに観測します。レスポンスを変換するには、適切なオペレーターはmapです。
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
// Generic interface for a standardized API response
interface ApiResponse<T> {
data: T;
timestamp: string;
path: string;
}
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, ApiResponse<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<ApiResponse<T>> {
const request = context.switchToHttp().getRequest();
return next.handle().pipe(
map((data) => ({
data,
timestamp: new Date().toISOString(),
path: request.url,
})),
);
}
}この標準化されたレスポンスパターンは面接で高評価につながります。API設計とレスポンスの一貫性を考える力を示せるからです。
Middleware: 生のHTTP処理(Expressに似ています)。Guard: アクセスに関する真偽の判断。Interceptor: RxJSによるリクエスト/レスポンスストリームの変換。面接でこれら3つを混同するのは即座のレッドフラッグになります。
NestJSのモジュラーアーキテクチャ:スケーラブルなアプリケーションを構築する
モジュラーアーキテクチャは、面接でジュニアとシニアの開発者を分けます。NestJSはモジュールベースの構成を強制しますが、本質的な質問はアーキテクチャ判断を狙います。いつモジュールを作成するか、モジュール間の依存関係をどう管理するかです。
上級の質問: 「適切に疎結合化されたモジュールでNestJSのEコマースアプリケーションをどう構造化しますか。」
import { Module } from '@nestjs/common';
import { OrdersService } from './orders.service';
import { OrdersController } from './orders.controller';
import { PaymentsModule } from '../payments/payments.module';
import { ProductsModule } from '../products/products.module';
@Module({
// Import modules that OrdersModule depends on
imports: [PaymentsModule, ProductsModule],
controllers: [OrdersController],
providers: [OrdersService],
// Expose OrdersService to modules that import OrdersModule
exports: [OrdersService],
})
export class OrdersModule {}面接の典型的な落とし穴は循環依存です。OrdersModuleがPaymentsModuleをインポートし、PaymentsModuleがOrdersModuleをインポートすると、NestJSは起動時に失敗します。緊急脱出手段はforwardRefの使用です。
// Resolving circular dependencies
@Module({
imports: [forwardRef(() => PaymentsModule)],
})
export class OrdersModule {}最も強い回答は、forwardRefは対症療法であって解決策ではないと説明します。正しい修正は、共有モジュール(SharedOrderPaymentModule)を抽出するリファクタリングか、Event Emitterを使って通信を疎結合化することです。
Node.js / NestJSの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
Dynamic Modulesと高度な設定
Dynamic Modulesは、プロジェクト間で再利用可能な設定可能なモジュールを作成します。このパターンはシニアレベルの面接で登場します。
質問: 「動的オプションを持つ設定可能なキャッシュモジュールを作成してください。」
import { Module, DynamicModule, Global } from '@nestjs/common';
import { CacheService } from './cache.service';
// Module configuration interface
export interface CacheModuleOptions {
ttl: number; // Time-to-live in seconds
maxItems: number; // Maximum number of entries
prefix: string; // Key prefix
}
@Global() // Available everywhere without explicit import
@Module({})
export class CacheModule {
static forRoot(options: CacheModuleOptions): DynamicModule {
return {
module: CacheModule,
providers: [
{
// Inject options via a custom token
provide: 'CACHE_OPTIONS',
useValue: options,
},
CacheService,
],
exports: [CacheService],
};
}
}対応するサービスは@Inject()を介してオプションを注入します。
import { Injectable, Inject } from '@nestjs/common';
import { CacheModuleOptions } from './cache.module';
@Injectable()
export class CacheService {
private store = new Map<string, { value: any; expiry: number }>();
constructor(@Inject('CACHE_OPTIONS') private options: CacheModuleOptions) {}
set(key: string, value: any): void {
const prefixedKey = `${this.options.prefix}:${key}`;
this.store.set(prefixedKey, {
value,
expiry: Date.now() + this.options.ttl * 1000,
});
}
get<T>(key: string): T | null {
const prefixedKey = `${this.options.prefix}:${key}`;
const entry = this.store.get(prefixedKey);
// Check expiration before returning
if (!entry || entry.expiry < Date.now()) return null;
return entry.value as T;
}
}forRoot / forRootAsyncパターンはNestJSの定番です。forRootAsyncは注入可能なConfigServiceから設定を読み込み、本番環境で推奨されるプラクティスとなっています。
モジュールを@Global()としてマークするのは便利に見えますが、乱用すると見えない結合が生まれます。@Global()は横断的なサービス(config、cache、logger)に限定すべきだと面接で言及することは、アーキテクチャ的成熟度を示します。
カスタムデコレータとGuardsの合成
カスタムデコレータは、複数のGuardsとメタデータを単一のアノテーションに結合します。このトピックはミッドシニアのポジションで取り上げられます。
import { applyDecorators, UseGuards, SetMetadata } from '@nestjs/common';
import { JwtAuthGuard } from './jwt-auth.guard';
import { RolesGuard } from './roles.guard';
// Combines JWT authentication + role verification
export function Auth(...roles: string[]) {
return applyDecorators(
SetMetadata('roles', roles),
UseGuards(JwtAuthGuard, RolesGuard),
);
}コントローラーでの使用例です。
import { Controller, Get, Post, Body } from '@nestjs/common';
import { Auth } from '../auth/auth.decorator';
import { OrdersService } from './orders.service';
@Controller('orders')
export class OrdersController {
constructor(private ordersService: OrdersService) {}
@Get()
@Auth('admin', 'manager') // Single decorator replaces UseGuards + Roles
findAll() {
return this.ordersService.findAll();
}
@Post()
@Auth('admin')
create(@Body() dto: CreateOrderDto) {
return this.ordersService.create(dto);
}
}applyDecoratorsパターンはコントローラーのコードをシンプルにし、認可ロジックを一元化します。面接でこのアプローチを能動的に提案することは強いシグナルになります。
まとめ
- Guardsは
CanActivateとReflectorを使い、ハンドラーのメタデータに基づくアクセス判断を行います - InterceptorsはRxJS(
tap、map)を活用し、ハンドラーの前後でストリームを変換します - 実行順序Middleware → Guards → Interceptors → Pipes → Handlerはほぼ普遍的な質問です
- 循環依存は
forwardRefではなく、アーキテクチャのリファクタリングで解決すべきです - Dynamic Modules(
forRoot/forRootAsync)は再利用可能なコンポーネントを構築する力を示します applyDecoratorsは複数のGuardsを読みやすい一つのデコレータに合成します- これらの質問を実コードで練習するなら、NestJSモジュールとInterceptorsをご覧ください
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。
タグ
共有
関連記事

NestJS + Prisma:Node.js のためのモダンなバックエンドスタック
NestJS と Prisma によるモダンなバックエンド API 構築の完全ガイドです。セットアップ、モデル、サービス、トランザクション、ベストプラクティスを解説します。

Node.jsバックエンド面接質問:完全ガイド2026
Node.jsバックエンド面接で最も頻出の25問。Event Loop、async/await、Streams、クラスタリング、パフォーマンスを詳細な回答で解説します。

NestJS: 本格的なREST APIの構築ガイド
NestJSで本格的なREST APIを構築するための完全ガイドです。コントローラー、サービス、モジュール構成、class-validatorによるバリデーション、エラーハンドリングを実践的に解説します。