本記事は NestJS Advent Calendar 2019 19日目の記事です。
この記事の公開日は12月28日でクリスマスを過ぎていますが、アドカレが空いているためせっかくなので登録させていただきます。
NestJSのAdventカレンダーを盛り上げてくださったみなさん、本当にありがとうございました。
大変参考になりました。
NestJS実装
プロジェクト作成
nest new websocket-server
パッケージインストール
$ npm i --save @nestjs/websockets @nestjs/platform-socket.io
$ npm i --save-dev @types/socket.io
WebsocketGatewayを追加する
$ nest g gateway Events
CREATE /src/events.gateway.spec.ts (460 bytes)
CREATE /src/events.gateway.ts (240 bytes)
作成されたクラスを以下のように修正します。
import {
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
WsResponse,
MessageBody
} from '@nestjs/websockets';
import { Server } from 'socket.io';
@WebSocketGateway()
export class EventsGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('message')
handleMessage(@MessageBody() data: any): WsResponse<string> {
return { event: 'message', data: data };
}
}
今回はクライアントからリクエストされた内容をそのままオウム返しする処理にしてみました。
クライアント
クライアント側はAngular2 + Socket.ioで遊んでみるの記事を参考にさせていただき実装します。
プロジェクト作成
$ ng new websocket-client
パッケージインストール
$ npm install socket.io-client --save
$ npm install @types/socket.io-client --save-dev
サービス作成
websocket通信を行うサービスを作成します。
$ ng generate service websocket
CREATE src/websocket.service.spec.ts (348 bytes)
CREATE src/websocket.service.ts (138 bytes)
import { Injectable } from '@angular/core';
import * as io from 'socket.io-client';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class WebsocketService {
private url = 'http://localhost:3001';
private socket;
connect() {
this.socket = io(this.url);
}
emit(emitName: string, data?) {
this.socket.emit(emitName, data);
}
on(onName: string) {
let observable = new Observable(observer => {
this.socket.on(onName, (data) => {
observer.next(data);
});
return () => { this.socket.disconnect(); };
});
return observable;
}
}
コンポーネント作成
最後にコンポーネントを作成します。
$ ng generate component websocket
import { Component, OnInit, OnDestroy } from '@angular/core';
import { WebsocketService } from '../websocket.service';
@Component({
selector: 'ss-test-websocket',
templateUrl: './websocket.component.html',
styleUrls: ['./websocket.component.scss']
})
export class WebsocketComponent implements OnInit, OnDestroy {
connection;
data;
message;
constructor(private websocketService: WebsocketService) {}
onClick(){
this.websocketService.emit('events', { test: 'test' });
console.log('click start');
console.log('click end')
}
ngOnInit() {
this.websocketService.connect();
this.connection = this.websocketService.on('message').subscribe(data => {
console.log(data);
this.message = data;
})
this.websocketService.emit('message', 'Hello!!');
}
ngOnDestroy() {
this.connection.unsubscribe();
}
}
<p>websocket works!</p>
{{ message }}
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { WebsocketComponent } from './websocket/websocket.component';
const routes: Routes = [
{ path: 'websocket', component: WebsocketComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
実行結果
クライアント側にブロードキャストする
上記の処理は、リクエストに対してレスポンスするだけの処理だったのですが、コネクション接続しているクライアント全員に対してブロードキャストメッセージを送信する方法を紹介します。
サーバ
import {
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
WsResponse,
MessageBody
} from '@nestjs/websockets';
import { Server } from 'socket.io';
@WebSocketGateway(3001)
export class EventsGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('message')
handleMessage(@MessageBody() data: any): WsResponse<string> {
return { event: 'response', data: data };
}
@SubscribeMessage('broadcast')
broadcast(@MessageBody() data: any): void {
this.server.emit('broadcast', 'response')
}
}
server.emitでメッセージを送信しています。
クライアント
import { Component, OnInit, OnDestroy } from '@angular/core';
import { WebsocketService } from '../websocket.service';
@Component({
selector: 'ss-test-websocket',
templateUrl: './websocket.component.html',
styleUrls: ['./websocket.component.scss']
})
export class WebsocketComponent implements OnInit, OnDestroy {
connection;
message;
constructor(private websocketService: WebsocketService) {}
onClick(){
this.websocketService.emit('broadcast', 'Hello!');
}
ngOnInit() {
this.websocketService.connect();
this.connection = this.websocketService.on('broadcast').subscribe(data => {
console.log(data);
this.message = data;
})
}
ngOnDestroy() {
this.connection.unsubscribe();
}
}
<p>websocket works!</p>
<button (click)="onClick()">Click</button>
{{ message }}
結果
こちらを利用すればチャットなども作成可能です。
参考資料
NestJS->WEBSOCKETS->Gateways
公式のサンプルコード
Angular2 + Socket.ioで遊んでみる
その他
上記の内容に加えて、NestJSのwebsocketをAPIGateway+Lambdaでサーバレス実装を載せようと思っていたのですが、WebSocketGatewayの呼び出しがうまくいかず断念しました・・・
RESTはコントローラを呼び出す設定に普通にできたのですが。
今後色々試して出来たらまたこちらを更新します。
もしプロキシする方法わかる人は教えてください。