Skip to main content

Добавление Swagger документации в NestJS-mod приложение и генерация REST-клиента для Angular-приложения

Подключение генератора Swagger документации к бэкенду.

Подключение https://www.npmjs.com/package/prisma-class-generator для генерации DTO из Prisma - схемы.

Создание nx библиотеки для работы с бэкендом.

Подключение https://www.npmjs.com/package/@openapitools/openapi-generator-cli для генерации SDK для работы с бэкендом.

1. Устанавливаем все необходимые пакеты

Команды

# Install all need dependencies
npm i --save @nestjs/swagger

# Install all need dev-dependencies
npm i --save-dev prisma-class-generator @openapitools/openapi-generator-cli

# Install all need peer-dependencies
npm i --save class-transformer class-validator

Вывод консоли

$ npm i --save @nestjs/swagger

added 5 packages, removed 1 package, and audited 2512 packages in 14s

300 packages are looking for funding
run `npm fund` for details

17 vulnerabilities (6 moderate, 11 high)

To address issues that do not require attention, run:
npm audit fix

To address all issues (including breaking changes), run:
npm audit fix --force

Run `npm audit` for details.

$ npm i --save-dev prisma-class-generator @openapitools/openapi-generator-cli

added 50 packages, and audited 2562 packages in 15s

304 packages are looking for funding
run `npm fund` for details

18 vulnerabilities (6 moderate, 12 high)

To address issues that do not require attention, run:
npm audit fix

To address all issues (including breaking changes), run:
npm audit fix --force

Run `npm audit` for details.

$ npm i --save class-transformer class-validator

added 1 package, removed 1 package, and audited 2768 packages in 9s

331 packages are looking for funding
run `npm fund` for details

18 vulnerabilities (6 moderate, 12 high)

To address issues that do not require attention, run:
npm audit fix

To address all issues (including breaking changes), run:
npm audit fix --force

Run `npm audit` for details.

2. Создаем Angular библиотеку для работы с бэкендом

Эта библиотека будет использоваться для работы с бэкендом.

Команды

# Create Angular library
./node_modules/.bin/nx g @nx/angular:library app-angular-rest-sdk --buildable --publishable --directory=libs/sdk/app-angular-rest-sdk --simpleName=true --projectNameAndRootFormat=as-provided --strict=true --prefix=app-angular-rest-sdk --standalone=true --selector=app-angular-rest-sdk --changeDetection=OnPush --importPath=@nestjs-mod-fullstack/app-angular-rest-sdk

# Change file with test options
rm -rf libs/sdk/app-angular-rest-sdk/src/test-setup.ts
cp apps/client/src/test-setup.ts libs/sdk/app-angular-rest-sdk/src/test-setup.ts

Вывод консоли

$ ./node_modules/.bin/nx g @nx/angular:library app-angular-rest-sdk --buildable --publishable --directory=libs/sdk/app-angular-rest-sdk --simpleName=true --projectNameAndRootFormat=as-provided --strict=true --prefix=app-angular-rest-sdk --standalone=true --selector=app-angular-rest-sdk --changeDetection=OnPush --importPath=@nestjs-mod-fullstack/app-angular-rest-sdk

NX Generating @nx/angular:library

UPDATE nx.json
CREATE libs/sdk/app-angular-rest-sdk/project.json
CREATE libs/sdk/app-angular-rest-sdk/README.md
CREATE libs/sdk/app-angular-rest-sdk/ng-package.json
CREATE libs/sdk/app-angular-rest-sdk/package.json
CREATE libs/sdk/app-angular-rest-sdk/tsconfig.json
CREATE libs/sdk/app-angular-rest-sdk/tsconfig.lib.json
CREATE libs/sdk/app-angular-rest-sdk/tsconfig.lib.prod.json
CREATE libs/sdk/app-angular-rest-sdk/src/index.ts
CREATE libs/sdk/app-angular-rest-sdk/jest.config.ts
CREATE libs/sdk/app-angular-rest-sdk/src/test-setup.ts
CREATE libs/sdk/app-angular-rest-sdk/tsconfig.spec.json
CREATE libs/sdk/app-angular-rest-sdk/src/lib/app-angular-rest-sdk/app-angular-rest-sdk.component.css
CREATE libs/sdk/app-angular-rest-sdk/src/lib/app-angular-rest-sdk/app-angular-rest-sdk.component.html
CREATE libs/sdk/app-angular-rest-sdk/src/lib/app-angular-rest-sdk/app-angular-rest-sdk.component.spec.ts
CREATE libs/sdk/app-angular-rest-sdk/src/lib/app-angular-rest-sdk/app-angular-rest-sdk.component.ts
CREATE libs/sdk/app-angular-rest-sdk/.eslintrc.json
UPDATE package.json
UPDATE tsconfig.base.json

