NestJSとWebSocket 2026年版:リアルタイム通信、Gatewayパターン、面接対策
NestJSでのWebSocket実装を徹底解説。Gatewayパターン、Socket.IO統合、認証、エラーハンドリング、2026年の技術面接で頻出の質問まで網羅します。

NestJSのWebSocketは、Gatewayパターンを活用した構造化されたリアルタイム通信を提供します。生のWebSocket実装とは異なり、NestJSはデコレータを通じて複雑さを抽象化し、依存性注入システムとシームレスに統合されます。
NestJSにおけるWebSocket Gatewayは、@WebSocketGateway()デコレータが付与されたクラスであり、クライアントとサーバー間の双方向通信を処理します。複数のアダプター(Socket.IO、ws)をサポートし、NestJSのGuards、Pipes、Interceptorsと統合可能です。
Socket.IOを使用したWebSocket Gatewayの構築
NestJSのデフォルトアダプターはSocket.IOを使用しており、自動再接続、ルームサポート、HTTPロングポーリングへのフォールバック機能を提供します。インストールには、コアWebSocketsモジュールとともにプラットフォーム固有のパッケージが必要です。
# terminal
npm install @nestjs/websockets @nestjs/platform-socket.io socket.io基本的なGatewayは、受信メッセージをリッスンし、接続されたクライアントにレスポンスをブロードキャストできます。
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は、クライアントの接続と切断を処理するためのライフサイクルインターフェースを実装します。これらのフックにより、リソースのクリーンアップ、プレゼンストラッキング、接続の検証が可能になります。
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<string, { joinedAt: Date }>();
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ドキュメントでは、Guard統合について詳しく説明されています。
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エラーイベントを通じて受信するエラーをスローします。
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認証面接モジュールをご覧ください。
Node.js / NestJSの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
ルームベースのブロードキャストパターン
Socket.IOのルーム機能により、接続されたクライアントのサブセットへのターゲットを絞ったメッセージ配信が可能になります。一般的なユースケースには、チャットルーム、ゲームロビー、ライブコラボレーション機能があります。
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をキャッチし、クライアント向けにエラーレスポンスをフォーマットします。
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レベルでフィルターを適用します。
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
}このパターンは、ミドルウェアとインターセプターモジュールで説明されているNestJSのデコレータとフィルターの哲学を反映しています。
Redisアダプターによるスケーリング
単一サーバーのWebSocketデプロイメントは、ロードバランシングがクライアントを複数のインスタンスに分散する場合に失敗します。Socket.IO Redisアダプターは、pub/subメカニズムを通じてインスタンス間でイベントを同期します。
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は、インスタンス間通信のためにアダプターを使用します。
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ライブラリがテストサーバーに接続します。ライフサイクルフックとメッセージハンドラーは、イベントを発行してレスポンスや副作用をアサートすることでテストされます。
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ライブラリアダプター(@nestjs/platform-ws)は、Socket.IOの自動再接続、ルーム、フォールバックトランスポートなどの機能を必要としないアプリケーションに対して、より低いオーバーヘッドを提供します。高頻度取引システムやゲームサーバーでは、レイテンシ削減のためにwsが好まれることが多いです。
より広範なNestJSアーキテクチャの概念については、Guards、Interceptors、モジュラーアーキテクチャに関する関連記事で、これらのパターンがHTTPとWebSocketコンテキスト間でどのように統合されるかを説明しています。
まとめ
- WebSocket GatewayはNestJSの依存性注入、Guards、Filtersと統合され、HTTPとリアルタイムエンドポイント間で一貫したパターンを提供します
- ライフサイクルフック(
OnGatewayConnection、OnGatewayDisconnect)は、プレゼンストラッキングとリソースクリーンアップを管理します - 認証フローはHTTPとは異なり、トークン検証はハンドシェイク中または専用のWebSocket Guardsを通じて行われます
- Socket.IOによるルームベースのブロードキャストにより、ソケットリストを手動で管理することなくターゲットを絞ったメッセージングが可能です
- Redisアダプターは、複数のサーバーインスタンス間でイベントを同期することで水平スケーリングの問題を解決します
- 例外フィルターには、
WsExceptionとカスタムフィルターを使用したWebSocket固有の実装が必要です
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。

執筆
Anthony Fillion-Mailletフルスタック開発者、SharpSkill 創業者
10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。
2026年8月10日 更新
共有
関連記事

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

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

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