# NestJSとWebSocket 2026年版:リアルタイム通信、Gatewayパターン、面接対策 > NestJSでのWebSocket実装を徹底解説。Gatewayパターン、Socket.IO統合、認証、エラーハンドリング、2026年の技術面接で頻出の質問まで網羅します。 - Published: 2026-08-10 - Updated: 2026-08-10 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- NestJSのWebSocketは、Gatewayパターンを活用した構造化されたリアルタイム通信を提供します。生のWebSocket実装とは異なり、NestJSはデコレータを通じて複雑さを抽象化し、依存性注入システムとシームレスに統合されます。 > **基本概念** > > NestJSにおけるWebSocket Gatewayは、`@WebSocketGateway()`デコレータが付与されたクラスであり、クライアントとサーバー間の双方向通信を処理します。複数のアダプター(Socket.IO、ws)をサポートし、NestJSのGuards、Pipes、Interceptorsと統合可能です。 ## Socket.IOを使用したWebSocket Gatewayの構築 NestJSのデフォルトアダプターは[Socket.IO](https://socket.io/docs/v4/)を使用しており、自動再接続、ルームサポート、HTTPロングポーリングへのフォールバック機能を提供します。インストールには、コアWebSocketsモジュールとともにプラットフォーム固有のパッケージが必要です。 ```bash # terminal npm install @nestjs/websockets @nestjs/platform-socket.io socket.io ``` 基本的なGatewayは、受信メッセージをリッスンし、接続されたクライアントにレスポンスをブロードキャストできます。 ```typescript // chat.gateway.ts import { WebSocketGateway, WebSocketServer, SubscribeMessage, MessageBody, ConnectedSocket, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; @WebSocketGateway({ cors: { origin: '*' }, // Configure CORS for browser clients }) export class ChatGateway { @WebSocketServer() server: Server; // Access to the underlying Socket.IO server @SubscribeMessage('message') // Listen for 'message' events handleMessage( @MessageBody() data: { text: string; room: string }, @ConnectedSocket() client: Socket, ): void { // Broadcast to all clients in the room except sender client.to(data.room).emit('message', { text: data.text, senderId: client.id, timestamp: Date.now(), }); } } ``` `@SubscribeMessage`デコレータは、メソッドを特定のイベント名にバインドします。`@MessageBody()`はペイロードを抽出し、`@ConnectedSocket()`はターゲットを絞ったレスポンスのためにクライアントソケットへのアクセスを提供します。 ## 接続管理のためのライフサイクルフック NestJSのGatewayは、クライアントの接続と切断を処理するためのライフサイクルインターフェースを実装します。これらのフックにより、リソースのクリーンアップ、プレゼンストラッキング、接続の検証が可能になります。 ```typescript // presence.gateway.ts import { WebSocketGateway, OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; import { Logger } from '@nestjs/common'; @WebSocketGateway() export class PresenceGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { private readonly logger = new Logger(PresenceGateway.name); private connectedUsers = new Map(); afterInit(server: Server): void { // Called once when the gateway initializes this.logger.log('WebSocket Gateway initialized'); } handleConnection(client: Socket): void { // Track new connections this.connectedUsers.set(client.id, { joinedAt: new Date() }); this.logger.log(`Client connected: ${client.id}`); } handleDisconnect(client: Socket): void { // Cleanup on disconnect this.connectedUsers.delete(client.id); this.logger.log(`Client disconnected: ${client.id}`); } } ``` 3つのライフサイクルインターフェースはそれぞれ異なる目的を持ちます。`OnGatewayInit`は起動時に一度実行され、`OnGatewayConnection`はクライアント接続ごとにトリガーされ、`OnGatewayDisconnect`はクライアント離脱時のクリーンアップを処理します。 ## WebSocket Guardsによる認証 WebSocket接続は、保護されたイベントへのアクセスを許可する前に認証検証が必要です。NestJS GuardsはGatewayで機能しますが、実行コンテキストはHTTPリクエストとは異なります。公式の[NestJS WebSocketsドキュメント](https://docs.nestjs.com/websockets/gateways)では、Guard統合について詳しく説明されています。 ```typescript // ws-auth.guard.ts import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { WsException } from '@nestjs/websockets'; import { JwtService } from '@nestjs/jwt'; import { Socket } from 'socket.io'; @Injectable() export class WsAuthGuard implements CanActivate { constructor(private jwtService: JwtService) {} canActivate(context: ExecutionContext): boolean { const client: Socket = context.switchToWs().getClient(); // Extract token from handshake auth or query params const token = client.handshake.auth?.token || client.handshake.query?.token; if (!token) { throw new WsException('Missing authentication token'); } try { const payload = this.jwtService.verify(token as string); // Attach user data to socket for later use client.data.user = payload; return true; } catch { throw new WsException('Invalid token'); } } } ``` `@UseGuards()`デコレータを使用して、Gatewayクラスまたは個別のメッセージハンドラーにGuardを適用します。`WsException`クラスは、クライアントがSocket.IOエラーイベントを通じて受信するエラーをスローします。 ```typescript // secure-chat.gateway.ts import { UseGuards } from '@nestjs/common'; import { WebSocketGateway, SubscribeMessage } from '@nestjs/websockets'; import { WsAuthGuard } from './ws-auth.guard'; @WebSocketGateway() @UseGuards(WsAuthGuard) // Protect all handlers in this gateway export class SecureChatGateway { @SubscribeMessage('privateMessage') handlePrivateMessage(): void { // Only authenticated clients reach this handler } } ``` モジュールレベルの認証に関する質問については、JWTストラテジーとセッション管理を網羅した[NestJS認証面接モジュール](/ja/technologies/node-nestjs/interview-questions/authentication-jwt)をご覧ください。 ## ルームベースのブロードキャストパターン Socket.IOのルーム機能により、接続されたクライアントのサブセットへのターゲットを絞ったメッセージ配信が可能になります。一般的なユースケースには、チャットルーム、ゲームロビー、ライブコラボレーション機能があります。 ```typescript // room.gateway.ts import { WebSocketGateway, SubscribeMessage, ConnectedSocket, MessageBody, WebSocketServer, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; @WebSocketGateway() export class RoomGateway { @WebSocketServer() server: Server; @SubscribeMessage('joinRoom') handleJoinRoom( @MessageBody() roomId: string, @ConnectedSocket() client: Socket, ): { event: string; data: string } { // Add client to the specified room client.join(roomId); // Notify others in the room client.to(roomId).emit('userJoined', { userId: client.id }); return { event: 'joinedRoom', data: roomId }; } @SubscribeMessage('leaveRoom') handleLeaveRoom( @MessageBody() roomId: string, @ConnectedSocket() client: Socket, ): void { client.leave(roomId); client.to(roomId).emit('userLeft', { userId: client.id }); } @SubscribeMessage('roomBroadcast') handleRoomBroadcast( @MessageBody() payload: { roomId: string; message: string }, ): void { // Send to all clients in room, including sender this.server.to(payload.roomId).emit('roomMessage', payload.message); } } ``` `client.to(room).emit()`と`server.to(room).emit()`の違いは重要です。前者は送信者を除外し、後者はすべてのルームメンバーを含みます。 ## 例外フィルターによるエラーハンドリング WebSocket例外は、HTTP例外フィルターがGatewayコンテキストに適用されないため、専用のフィルターが必要です。カスタムフィルターは`WsException`をキャッチし、クライアント向けにエラーレスポンスをフォーマットします。 ```typescript // ws-exception.filter.ts import { Catch, ArgumentsHost, ExceptionFilter } from '@nestjs/common'; import { WsException } from '@nestjs/websockets'; import { Socket } from 'socket.io'; @Catch(WsException) export class WsExceptionFilter implements ExceptionFilter { catch(exception: WsException, host: ArgumentsHost): void { const client: Socket = host.switchToWs().getClient(); const error = exception.getError(); // Send structured error to client client.emit('error', { type: 'WsException', message: typeof error === 'string' ? error : error, timestamp: new Date().toISOString(), }); } } ``` すべてのメッセージハンドラーで一貫したエラーハンドリングを実現するために、Gatewayレベルでフィルターを適用します。 ```typescript // filtered.gateway.ts import { UseFilters } from '@nestjs/common'; import { WebSocketGateway } from '@nestjs/websockets'; import { WsExceptionFilter } from './ws-exception.filter'; @WebSocketGateway() @UseFilters(WsExceptionFilter) export class FilteredGateway { // All handlers benefit from centralized error handling } ``` このパターンは、[ミドルウェアとインターセプターモジュール](/ja/technologies/node-nestjs/interview-questions/middleware-interceptors)で説明されているNestJSのデコレータとフィルターの哲学を反映しています。 ## Redisアダプターによるスケーリング 単一サーバーのWebSocketデプロイメントは、ロードバランシングがクライアントを複数のインスタンスに分散する場合に失敗します。[Socket.IO Redisアダプター](https://socket.io/docs/v4/redis-adapter/)は、pub/subメカニズムを通じてインスタンス間でイベントを同期します。 ```typescript // app.module.ts import { Module } from '@nestjs/common'; import { createAdapter } from '@socket.io/redis-adapter'; import { createClient } from 'redis'; import { ChatGateway } from './chat.gateway'; @Module({ providers: [ ChatGateway, { provide: 'REDIS_ADAPTER', useFactory: async () => { const pubClient = createClient({ url: process.env.REDIS_URL }); const subClient = pubClient.duplicate(); await Promise.all([pubClient.connect(), subClient.connect()]); return createAdapter(pubClient, subClient); }, }, ], }) export class AppModule {} ``` Gatewayは、インスタンス間通信のためにアダプターを使用します。 ```typescript // scalable.gateway.ts import { WebSocketGateway, WebSocketServer, OnGatewayInit, } from '@nestjs/websockets'; import { Inject } from '@nestjs/common'; import { Server } from 'socket.io'; import { Adapter } from 'socket.io-adapter'; @WebSocketGateway() export class ScalableGateway implements OnGatewayInit { @WebSocketServer() server: Server; constructor(@Inject('REDIS_ADAPTER') private redisAdapter: Adapter) {} afterInit(): void { // Attach Redis adapter for multi-instance support this.server.adapter(this.redisAdapter as any); } } ``` Redisアダプターを使用すると、1つのサーバーインスタンスで発行されたメッセージが、クラスター内の任意のインスタンスに接続されているクライアントに到達します。 ## NestJS WebSocketの面接頻出質問 技術面接では、リアルタイムアーキテクチャの決定とNestJS固有の実装詳細についての理解が問われることが多いです。 > **よくある質問** > > **NestJSはWebSocket認証をHTTPとどのように異なる方法で処理しますか?** > > HTTPミドルウェアはWebSocket接続に対して実行されません。認証はハンドシェイクフェーズ中、またはGatewayに適用されたGuardsを通じて行われます。トークン検証は通常、`handleConnection()`または`socket.handshake.auth`から読み取るカスタムGuardで行われます。 **`@WebSocketGateway()`のポート設定とメインアプリケーションポートの違いは何ですか?** デフォルトでは、WebSocket GatewayはHTTPサーバーポートを共有します。`@WebSocketGateway(3001)`でポートを指定すると、そのポートに別のWebSocketサーバーが作成されます。共有ポートはデプロイメントを簡素化しますが、リバースプロキシを使用する場合はパスベースのルーティング(`/socket.io`)が必要です。 **NestJSでWebSocket Gatewayをテストする方法は?** NestJSのテストユーティリティは、`Test.createTestingModule()`を通じてGatewayテストをサポートしています。[socket.io-client](https://github.com/socketio/socket.io-client)ライブラリがテストサーバーに接続します。ライフサイクルフックとメッセージハンドラーは、イベントを発行してレスポンスや副作用をアサートすることでテストされます。 ```typescript // chat.gateway.spec.ts import { Test } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; import { io, Socket } from 'socket.io-client'; import { ChatGateway } from './chat.gateway'; describe('ChatGateway', () => { let app: INestApplication; let client: Socket; beforeAll(async () => { const module = await Test.createTestingModule({ providers: [ChatGateway], }).compile(); app = module.createNestApplication(); await app.listen(3000); client = io('http://localhost:3000'); }); afterAll(async () => { client.disconnect(); await app.close(); }); it('receives message acknowledgment', (done) => { client.emit('message', { text: 'test', room: 'lobby' }); client.on('message', (data) => { expect(data.text).toBe('test'); done(); }); }); }); ``` **Socket.IOの代わりにwsアダプターを使用すべき場面は?** [wsライブラリ](https://github.com/websockets/ws)アダプター(`@nestjs/platform-ws`)は、Socket.IOの自動再接続、ルーム、フォールバックトランスポートなどの機能を必要としないアプリケーションに対して、より低いオーバーヘッドを提供します。高頻度取引システムやゲームサーバーでは、レイテンシ削減のためにwsが好まれることが多いです。 より広範なNestJSアーキテクチャの概念については、[Guards、Interceptors、モジュラーアーキテクチャ](/ja/blog/node-nestjs/nestjs-guards-interceptors-modular-architecture)に関する関連記事で、これらのパターンがHTTPとWebSocketコンテキスト間でどのように統合されるかを説明しています。 ## まとめ - WebSocket GatewayはNestJSの依存性注入、Guards、Filtersと統合され、HTTPとリアルタイムエンドポイント間で一貫したパターンを提供します - ライフサイクルフック(`OnGatewayConnection`、`OnGatewayDisconnect`)は、プレゼンストラッキングとリソースクリーンアップを管理します - 認証フローはHTTPとは異なり、トークン検証はハンドシェイク中または専用のWebSocket Guardsを通じて行われます - Socket.IOによるルームベースのブロードキャストにより、ソケットリストを手動で管理することなくターゲットを絞ったメッセージングが可能です - Redisアダプターは、複数のサーバーインスタンス間でイベントを同期することで水平スケーリングの問題を解決します - 例外フィルターには、`WsException`とカスタムフィルターを使用したWebSocket固有の実装が必要です --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/node-nestjs/nestjs-websockets-real-time-gateway-best-practices