added 31 packages, removed 37 packages, and audited 2556 packages in 12s

304 packages are looking for funding
run `npm fund` for details

16 vulnerabilities (4 moderate, 12 high)

To address issues that do not require attention, run:
npm audit fix

To address all issues (including breaking changes), run:
npm audit fix --force

Run `npm audit` for details.

NX 👀 View Details of app-angular-rest-sdk

Run "nx show project app-angular-rest-sdk" to view details about this project.

3. Создаем NestJS библиотеку для работы с бэкендом

Эта библиотека будет использоваться из E2E-тестов бэкенда.

Команды

# Create NestJS library
./node_modules/.bin/nx g @nestjs-mod/schematics:library app-rest-sdk --buildable --publishable --directory=libs/sdk/app-rest-sdk --simpleName=true --projectNameAndRootFormat=as-provided --strict=true

Вывод консоли

$ ./node_modules/.bin/nx g @nestjs-mod/schematics:library app-rest-sdk --buildable --publishable --directory=libs/sdk/app-rest-sdk --simpleName=true --projectNameAndRootFormat=as-provided --strict=true

NX Generating @nestjs-mod/schematics:library

CREATE libs/sdk/app-rest-sdk/tsconfig.json
CREATE libs/sdk/app-rest-sdk/src/index.ts
CREATE libs/sdk/app-rest-sdk/tsconfig.lib.json
CREATE libs/sdk/app-rest-sdk/README.md
CREATE libs/sdk/app-rest-sdk/package.json
CREATE libs/sdk/app-rest-sdk/project.json
CREATE libs/sdk/app-rest-sdk/.eslintrc.json
CREATE libs/sdk/app-rest-sdk/jest.config.ts
CREATE libs/sdk/app-rest-sdk/tsconfig.spec.json
UPDATE tsconfig.base.json
CREATE libs/sdk/app-rest-sdk/src/lib/app-rest-sdk.configuration.ts
CREATE libs/sdk/app-rest-sdk/src/lib/app-rest-sdk.environments.ts
CREATE libs/sdk/app-rest-sdk/src/lib/app-rest-sdk.module.ts

4. Добавляем дополнительный генератор DTO в Prisma-схему

Обновленный файл apps/server/src/prisma/app-schema.prisma

generator client {
provider = "prisma-client-js"
output = "../../../../node_modules/@prisma/app-client"
engineType = "binary"
}

generator prismaClassGenerator {
provider = "prisma-class-generator"
output = "../app/generated/rest/dto"
dryRun = "false"
separateRelationFields = "false"
makeIndexFile = "file"
}

datasource db {
provider = "postgres"
url = env("SERVER_APP_DATABASE_URL")
}

model AppDemo {
id String @id(map: "PK_APP_DEMO") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
name String @unique(map: "UQ_APP_DEMO") @db.VarChar(128)
createdAt DateTime @default(now()) @db.Timestamp(6)
updatedAt DateTime @default(now()) @db.Timestamp(6)
}

model migrations {
installed_rank Int @id(map: "__migrations_pk")
version String? @db.VarChar(50)
description String @db.VarChar(200)
type String @db.VarChar(20)
script String @db.VarChar(1000)
checksum Int?
installed_by String @db.VarChar(100)
installed_on DateTime @default(now()) @db.Timestamp(6)
execution_time Int
success Boolean

@@index([success], map: "__migrations_s_idx")
@@map("__migrations")
}

