Предыдущая статья: Подключение PrismaORM в NestJS-mod приложение и проверка его работы через REST
Подключение генератора 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...
Ссылки
https://nestjs.com - официальный сайт фреймворка
https://nestjs-mod.com - официальный сайт дополнительных утилит
https://github.com/nestjs-mod/nestjs-mod-fullstack - проект из поста
https://github.com/nestjs-mod/nestjs-mod-fullstack/commit/0353b23b1b65d1ff8e6e5f6185e235bbe05cf523 - коммит на текущие изменения
csl
При вызове сообщения о Deprecated, memory leak, no support. Есть альтернативная команда без warning ?
npm -v
9.2.0
node -v
18.19.1
kaufmanendy Автор
Сейчас эта команда запускается в среде продакшена, в итоговом варианте при деплое на серваке она будет запускаться в изолированном контейнере и никак не повлияет на продакшен установку зависимостей, там не будет варнинга этого как и самой зависимости
csl
Итог уже на github (видел, к этому посту ещё и ссылку на коммит)?
kaufmanendy Автор
До итогового варианта еще очень далеко) ну вырезание зависимостей в докер образе уже есть в неком варианте https://github.com/nestjs-mod/nestjs-mod-fullstack/blob/master/.docker/server.Dockerfile, я на хабре отрицательную карму имею поэтому раз в неделю могу только сюда писать)