From f160dc52f1bcfbc5b46ba89421eb545c5d003c24 Mon Sep 17 00:00:00 2001 From: Artem Niehrieiev Date: Fri, 29 Aug 2025 09:26:19 +0000 Subject: [PATCH] feat: add throttling support with @nestjs/throttler and apply limits to company info endpoint --- backend/package.json | 1 + backend/src/app.module.ts | 16 +++++++++++++++- .../company-info/company-info.controller.ts | 2 ++ .../get-full-user-company-info.use.case.ts | 4 ++-- yarn.lock | 12 ++++++++++++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/backend/package.json b/backend/package.json index ec67cbfcc..21cf8caa3 100644 --- a/backend/package.json +++ b/backend/package.json @@ -42,6 +42,7 @@ "@nestjs/platform-express": "11.1.6", "@nestjs/schedule": "^6.0.0", "@nestjs/swagger": "^11.2.0", + "@nestjs/throttler": "^6.4.0", "@nestjs/typeorm": "^11.0.0", "@nestjsx/crud": "4.6.2", "@rocketadmin/shared-code": "workspace:*", diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index e9ca85130..f1f7de493 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -1,5 +1,5 @@ import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; -import { APP_INTERCEPTOR } from '@nestjs/core'; +import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core'; import { ScheduleModule } from '@nestjs/schedule'; import { DataSource } from 'typeorm'; import { AppController } from './app.controller.js'; @@ -36,10 +36,20 @@ import { SaasModule } from './microservices/saas-microservice/saas.module.js'; import { AppLoggerMiddleware } from './middlewares/logging-middleware/app-logger-middlewate.js'; import { DatabaseModule } from './shared/database/database.module.js'; import { GetHelloUseCase } from './use-cases-app/get-hello.use.case.js'; +import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; @Module({ imports: [ ScheduleModule.forRoot(), + ThrottlerModule.forRoot({ + throttlers: [ + { + ttl: 60000, + limit: 10, + }, + ], + skipIf: () => true, + }), ConnectionModule, ConnectionPropertiesModule, ConversionModule, @@ -75,6 +85,10 @@ import { GetHelloUseCase } from './use-cases-app/get-hello.use.case.js'; provide: APP_INTERCEPTOR, useClass: TimeoutInterceptor, }, + { + provide: APP_GUARD, + useClass: ThrottlerGuard, + }, { provide: BaseType.GLOBAL_DB_CONTEXT, useClass: GlobalDatabaseContext, diff --git a/backend/src/entities/company-info/company-info.controller.ts b/backend/src/entities/company-info/company-info.controller.ts index e17e927a4..6a094f4fc 100644 --- a/backend/src/entities/company-info/company-info.controller.ts +++ b/backend/src/entities/company-info/company-info.controller.ts @@ -21,6 +21,7 @@ import { } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { Throttle } from '@nestjs/throttler'; import { Request, Response } from 'express'; import { UseCaseType } from '../../common/data-injection.tokens.js'; import { SlugUuid } from '../../decorators/slug-uuid.decorator.js'; @@ -204,6 +205,7 @@ export class CompanyInfoController { type: FoundUserFullCompanyInfoDs, }) @UseGuards(CompanyUserGuard) + @Throttle({ default: { limit: 5, ttl: 60000 } }) @Get('my/full') async getUserCompanies(@UserId() userId: string): Promise { return await this.getUserFullCompanyInfoUseCase.execute(userId); diff --git a/backend/src/entities/company-info/use-cases/get-full-user-company-info.use.case.ts b/backend/src/entities/company-info/use-cases/get-full-user-company-info.use.case.ts index 59ab6b795..cf63c89c8 100644 --- a/backend/src/entities/company-info/use-cases/get-full-user-company-info.use.case.ts +++ b/backend/src/entities/company-info/use-cases/get-full-user-company-info.use.case.ts @@ -1,4 +1,4 @@ -import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Inject, Injectable, Scope } from '@nestjs/common'; import AbstractUseCase from '../../../common/abstract-use.case.js'; import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js'; import { BaseType } from '../../../common/data-injection.tokens.js'; @@ -14,7 +14,7 @@ import { CompanyInfoEntity } from '../company-info.entity.js'; import { buildFoundCompanyFullInfoDs, buildFoundCompanyInfoDs } from '../utils/build-found-company-info-ds.js'; import { IGetUserFullCompanyInfo } from './company-info-use-cases.interface.js'; -@Injectable() +@Injectable({ scope: Scope.REQUEST }) export class GetUserCompanyFullInfoUseCase extends AbstractUseCase implements IGetUserFullCompanyInfo diff --git a/yarn.lock b/yarn.lock index 0394c9187..7c46cda37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2524,6 +2524,17 @@ __metadata: languageName: node linkType: hard +"@nestjs/throttler@npm:^6.4.0": + version: 6.4.0 + resolution: "@nestjs/throttler@npm:6.4.0" + peerDependencies: + "@nestjs/common": ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + "@nestjs/core": ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + reflect-metadata: ^0.1.13 || ^0.2.0 + checksum: 056d7d2874ab02303af02239661257a291edef9b57e75e99686b48eae376d88d513211f21d68655e4d673838c7c46d85a5fb1575ce7945a5c08a7d05a1ce46fe + languageName: node + linkType: hard + "@nestjs/typeorm@npm:^11.0.0": version: 11.0.0 resolution: "@nestjs/typeorm@npm:11.0.0" @@ -5631,6 +5642,7 @@ __metadata: "@nestjs/schematics": 11.0.7 "@nestjs/swagger": ^11.2.0 "@nestjs/testing": ^11.1.6 + "@nestjs/throttler": ^6.4.0 "@nestjs/typeorm": ^11.0.0 "@nestjsx/crud": 4.6.2 "@rocketadmin/shared-code": "workspace:*"