5. Добавляем поддержку генерации Swagger документации в бэкенд коде

Обновленный файла apps/server/src/main.ts

import { DefaultNestApplicationInitializer, DefaultNestApplicationListener, InfrastructureMarkdownReportGenerator, PACKAGE_JSON_FILE, ProjectUtils, bootstrapNestApplication, isInfrastructureMode } from '@nestjs-mod/common';
import { DOCKER_COMPOSE_FILE, DockerCompose, DockerComposePostgreSQL } from '@nestjs-mod/docker-compose';
import { FLYWAY_JS_CONFIG_FILE, Flyway } from '@nestjs-mod/flyway';
import { NestjsPinoLoggerModule } from '@nestjs-mod/pino';
import { ECOSYSTEM_CONFIG_FILE, Pm2 } from '@nestjs-mod/pm2';
import { FakePrismaClient, PRISMA_SCHEMA_FILE, PrismaModule } from '@nestjs-mod/prisma';
import { TerminusHealthCheckModule } from '@nestjs-mod/terminus';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { MemoryHealthIndicator } from '@nestjs/terminus';
import { writeFileSync } from 'fs';
import { join, resolve } from 'path';
import { AppModule } from './app/app.module';

const appFeatureName = 'app';
const rootFolder = join(__dirname, '..', '..', '..');
const appFolder = join(rootFolder, 'apps', 'server');

bootstrapNestApplication({
modules: {
system: [
ProjectUtils.forRoot({
staticConfiguration: {
applicationPackageJsonFile: join(appFolder, PACKAGE_JSON_FILE),
packageJsonFile: join(rootFolder, PACKAGE_JSON_FILE),
envFile: join(rootFolder, '.env'),
},
}),
DefaultNestApplicationInitializer.forRoot({
staticConfiguration: { bufferLogs: true },
}),
NestjsPinoLoggerModule.forRoot(),
TerminusHealthCheckModule.forRootAsync({
configurationFactory: (memoryHealthIndicator: MemoryHealthIndicator) => ({
standardHealthIndicators: [
{
name: 'memory_heap',
check: () => memoryHealthIndicator.checkHeap('memory_heap', 150 * 1024 * 1024),
},
],
}),
inject: [MemoryHealthIndicator],
}),
DefaultNestApplicationListener.forRoot({
staticConfiguration: {
// When running in infrastructure mode, the backend server does not start.
mode: isInfrastructureMode() ? 'silent' : 'listen',
async preListen(options) {
if (options.app) {
options.app.setGlobalPrefix('api');

const swaggerConf = new DocumentBuilder().addBearerAuth().build();

const document = SwaggerModule.createDocument(options.app, swaggerConf);

SwaggerModule.setup('swagger', options.app, document);

if (isInfrastructureMode()) {
writeFileSync(resolve(__dirname, '..', '..', '..', 'app-swagger.json'), JSON.stringify(document));
}
}
},
},
}),
],
core: [
PrismaModule.forRoot({
staticConfiguration: {
schemaFile: join(appFolder, 'src', 'prisma', `${appFeatureName}-${PRISMA_SCHEMA_FILE}`),
featureName: appFeatureName,
prismaModule: isInfrastructureMode() ? { PrismaClient: FakePrismaClient } : import(`@prisma/app-client`),
addMigrationScripts: false,
},
}),
],
feature: [AppModule.forRoot()],
infrastructure: [
InfrastructureMarkdownReportGenerator.forRoot({
staticConfiguration: {
markdownFile: join(appFolder, 'INFRASTRUCTURE.MD'),
skipEmptySettings: true,
},
}),
Pm2.forRoot({
configuration: {
ecosystemConfigFile: join(rootFolder, ECOSYSTEM_CONFIG_FILE),
applicationScriptFile: join('dist/apps/server/main.js'),
},
}),
DockerCompose.forRoot({
configuration: {
dockerComposeFileVersion: '3',
dockerComposeFile: join(appFolder, DOCKER_COMPOSE_FILE),
},
}),
DockerComposePostgreSQL.forRoot(),
DockerComposePostgreSQL.forFeature({
featureModuleName: appFeatureName,
}),
Flyway.forRoot({
staticConfiguration: {
featureName: appFeatureName,
migrationsFolder: join(appFolder, 'src', 'migrations'),
configFile: join(rootFolder, FLYWAY_JS_CONFIG_FILE),
},
}),
],
},
});

