Websocket Client for NestJS based on ws
npm i nestjs-websocket
nestjs-websocket
can be configured with this options:
/**
* WebSocket Client options
* @see {@link https://github.com/websockets/ws/blob/master/doc/ws.md#class-websocket}
*/
interface WebSocketModuleOptions {
/**
* Required parameter a URL to connect to.
* such as http://localhost:3000 or wss://localhost:3000.
*/
url: string | URL;
/**
* Optional parameter a list of subprotocols.
*/
protocols?: string | string[]
/**
* Optional parameter a client or http request options.
*/
options?: ClientOptions | ClientRequestArgs
}
Use WebSocketModule.forRoot
method with Options interface:
import { WebSocketModule } from 'nestjs-websocket'
@Module({
imports: [
WebSocketModule.forRoot({
url: 'ws://localhost:3000',
protocols: ['foo', 'bar'],
options: {
followRedirects: false,
handshakeTimeout: 10000,
maxPayload: 2000000,
maxRedirects: 10,
origin: 'http:/example.com',
perMessageDeflate: false,
protocolVersion: 1,
skipUTF8Validation: false,
},
}),
],
...
})
class MyModule {}
With WebSocketModule.forRootAsync
you can, for example, import your ConfigModule
and inject ConfigService
to use it in useFactory
method.
useFactory
should return object with Options interface
Here's an example:
import { Module, Injectable } from '@nestjs/common'
import { WebSocketModule } from 'nestjs-websocket'
@Injectable()
class ConfigService {
public readonly url = 'ws://localhost:3000'
}
@Module({
providers: [ConfigService],
exports: [ConfigService]
})
class ConfigModule {}
@Module({
imports: [
WebSocketModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => {
return {
url: config.url,
}
},
}),
],
...
})
class MyModule {}
Or you can just pass ConfigService
to providers
, if you don't have any ConfigModule
:
import { Module, Injectable } from '@nestjs/common'
import { WebSocketModule } from 'nestjs-websocket'
@Injectable()
class ConfigService {
public readonly url = 'ws://localhost:3000'
}
@Module({
imports: [
WebSocketModule.forRootAsync({
providers: [ConfigService],
inject: [ConfigService],
useFactory: (config: ConfigService) => {
return {
url: config.url,
}
},
}),
],
controllers: [TestController]
})
class TestModule {}
WebSocketClient
implements a WebSocket. So if you are familiar with it, you are ready to go.
import { Injectable } from '@nestjs/common'
import {
InjectWebSocketProvider,
WebSocketClient,
OnOpen,
OnMessage,
} from 'nestjs-websocket';
@Injectable()
class TestService {
private data: Record<any, any> = {}
constructor(
@InjectWebSocketProvider()
private readonly ws: WebSocketClient,
) {}
@OnOpen()
onOpen() {
this.ws.send(JSON.stringify(eventData))
}
@OnMessage()
message(data: WebSocketClient.Data) {
this.data = JSON.parse(data.toString())
}
async getData(): Promise<Record<any, any>> {
return this.data
}
}
@EventListener
decorator will handle any event emitted from websocket server.
import { Injectable } from '@nestjs/common'
import { ClientRequest, IncomingMessage } from 'http'
import {
EventListener
} from 'nestjs-websocket';
@Injectable()
class TestService {
@EventListener('open')
open() {
console.log('The connection is established.')
}
@EventListener('ping')
ping(data: Buffer) {
console.log(`A ping ${data.toString()} is received from the server.`)
}
@EventListener('unexpected-response')
unexpectedResponse(request: ClientRequest, response: IncomingMessage) {
console.log(`The server response ${response} is not the expected one.`)
}
@EventListener('upgrade')
upgrade(response: IncomingMessage) {
console.log(`Response headers ${response} are received from the server as part of the handshake.`)
}
}
@OnOpen
is a shortcut for @EventListener('open')
. Event emitted when the connection is established.
import { Injectable } from '@nestjs/common'
import {
OnOpen
} from 'nestjs-websocket';
@Injectable()
class TestService {
@OnOpen()
open() {
console.log('The connection is established.')
}
}
@OnClose
is a shortcut for @EventListener('close')
Event emitted when the connection is closed. code
property is a numeric value for status code explaining why the connection has been closed. reason
is a Buffer containing a human-readable string explaining why the connection has been closed.
import { Injectable } from '@nestjs/common'
import {
OnClose
} from 'nestjs-websocket';
@Injectable()
class TestService {
@OnClose()
close(code: number, reason: string) {
console.log(`The connection is closed. Reason: ${code} - ${reason}`)
}
}
@OnError
is a shortcut for @EventListener('error')
. Event emitted when an error occurs. Errors may have a .code property.
import { Injectable } from '@nestjs/common'
import {
OnError
} from 'nestjs-websocket';
@Injectable()
class TestService {
@OnError()
error(err: Error) {
console.log(`An error occurs: ${err}`)
}
}
@OnMessage
is a shortcut for @EventListener('message')
. Event emitted when a message is received. data
is the message content.
import { Injectable } from '@nestjs/common'
import {
OnMessage
} from 'nestjs-websocket';
@Injectable()
class TestService {
@OnMessage()
message(data: WebSocketClient.Data) {
console.log(`Data received: ${JSON.parse(data.toString())}`)
}
}
This package exposes a getWebSocketToken()
function that returns a prepared injection token based on the provided context.
Using this token, you can easily provide a mock implementation of the ws using any of the standard custom provider techniques, including useClass, useValue, and useFactory.
const module: TestingModule = await Test.createTestingModule({
providers: [
MyService,
{
provide: getWebSocketToken(),
useValue: mockProvider,
},
],
}).compile();
See Changelog for more information.
Contributions welcome! See Contributing.
Licensed under the Apache 2.0 - see the LICENSE file for details.