17
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

本記事は 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)

作成されたクラスを以下のように修正します。

events.gateway.ts
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)
websocket.service.ts
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
websocket/websocket.component.ts
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();
  }
}
websocket/websocket.component.html
<p>websocket works!</p>
{{ message }}
app-routing.module.ts
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 { }

実行結果

ブラウザ.png

クライアント側にブロードキャストする

上記の処理は、リクエストに対してレスポンスするだけの処理だったのですが、コネクション接続しているクライアント全員に対してブロードキャストメッセージを送信する方法を紹介します。

サーバ

events.gateway.ts
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でメッセージを送信しています。

クライアント

websocket/websocket.component.ts
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();
  }
}
websocket/websocket.component.html
<p>websocket works!</p>
<button (click)="onClick()">Click</button>
{{ message }}

結果

websocket.gif

こちらを利用すればチャットなども作成可能です。

参考資料

NestJS->WEBSOCKETS->Gateways
公式のサンプルコード
Angular2 + Socket.ioで遊んでみる

その他

上記の内容に加えて、NestJSのwebsocketをAPIGateway+Lambdaでサーバレス実装を載せようと思っていたのですが、WebSocketGatewayの呼び出しがうまくいかず断念しました・・・
RESTはコントローラを呼び出す設定に普通にできたのですが。
今後色々試して出来たらまたこちらを更新します。
もしプロキシする方法わかる人は教えてください。

17
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?