6. Добавляем команду для генерации SDK для работы с бэкендом

Обновленная часть файла apps/server/project.json

{
"generate": {
"executor": "nx:run-commands",
"options": {
"commands": ["./node_modules/.bin/prisma generate --schema=./apps/server/src/prisma/app-schema.prisma", "./node_modules/.bin/rucken make-ts-list", "export NESTJS_MODE=infrastructure && ./node_modules/.bin/nx serve server --host=0.0.0.0 --watch=false", "rm -rf ./libs/sdk/app-angular-rest-sdk/src/lib && mkdir ./libs/sdk/app-angular-rest-sdk/src/lib && ./node_modules/.bin/openapi-generator-cli generate -i ./app-swagger.json -g typescript-angular -o ./libs/sdk/app-angular-rest-sdk/src/lib --additional-properties=apiModulePrefix=RestClient,configurationPrefix=RestClient,fileNaming=kebab-case,modelFileSuffix=.interface,modelSuffix=Interface,enumNameSuffix=Type,enumPropertyNaming=original,serviceFileSuffix=-rest.service,serviceSuffix=RestService", "rm -rf ./libs/sdk/app-rest-sdk/src/lib && mkdir ./libs/sdk/app-rest-sdk/src/lib && ./node_modules/.bin/openapi-generator-cli generate -i ./app-swagger.json -g typescript-axios -o ./libs/sdk/app-rest-sdk/src/lib"],
"parallel": false,
"envFile": "./.env",
"color": true
}
}
}

7. Для работы Swagger генератора нужно установить Java

Команды

sudo apt install default-jre

Вывод консоли

$ sudo apt install default-jre
[sudo] password for endy:
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
default-jre-headless openjdk-11-jre openjdk-11-jre-headless
Suggested packages:
fonts-ipafont-gothic fonts-ipafont-mincho fonts-wqy-microhei | fonts-wqy-zenhei
The following NEW packages will be installed:
default-jre default-jre-headless openjdk-11-jre openjdk-11-jre-headless
0 upgraded, 4 newly installed, 0 to remove and 9 not upgraded.
Need to get 38,5 MB of archives.
After this operation, 177 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://ru.archive.ubuntu.com/ubuntu focal-updates/main amd64 openjdk-11-jre-headless amd64 11.0.24+8-1ubuntu3~20.04 [38,3 MB]
Get:2 http://ru.archive.ubuntu.com/ubuntu focal/main amd64 default-jre-headless amd64 2:1.11-72 [3 192 B]
Get:3 http://ru.archive.ubuntu.com/ubuntu focal-updates/main amd64 openjdk-11-jre amd64 11.0.24+8-1ubuntu3~20.04 [195 kB]
Get:4 http://ru.archive.ubuntu.com/ubuntu focal/main amd64 default-jre amd64 2:1.11-72 [1 084 B]
Fetched 38,5 MB in 6s (6 643 kB/s)
Selecting previously unselected package openjdk-11-jre-headless:amd64.
(Reading database ... 224645 files and directories currently installed.)
Preparing to unpack .../openjdk-11-jre-headless_11.0.24+8-1ubuntu3~20.04_amd64.deb ...
Unpacking openjdk-11-jre-headless:amd64 (11.0.24+8-1ubuntu3~20.04) ...
Selecting previously unselected package default-jre-headless.
Preparing to unpack .../default-jre-headless_2%3a1.11-72_amd64.deb ...
Unpacking default-jre-headless (2:1.11-72) ...
Selecting previously unselected package openjdk-11-jre:amd64.
Preparing to unpack .../openjdk-11-jre_11.0.24+8-1ubuntu3~20.04_amd64.deb ...
Unpacking openjdk-11-jre:amd64 (11.0.24+8-1ubuntu3~20.04) ...
Selecting previously unselected package default-jre.
Preparing to unpack .../default-jre_2%3a1.11-72_amd64.deb ...
Unpacking default-jre (2:1.11-72) ...
Setting up openjdk-11-jre-headless:amd64 (11.0.24+8-1ubuntu3~20.04) ...
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/java to provide /usr/bin/java (java) in auto mode
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs to provide /usr/bin/jjs (jjs) in auto mode
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/keytool to provide /usr/bin/keytool (keytool) in auto mode
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/rmid to provide /usr/bin/rmid (rmid) in auto mode
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/rmiregistry to provide /usr/bin/rmiregistry (rmiregistry) in auto mode
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/pack200 to provide /usr/bin/pack200 (pack200) in auto mode
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/unpack200 to provide /usr/bin/unpack200 (unpack200) in auto mode
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/lib/jexec to provide /usr/bin/jexec (jexec) in auto mode
Setting up openjdk-11-jre:amd64 (11.0.24+8-1ubuntu3~20.04) ...
Setting up default-jre-headless (2:1.11-72) ...
Setting up default-jre (2:1.11-72) ...
Processing triggers for mime-support (3.64ubuntu1) ...
Processing triggers for hicolor-icon-theme (0.17-2) ...
Processing triggers for gnome-menus (3.36.0-1ubuntu1) ...
Processing triggers for desktop-file-utils (0.24-1ubuntu3) ...

