Skip to content

blockcoders/nestjs-io-client

Repository files navigation

NestJS Socket.io Client

npm CircleCI Coverage Status vulnerabilities supported platforms

Socket.io Client for NestJS based on socket.io-client

Install

npm i nestjs-io-client

Register module

Configuration params

nestjs-io-client can be configured with this options:

/**
 * Socket.io Client options
 * @see {@link https://socket.io/docs/v4/client-api/#iourl}
 */
interface IoClientModuleOptions {
  /**
   * Required parameter a URL to connect.
   * such as http://localhost:3000/my-namespace or ws://localhost:3000/my-namespace.
   */
  uri: string;

  /**
   * Optional parameter a client or http request options.
   */
  options?: Partial<ManagerOptions & SocketOptions>
}

Synchronous configuration

Use IoClientModule.forRoot method with Options interface:

import { IoClientModule } from 'nestjs-io-client'

@Module({
  imports: [
    IoClientModule.forRoot({
      uri: 'ws://localhost:3000/my-namespace',
      options: {
        reconnectionDelayMax: 10000,
        auth: { token: '123' },
        query: {
          foo: 'value'
        },
      },
    }),
  ],
  ...
})
class MyModule {}

Asynchronous configuration

With IoClientModule.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 { IoClientModule } from 'nestjs-io-client'

@Injectable()
class ConfigService {
  public readonly uri = 'ws://localhost:3000/my-namespace'
}

@Module({
  providers: [ConfigService],
  exports: [ConfigService]
})
class ConfigModule {}

@Module({
  imports: [
    IoClientModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => {
        return {
          uri: config.uri,
        }
      },
    }),
  ],
  ...
})
class MyModule {}

Or you can just pass ConfigService to providers, if you don't have any ConfigModule:

import { Module, Injectable } from '@nestjs/common'
import { IoClientModule } from 'nestjs-io-client'

@Injectable()
class ConfigService {
  public readonly uri = 'ws://localhost:3000/my-namespace'
}

@Module({
  imports: [
    IoClientModule.forRootAsync({
      providers: [ConfigService],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => {
        return {
          uri: config.uri,
        }
      },
    }),
  ],
  controllers: [TestController]
})
class TestModule {}

IoClient

IoClient implements a Socket. So if you are familiar with it, you are ready to go.

import { Injectable } from '@nestjs/common'
import {
  InjectIoClientProvider,
  IoClient,
  OnConnect,
  OnConnectError,
  EventListener,
} from 'nestjs-io-client';

@Injectable()
class TestService {
  constructor(
    @InjectIoClientProvider()
    private readonly io: IoClient,
  ) {}

  @OnConnect()
  connect() {
    console.log('connected!')
    console.log(this.io.id); // "G5p5..."
    console.log(this.io.connected); // true
  }
  
  @OnConnectError()
  connectError(err: Error) {
    console.error(`An error occurs: ${err}`)
  }

  @EventListener('news')
  message(data: any) {
    console.log(data);
  }
}

Socket.io Events

EventListener

@EventListener decorator will handle any event emitted from socket.io server.

import { Injectable } from '@nestjs/common'
import {
  EventListener
} from 'nestjs-io-client';

@Injectable()
class TestService {
  @EventListener('connect')
  open() {
    console.log('The connection is established.')
  }
  
  @EventListener('ping')
  ping() {
    console.log('A ping is received from the server.')
  }
  
  @EventListener('reconnect')
  unexpectedResponse() {
    console.log('successful reconnection')
  }
  
  @EventListener('news')
  upgrade(data: any) {
    console.log(data);
  }
}

OnConnect

@OnConnect is a shortcut for @EventListener('connect'). Event emitted when the connection is established.

import { Injectable } from '@nestjs/common'
import {
  OnConnect
} from 'nestjs-io-client';

@Injectable()
class TestService {
  @OnConnect()
  connect() {
    console.log('The connection is established.')
  }
}

OnDisconnect

@OnDisconnect is a shortcut for @EventListener('disconnect') Event emitted when the connection is closed. reason is a string explaining why the connection has been closed.

import { Injectable } from '@nestjs/common'
import {
  IoClient,
  OnDisconnect
} from 'nestjs-io-client';

@Injectable()
class TestService {
  @OnDisconnect()
  disconnect(reason: IoClient.DisconnectReason) {
    if (reason === "io server disconnect") {
      // the disconnection was initiated by the server, you need to reconnect manually
      socket.connect();
    }
    // else the socket will automatically try to reconnect
  }
}

OnConnectError

@OnConnectError is a shortcut for @EventListener('connect_error') Event emitted when when an namespace middleware error occurs.

import { Injectable } from '@nestjs/common'
import {
  OnConnectError
} from 'nestjs-io-client';

@Injectable()
class TestService {
  @OnConnectError()
  connectError(err: Error) {
    console.error(`An error occurs: ${err}`)
  }
}

Testing a class that uses @InjectIoClientProvider

This package exposes a getIoClientToken() function that returns a prepared injection token based on the provided context. Using this token, you can easily provide a mock implementation of the Socket using any of the standard custom provider techniques, including useClass, useValue, and useFactory.

const module: TestingModule = await Test.createTestingModule({
  providers: [
    MyService,
    {
      provide: getIoClientToken(),
      useValue: mockProvider,
    },
  ],
}).compile();

Change Log

See Changelog for more information.

Contributing

Contributions welcome! See Contributing.

Collaborators

License

Licensed under the Apache 2.0 - see the LICENSE file for details.