├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── README.md
├── nest-cli.json
├── package-lock.json
├── package.json
├── src
├── bootstrap.module.ts
├── main.ts
├── module
│ ├── redis-event-pub-sub
│ │ ├── event
│ │ │ ├── emitter
│ │ │ │ ├── contract
│ │ │ │ │ ├── event-emitter.interface.ts
│ │ │ │ │ └── publishable-event.interface.ts
│ │ │ │ └── redis.event-emitter.ts
│ │ │ └── subscriber
│ │ │ │ ├── event-emitter-2.event-subscriber.ts
│ │ │ │ └── event-subscriber.interface.ts
│ │ ├── redis-event-pub-sub.module-definition.ts
│ │ └── redis-event-pub-sub.module.ts
│ └── user-chat
│ │ ├── event
│ │ └── new-message.event.ts
│ │ ├── transport
│ │ ├── http
│ │ │ └── action
│ │ │ │ └── send-message
│ │ │ │ ├── request
│ │ │ │ └── send-message.http-request.ts
│ │ │ │ └── send-message.action.ts
│ │ └── websocket
│ │ │ └── messages
│ │ │ └── messages.websocket-gateway.ts
│ │ └── user-chat.module.ts
└── shared
│ └── writable.type.ts
├── test
├── app.e2e-spec.ts
└── jest-e2e.json
├── tsconfig.build.json
├── tsconfig.json
└── ws_client.html
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | tsconfigRootDir: __dirname,
6 | sourceType: 'module',
7 | },
8 | plugins: ['@typescript-eslint/eslint-plugin'],
9 | extends: [
10 | 'plugin:@typescript-eslint/recommended',
11 | 'plugin:prettier/recommended',
12 | ],
13 | root: true,
14 | env: {
15 | node: true,
16 | jest: true,
17 | },
18 | ignorePatterns: ['.eslintrc.js'],
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/explicit-module-boundary-types': 'off',
23 | '@typescript-eslint/no-explicit-any': 'off',
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | pnpm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 |
14 | # OS
15 | .DS_Store
16 |
17 | # Tests
18 | /coverage
19 | /.nyc_output
20 |
21 | # IDEs and editors
22 | /.idea
23 | .project
24 | .classpath
25 | .c9/
26 | *.launch
27 | .settings/
28 | *.sublime-workspace
29 |
30 | # IDE - VSCode
31 | .vscode/*
32 | !.vscode/settings.json
33 | !.vscode/tasks.json
34 | !.vscode/launch.json
35 | !.vscode/extensions.json
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
6 | [circleci-url]: https://circleci.com/gh/nestjs/nest
7 |
8 | A progressive Node.js framework for building efficient and scalable server-side applications.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
24 |
25 | ## Description
26 |
27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
28 |
29 | ## Installation
30 |
31 | ```bash
32 | $ npm install
33 | ```
34 |
35 | ## Running the app
36 |
37 | ```bash
38 | # development
39 | $ npm run start
40 |
41 | # watch mode
42 | $ npm run start:dev
43 |
44 | # production mode
45 | $ npm run start:prod
46 | ```
47 |
48 | ## Test
49 |
50 | ```bash
51 | # unit tests
52 | $ npm run test
53 |
54 | # e2e tests
55 | $ npm run test:e2e
56 |
57 | # test coverage
58 | $ npm run test:cov
59 | ```
60 |
61 | ## Support
62 |
63 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
64 |
65 | ## Stay in touch
66 |
67 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
68 | - Website - [https://nestjs.com](https://nestjs.com/)
69 | - Twitter - [@nestframework](https://twitter.com/nestframework)
70 |
71 | ## License
72 |
73 | Nest is [MIT licensed](LICENSE).
74 |
--------------------------------------------------------------------------------
/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/nest-cli",
3 | "collection": "@nestjs/schematics",
4 | "sourceRoot": "src",
5 | "compilerOptions": {
6 | "deleteOutDir": true
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nestjs-events-websocket",
3 | "version": "0.0.1",
4 | "description": "",
5 | "author": "",
6 | "private": true,
7 | "license": "UNLICENSED",
8 | "scripts": {
9 | "build": "nest build",
10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
11 | "start": "nest start",
12 | "start:dev": "nest start --watch",
13 | "start:debug": "nest start --debug --watch",
14 | "start:prod": "node dist/main",
15 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
16 | "test": "jest",
17 | "test:watch": "jest --watch",
18 | "test:cov": "jest --coverage",
19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
20 | "test:e2e": "jest --config ./test/jest-e2e.json"
21 | },
22 | "dependencies": {
23 | "@nestjs/common": "^10.2.7",
24 | "@nestjs/config": "^3.1.1",
25 | "@nestjs/core": "^10.2.7",
26 | "@nestjs/event-emitter": "^2.0.2",
27 | "@nestjs/platform-express": "^10.2.7",
28 | "@nestjs/platform-socket.io": "^10.2.7",
29 | "@nestjs/websockets": "^10.2.7",
30 | "class-transformer": "^0.5.1",
31 | "class-validator": "^0.14.0",
32 | "redis": "^4.6.10",
33 | "reflect-metadata": "^0.1.13",
34 | "rxjs": "^7.2.0"
35 | },
36 | "devDependencies": {
37 | "@nestjs/cli": "^9.0.0",
38 | "@nestjs/schematics": "^9.0.0",
39 | "@nestjs/testing": "^10.2.7",
40 | "@types/express": "^4.17.13",
41 | "@types/jest": "29.2.4",
42 | "@types/node": "18.11.18",
43 | "@types/supertest": "^2.0.11",
44 | "@typescript-eslint/eslint-plugin": "^5.0.0",
45 | "@typescript-eslint/parser": "^5.0.0",
46 | "eslint": "^8.0.1",
47 | "eslint-config-prettier": "^8.3.0",
48 | "eslint-plugin-prettier": "^4.0.0",
49 | "jest": "29.3.1",
50 | "prettier": "^2.3.2",
51 | "source-map-support": "^0.5.20",
52 | "supertest": "^6.1.3",
53 | "ts-jest": "29.0.3",
54 | "ts-loader": "^9.2.3",
55 | "ts-node": "^10.0.0",
56 | "tsconfig-paths": "4.1.1",
57 | "typescript": "^4.7.4"
58 | },
59 | "jest": {
60 | "moduleFileExtensions": [
61 | "js",
62 | "json",
63 | "ts"
64 | ],
65 | "rootDir": "src",
66 | "testRegex": ".*\\.spec\\.ts$",
67 | "transform": {
68 | "^.+\\.(t|j)s$": "ts-jest"
69 | },
70 | "collectCoverageFrom": [
71 | "**/*.(t|j)s"
72 | ],
73 | "coverageDirectory": "../coverage",
74 | "testEnvironment": "node"
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/bootstrap.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigModule, ConfigService } from '@nestjs/config';
3 | import { EventEmitterModule } from '@nestjs/event-emitter';
4 | import { RedisEventPubSubModule } from './module/redis-event-pub-sub/redis-event-pub-sub.module';
5 | import { UserChatModule } from './module/user-chat/user-chat.module';
6 |
7 | @Module({
8 | imports: [
9 | ConfigModule.forRoot(),
10 | RedisEventPubSubModule.registerAsync({
11 | isGlobal: true,
12 | imports: [ConfigModule],
13 | useFactory: (configService: ConfigService) => ({
14 | host: configService.get('REDIS_HOST') || 'localhost',
15 | port: configService.get('REDIS_PORT') || '6379',
16 | }),
17 | inject: [ConfigService],
18 | }),
19 | EventEmitterModule.forRoot({
20 | global: true,
21 | }),
22 | UserChatModule,
23 | ],
24 | controllers: [],
25 | providers: [],
26 | })
27 | export class BootstrapModule {}
28 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { BootstrapModule } from './bootstrap.module';
3 |
4 | async function bootstrap() {
5 | const app = await NestFactory.create(BootstrapModule);
6 | await app.listen(3000);
7 | }
8 | bootstrap();
9 |
--------------------------------------------------------------------------------
/src/module/redis-event-pub-sub/event/emitter/contract/event-emitter.interface.ts:
--------------------------------------------------------------------------------
1 | export interface EventEmitterInterface {
2 | emit(eventName: string, data: any): void;
3 | }
4 |
--------------------------------------------------------------------------------
/src/module/redis-event-pub-sub/event/emitter/contract/publishable-event.interface.ts:
--------------------------------------------------------------------------------
1 | export interface PublishableEventInterface {
2 | readonly publishableEventName: string;
3 | }
4 |
--------------------------------------------------------------------------------
/src/module/redis-event-pub-sub/event/emitter/redis.event-emitter.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter2 } from '@nestjs/event-emitter';
2 | import { RedisClientType } from 'redis';
3 | import { EventEmitterInterface } from './contract/event-emitter.interface';
4 | import { PublishableEventInterface } from './contract/publishable-event.interface';
5 |
6 | export const EVENT_EMITTER_TOKEN = 'EVENT_EMITTER_TOKEN';
7 |
8 | export class RedisEventEmitter implements EventEmitterInterface {
9 | constructor(
10 | private redisPubClient: RedisClientType,
11 | private eventEmitter: EventEmitter2,
12 | ) {}
13 |
14 | async emit(eventName: string, payload: Record): Promise {
15 | this.eventEmitter.emit(eventName, payload);
16 |
17 | if (this.isPublishableEvent(payload)) {
18 | await this.redisPubClient.publish(
19 | payload.publishableEventName,
20 | JSON.stringify(payload),
21 | );
22 | }
23 | }
24 |
25 | private isPublishableEvent(event: any): event is PublishableEventInterface {
26 | return event.publishableEventName !== undefined;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/module/redis-event-pub-sub/event/subscriber/event-emitter-2.event-subscriber.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter2 } from '@nestjs/event-emitter';
2 | import { EventSubscriberInterface } from './event-subscriber.interface';
3 |
4 | export class EventEmitter2EventSubscriber implements EventSubscriberInterface {
5 | constructor(private eventEmitter: EventEmitter2) {}
6 |
7 | on(name: string, listener: any): void {
8 | this.eventEmitter.on(name, listener);
9 | }
10 |
11 | off(name: string, listener: any): void {
12 | this.eventEmitter.removeListener(name, listener);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/module/redis-event-pub-sub/event/subscriber/event-subscriber.interface.ts:
--------------------------------------------------------------------------------
1 | export const EVENT_SUBSCRIBER_TOKEN = 'EVENT_SUBSCRIBER_TOKEN';
2 |
3 | export interface EventSubscriberInterface {
4 | on(name: string, listener: any): void;
5 |
6 | off(name: string, listener: any): void;
7 | }
8 |
--------------------------------------------------------------------------------
/src/module/redis-event-pub-sub/redis-event-pub-sub.module-definition.ts:
--------------------------------------------------------------------------------
1 | import { ConfigurableModuleBuilder } from '@nestjs/common';
2 |
3 | export class RedisEventPubSubModuleOptions {
4 | readonly host: string;
5 | readonly port: string;
6 | }
7 |
8 | export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
9 | new ConfigurableModuleBuilder()
10 | .setExtras(
11 | {
12 | isGlobal: true,
13 | },
14 | (definition, extras) => ({
15 | ...definition,
16 | global: extras.isGlobal,
17 | }),
18 | )
19 | .build();
20 |
--------------------------------------------------------------------------------
/src/module/redis-event-pub-sub/redis-event-pub-sub.module.ts:
--------------------------------------------------------------------------------
1 | import { DynamicModule, Module } from '@nestjs/common';
2 | import { EventEmitter2 } from '@nestjs/event-emitter';
3 | import { RedisClientType, createClient } from 'redis';
4 | import { Writeable } from '../../shared/writable.type';
5 | import { PublishableEventInterface } from './event/emitter/contract/publishable-event.interface';
6 | import {
7 | EVENT_EMITTER_TOKEN,
8 | RedisEventEmitter,
9 | } from './event/emitter/redis.event-emitter';
10 | import { EventEmitter2EventSubscriber } from './event/subscriber/event-emitter-2.event-subscriber';
11 | import { EVENT_SUBSCRIBER_TOKEN } from './event/subscriber/event-subscriber.interface';
12 | import {
13 | ConfigurableModuleClass,
14 | MODULE_OPTIONS_TOKEN,
15 | RedisEventPubSubModuleOptions,
16 | } from './redis-event-pub-sub.module-definition';
17 |
18 | export const REDIS_PUB_CLIENT = 'REDIS_PUB_CLIENT';
19 | export const REDIS_SUB_CLIENT = 'REDIS_SUB_CLIENT';
20 | export const REDIS_EVENT_PUB_SUB_REGISTER_EVENT_OPTIONS =
21 | 'REDIS_EVENT_PUB_SUB_REGISTER_EVENT_OPTIONS';
22 |
23 | @Module({
24 | providers: [
25 | {
26 | provide: REDIS_EVENT_PUB_SUB_REGISTER_EVENT_OPTIONS,
27 | useFactory: (options: RedisEventPubSubModuleOptions) => options,
28 | inject: [MODULE_OPTIONS_TOKEN],
29 | },
30 | {
31 | provide: REDIS_PUB_CLIENT,
32 | useFactory: async (options: RedisEventPubSubModuleOptions) => {
33 | const client = createClient({
34 | url: `redis://${options.host}:${options.port}`,
35 | });
36 | client.on('error', (err) => console.error('Redis Client Error', err));
37 | await client.connect();
38 | return client;
39 | },
40 | inject: [MODULE_OPTIONS_TOKEN],
41 | },
42 | {
43 | provide: EVENT_EMITTER_TOKEN,
44 | useFactory: (
45 | redisPubClient: RedisClientType,
46 | eventEmitter: EventEmitter2,
47 | ) => {
48 | return new RedisEventEmitter(redisPubClient, eventEmitter);
49 | },
50 | inject: [REDIS_PUB_CLIENT, EventEmitter2],
51 | },
52 | {
53 | provide: EVENT_SUBSCRIBER_TOKEN,
54 | useFactory: (eventEmitterSub: EventEmitter2) => {
55 | return new EventEmitter2EventSubscriber(eventEmitterSub);
56 | },
57 | inject: [EventEmitter2],
58 | },
59 | ],
60 | exports: [
61 | REDIS_PUB_CLIENT,
62 | EVENT_EMITTER_TOKEN,
63 | EVENT_SUBSCRIBER_TOKEN,
64 | REDIS_EVENT_PUB_SUB_REGISTER_EVENT_OPTIONS,
65 | ],
66 | })
67 | export class RedisEventPubSubModule extends ConfigurableModuleClass {
68 | static registerEvents(eventsPublishableNames: string[]): DynamicModule {
69 | return {
70 | module: class {},
71 | providers: [
72 | {
73 | provide: REDIS_SUB_CLIENT,
74 | useFactory: async (
75 | options: RedisEventPubSubModuleOptions,
76 | eventEmitter: EventEmitter2,
77 | ) => {
78 | const client = createClient({
79 | url: `redis://${options.host}:${options.port}`,
80 | });
81 | client.on('error', (err) =>
82 | console.error('Redis Client Error', err),
83 | );
84 | await client.connect();
85 | for (const eventPublishableName of eventsPublishableNames) {
86 | await client.subscribe(eventPublishableName, (message) => {
87 | const normalizedMessage = JSON.parse(
88 | message,
89 | ) as PublishableEventInterface;
90 | delete (
91 | normalizedMessage as Writeable
92 | ).publishableEventName;
93 | eventEmitter.emit(eventPublishableName, normalizedMessage);
94 | });
95 | }
96 | return client;
97 | },
98 | inject: [REDIS_EVENT_PUB_SUB_REGISTER_EVENT_OPTIONS, EventEmitter2],
99 | },
100 | ],
101 | };
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/module/user-chat/event/new-message.event.ts:
--------------------------------------------------------------------------------
1 | import { PublishableEventInterface } from '../../redis-event-pub-sub/event/emitter/contract/publishable-event.interface';
2 |
3 | export class NewMessageEvent implements PublishableEventInterface {
4 | static publishableEventName = 'events:new-message';
5 |
6 | publishableEventName = NewMessageEvent.publishableEventName;
7 |
8 | constructor(public readonly message: string) {}
9 | }
10 |
--------------------------------------------------------------------------------
/src/module/user-chat/transport/http/action/send-message/request/send-message.http-request.ts:
--------------------------------------------------------------------------------
1 | import { IsString } from 'class-validator';
2 |
3 | export class SendMessageHttpRequest {
4 | @IsString()
5 | content: string;
6 | }
7 |
--------------------------------------------------------------------------------
/src/module/user-chat/transport/http/action/send-message/send-message.action.ts:
--------------------------------------------------------------------------------
1 | import { Body, Controller, Inject, Post } from '@nestjs/common';
2 | import { EventEmitterInterface } from '../../../../../redis-event-pub-sub/event/emitter/contract/event-emitter.interface';
3 | import { EVENT_EMITTER_TOKEN } from '../../../../../redis-event-pub-sub/event/emitter/redis.event-emitter';
4 | import { NewMessageEvent } from '../../../../event/new-message.event';
5 | import { SendMessageHttpRequest } from './request/send-message.http-request';
6 |
7 | @Controller('messages')
8 | export class SendMessageAction {
9 | constructor(
10 | @Inject(EVENT_EMITTER_TOKEN)
11 | private readonly eventEmitter: EventEmitterInterface,
12 | ) {}
13 |
14 | @Post()
15 | async handle(@Body() request: SendMessageHttpRequest) {
16 | await this.eventEmitter.emit(
17 | NewMessageEvent.name,
18 | new NewMessageEvent(request.content),
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/module/user-chat/transport/websocket/messages/messages.websocket-gateway.ts:
--------------------------------------------------------------------------------
1 | import { Inject } from '@nestjs/common';
2 | import {
3 | ConnectedSocket,
4 | SubscribeMessage,
5 | WebSocketGateway,
6 | } from '@nestjs/websockets';
7 | import { randomUUID } from 'crypto';
8 | import { Observable, from, map } from 'rxjs';
9 | import { PublishableEventInterface } from '../../../../redis-event-pub-sub/event/emitter/contract/publishable-event.interface';
10 | import {
11 | EVENT_SUBSCRIBER_TOKEN,
12 | EventSubscriberInterface,
13 | } from '../../../../redis-event-pub-sub/event/subscriber/event-subscriber.interface';
14 | import { NewMessageEvent } from '../../../event/new-message.event';
15 |
16 | export enum WebsocketEventSubscribeList {
17 | FETCH_EVENTS_MESSAGES = 'fetch-events-messages',
18 | EVENTS_MESSAGES_STREAM = 'events-messages-stream',
19 | }
20 |
21 | @WebSocketGateway({
22 | pingInterval: 30000,
23 | pingTimeout: 5000,
24 | cors: {
25 | origin: '*',
26 | },
27 | })
28 | export class MessagesWebsocketGateway {
29 | constructor(
30 | @Inject(EVENT_SUBSCRIBER_TOKEN)
31 | private eventSubscriber: EventSubscriberInterface,
32 | ) {}
33 |
34 | @SubscribeMessage(WebsocketEventSubscribeList.FETCH_EVENTS_MESSAGES)
35 | async streamMessagesData(@ConnectedSocket() client: any) {
36 | const stream$ = this.createWebsocketStreamFromEventFactory(
37 | client,
38 | this.eventSubscriber,
39 | NewMessageEvent.publishableEventName,
40 | );
41 |
42 | const event = WebsocketEventSubscribeList.EVENTS_MESSAGES_STREAM;
43 | return from(stream$).pipe(map((data) => ({ event, data })));
44 | }
45 |
46 | private createWebsocketStreamFromEventFactory(
47 | client: any,
48 | eventSubscriber: EventSubscriberInterface,
49 | eventName: string,
50 | ): Observable {
51 | return new Observable((observer) => {
52 | const dynamicListener = (message: PublishableEventInterface) => {
53 | observer.next(message);
54 | };
55 |
56 | eventSubscriber.on(eventName, dynamicListener);
57 |
58 | client.on('disconnect', () => {
59 | eventSubscriber.off(eventName, dynamicListener);
60 | });
61 | });
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/module/user-chat/user-chat.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { RedisEventPubSubModule } from '../redis-event-pub-sub/redis-event-pub-sub.module';
3 | import { NewMessageEvent } from './event/new-message.event';
4 | import { SendMessageAction } from './transport/http/action/send-message/send-message.action';
5 | import { MessagesWebsocketGateway } from './transport/websocket/messages/messages.websocket-gateway';
6 |
7 | @Module({
8 | imports: [
9 | RedisEventPubSubModule.registerEvents([
10 | NewMessageEvent.publishableEventName,
11 | ]),
12 | ],
13 | controllers: [SendMessageAction],
14 | providers: [MessagesWebsocketGateway],
15 | })
16 | export class UserChatModule {}
17 |
--------------------------------------------------------------------------------
/src/shared/writable.type.ts:
--------------------------------------------------------------------------------
1 | export type Writeable = { -readonly [P in keyof T]: T[P] };
2 |
--------------------------------------------------------------------------------
/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { INestApplication } from '@nestjs/common';
2 | import { Test, TestingModule } from '@nestjs/testing';
3 | import * as request from 'supertest';
4 | import { AppModule } from '../src/bootstrap.module';
5 |
6 | describe('AppController (e2e)', () => {
7 | let app: INestApplication;
8 |
9 | beforeEach(async () => {
10 | const moduleFixture: TestingModule = await Test.createTestingModule({
11 | imports: [AppModule],
12 | }).compile();
13 |
14 | app = moduleFixture.createNestApplication();
15 | await app.init();
16 | });
17 |
18 | it('/ (GET)', () => {
19 | return request(app.getHttpServer())
20 | .get('/')
21 | .expect(200)
22 | .expect('Hello World!');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "es2017",
10 | "sourceMap": true,
11 | "outDir": "./dist",
12 | "baseUrl": "./",
13 | "incremental": true,
14 | "skipLibCheck": true,
15 | "strictNullChecks": false,
16 | "noImplicitAny": false,
17 | "strictBindCallApply": false,
18 | "forceConsistentCasingInFileNames": false,
19 | "noFallthroughCasesInSwitch": false
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ws_client.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------