8. Так как eslint правила библиотек с SDK отличаются от правил приложения, добавляем папки с SDK в .eslintignore

Обновленный файла .eslintignore

node_modules
libs/sdk/app-rest-sdk/src/lib
libs/sdk/app-angular-rest-sdk/src/lib

9. Так как генераторы создают тайпскрипт конфигурации для тестов, они автоматически попадают в общий index.ts, нам нужно добавить их в исключения генератора index.ts файлов

Обновленный файла rucken.json

{
"makeTsList": {
"indexFileName": "index",
"excludes": [
"test-setup.ts", // <-- updates
"*node_modules*",
"*public_api.ts*",
"*.spec*",
"environment*",
"*e2e*",
"*.stories.ts",
"*.d.ts"
]
}
}

10. Запускаем все генераторы

Команды

npm run generate

Вывод консоли

$ npm run generate

> @nestjs-mod-fullstack/source@0.0.0 generate
> ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=generate --skip-nx-cache=true && npm run make-ts-list && npm run lint:fix


✔ nx run server:generate (14s)

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

NX Successfully ran target generate for project server (14s)


> @nestjs-mod-fullstack/source@0.0.0 make-ts-list
> ./node_modules/.bin/rucken make-ts-list


> @nestjs-mod-fullstack/source@0.0.0 lint:fix
> npm run tsc:lint && ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=lint --fix


> @nestjs-mod-fullstack/source@0.0.0 tsc:lint
> ./node_modules/.bin/tsc --noEmit -p tsconfig.base.json


✔ nx run client:lint [existing outputs match the cache, left as is]
✔ nx run app-angular-rest-sdk:lint [existing outputs match the cache, left as is]
✔ nx run server-e2e:lint (1s)
✔ nx run server:lint (1s)

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

NX Successfully ran target lint for 4 projects (1s)

With additional flags:
--fix=true

Nx read the output from the cache instead of running the command for 2 out of 4 tasks.


NX Nx detected flaky tasks

client:lint
app-angular-rest-sdk:lint
server-e2e:lint

Flaky tasks can disrupt your CI pipeline. Automatically retry them with Nx Cloud. Learn more at https://nx.dev/ci/features/flaky-tasks

11. Прописываем созданные DTO для методов в контроллере AppController и создаем все недостающие DTO

Обновленный файла apps/server/src/app/app.controller.ts

import { Controller, Delete, Get, Param, Post } from '@nestjs/common';

import { InjectPrismaClient } from '@nestjs-mod/prisma';
import { ApiCreatedResponse, ApiProperty, ApiResponse } from '@nestjs/swagger';
import { PrismaClient as AppPrismaClient } from '@prisma/app-client';
import { randomUUID } from 'crypto';
import { AppService } from './app.service';
import { AppDemo } from './generated/rest/dto/app_demo';

export class AppData {
// <- updates
@ApiProperty({ type: String })
message: string;
}

@Controller()
export class AppController {
constructor(
@InjectPrismaClient()
private readonly appPrismaClient: AppPrismaClient,
private readonly appService: AppService
) {}

@Get()
@ApiResponse({ type: AppData }) // <- updates
getData() {
return this.appService.getData();
}

@Post('/demo')
@ApiCreatedResponse({ type: AppDemo }) // <- updates
async demoCreateOne() {
return await this.appPrismaClient.appDemo.create({ data: { name: 'demo name' + randomUUID() } });
}

@Get('/demo/:id')
@ApiResponse({ type: AppDemo }) // <- updates
async demoFindOne(@Param('id') id: string) {
return await this.appPrismaClient.appDemo.findFirstOrThrow({ where: { id } });
}

@Delete('/demo/:id')
@ApiResponse({ type: AppDemo }) // <- updates
async demoDeleteOne(@Param('id') id: string) {
return await this.appPrismaClient.appDemo.delete({ where: { id } });
}

@Get('/demo')
@ApiResponse({ type: AppDemo, isArray: true }) // <- updates
async demoFindMany() {
return await this.appPrismaClient.appDemo.findMany();
}
}

12. Подключаем модуль SDK для работы с бэкендом в Angular приложение

Обновленный файла apps/client/src/app/app.config.ts

import { provideHttpClient } from '@angular/common/http';
import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
import { provideClientHydration } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { RestClientApiModule, RestClientConfiguration } from '@nestjs-mod-fullstack/app-angular-rest-sdk';
import { appRoutes } from './app.routes';

export const appConfig: ApplicationConfig = {
providers: [
provideClientHydration(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(appRoutes),
provideHttpClient(),
importProvidersFrom(
RestClientApiModule.forRoot(
() =>
new RestClientConfiguration({
basePath: 'http://localhost:3000',
})
)
),
],
};

13. Меняем HttpClient на DefaultRestService в компоненте Angular приложения которая работает с бэкендом

Обновленный файла apps/client/src/app/app.component.ts

import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { RouterModule } from '@angular/router';
import { DefaultRestService } from '@nestjs-mod-fullstack/app-angular-rest-sdk';
import { NxWelcomeComponent } from './nx-welcome.component';

@Component({
standalone: true,
imports: [NxWelcomeComponent, RouterModule],
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
encapsulation: ViewEncapsulation.None,
})
export class AppComponent implements OnInit {
title = 'client';
serverMessage!: string;

constructor(private readonly defaultRestService: DefaultRestService) {}

ngOnInit() {
this.defaultRestService.appControllerGetData().subscribe((result) => (this.serverMessage = result.message));
}
}

14. Подключаем SDK для работы с бэкендом в юнит-тесты Angular приложения

Обновленный файла apps/client/src/app/app.component.spec.ts

import { HttpClientModule } from '@angular/common/http';
import { TestBed } from '@angular/core/testing';
import { RouterModule } from '@angular/router';
import { RestClientApiModule, RestClientConfiguration } from '@nestjs-mod-fullstack/app-angular-rest-sdk';
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';

describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
AppComponent,
NxWelcomeComponent,
RouterModule.forRoot([]),
HttpClientModule, // <- updates
RestClientApiModule.forRoot(
// <- updates
() =>
new RestClientConfiguration({
basePath: 'http://localhost:3000',
})
),
],
}).compileComponents();
});

it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Welcome client');
});

it(`should have as title 'client'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('client');
});
});

15. Подключаем SDK для работы с бэкендом в E2E-тесты NestJS приложение

Обновленный файла apps/server-e2e/src/server/server.spec.ts

import { Configuration, DefaultApi } from '@nestjs-mod-fullstack/app-rest-sdk';

describe('GET /api', () => {
const defaultApi = new DefaultApi(new Configuration({ basePath: '/api' }));
let newDemoObject: { id: string };

it('should return a message', async () => {
const res = await defaultApi.appControllerGetData();

expect(res.status).toBe(200);
expect(res.data).toEqual({ message: 'Hello API' });
});

it('should create and return a demo object', async () => {
const res = await defaultApi.appControllerDemoCreateOne();

expect(res.status).toBe(201);
expect(res.data.name).toContain('demo name');

newDemoObject = res.data;
});

it('should get demo object by id', async () => {
const res = await defaultApi.appControllerDemoFindOne(newDemoObject.id);

expect(res.status).toBe(200);
expect(res.data).toMatchObject(newDemoObject);
});

it('should get all demo object', async () => {
const res = await defaultApi.appControllerDemoFindMany();

expect(res.status).toBe(200);
expect(res.data.filter((row) => row.id === newDemoObject.id)).toMatchObject([newDemoObject]);
});

it('should delete demo object by id', async () => {
const res = await defaultApi.appControllerDemoDeleteOne(newDemoObject.id);

expect(res.status).toBe(200);
expect(res.data).toMatchObject(newDemoObject);
});

it('should get all demo object', async () => {
const res = await defaultApi.appControllerDemoFindMany();

expect(res.status).toBe(200);
expect(res.data.filter((row) => row.id === newDemoObject.id)).toMatchObject([]);
});
});

16. Запускаем юнит-тесты для NestJS и Angular приложений

Команды

npm run test

Вывод консоли

$ npm run test

> @nestjs-mod-fullstack/source@0.0.0 test
> ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=test --skip-nx-cache=true --passWithNoTests --output-style=stream-without-prefixes



> nx run app-angular-rest-sdk:test --passWithNoTests


> nx run app-rest-sdk:test --passWithNoTests


> nx run client:test --passWithNoTests

NX Running target test for 4 projects

✔ nx run app-angular-rest-sdk:test (2s)

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

✔ nx run app-rest-sdk:test (2s)

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

NX Running target test for 4 projects

With additional flags:
--passWithNoTests=true

→ Executing 2/2 remaining tasks in parallel...
✔ nx run client:test (5s)


——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

NX Running target test for 4 projects

With additional flags:
✔ nx run server:test (5s)

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

NX Successfully ran target test for 4 projects (7s)

With additional flags:
--passWithNoTests=true

17. Запускаем E2E-тесты для NestJS и Angular приложений

Команды

./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=e2e --skip-nx-cache=true --output-style=stream-without-prefixes

Вывод консоли

$ ./node_modules/.bin/nx run-many --exclude=@nestjs-mod-fullstack/source --all -t=e2e --skip-nx-cache=true --output-style=stream-without-prefixes


> nx run client-e2e:e2e

> playwright test

NX Running target e2e for 2 projects and 1 task they depend on


NX Running target e2e for 2 projects and 1 task they depend on

→ Executing 1/3 remaining tasks...

⠴ nx run client-e2e:e2e
✔ nx run client-e2e:e2e (7s)

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
✔ nx run server:build:production (3s)

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————



——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

NX Running target e2e for 2 projects and 1 task they depend on

→ Executing 1/1 remaining tasks...

⠦ nx run server-e2e:e2e

✔ 2/2 succeeded [0 read from cache]

PASS server-e2e apps/server-e2e/src/server/server.spec.ts
GET /api
✓ should return a message (27 ms)
✔ nx run server-e2e:e2e (2s)

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

NX Successfully ran target e2e for 2 projects and 1 task they depend on (12s)


NX Nx detected a flaky task

server-e2e:e2e

Flaky tasks can disrupt your CI pipeline. Automatically retry them with Nx Cloud. Learn more at https://nx.dev/ci/features/flaky-tasks

Картинок в посте нет, проверка работы приложений происходит через тесты, Swagger документация доступна по адресу: http://localhost:3000/swagger.

В следующем посте я соберу приложения на NestJS и Angular и запущу их в двух вариантах: через PM2 и через Docker Compose...

Ссылки

#nestjs #angular #nestjsmod #fullstack #2024-08-14