Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement API Rate Limiting with ThrottlerModule #1329

Open
wants to merge 16 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions .gitignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this file. Why are you removing anything from this place

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i cant removed the .gitignore file that is what is preventing the node modules and others file from being pushed. the previous conflicts that i resolved was that from the package-lock.json then i had to pull the changes made from the dev branch to my branch and push it back to resolved the changes and i remove the package-lock.json in the .gitignore file that is preventing it from being push..

Copy link
Author

@NnatuanyaFrankOguguo NnatuanyaFrankOguguo Mar 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have added what i remove from the .gitignore file back.

Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ fabric.properties
# DB
data/db/

docker-compose.yml

# User-specific files
*.suo
*.user
Expand Down Expand Up @@ -104,7 +104,7 @@ BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
package-lock.json

project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
Expand Down Expand Up @@ -406,21 +406,30 @@ dist/
*.prod


# database files
data/db

#





# User specific ignores
todo.txt
/.vscode/
.vscode/

# Docker compose
docker-compose.yml

data/
data/
docker-compose.yml
package-lock.json

.dev.env


package-lock.json

docker-compose.yml
data/
.dev.env
Expand All @@ -430,13 +439,14 @@ compose/compose.yaml

data/
docker-compose.yml
package-lock.json/
package-lock.json


.dev.env


package-lock.json
docker-compose.yml


data/
.dev.env


40 changes: 33 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,48 @@

## Overview

## Rate Limiting

This project uses `@nestjs/throttler` for rate limiting to prevent abuse.

### Configuration

- **Global Rate Limit**: 10 requests per minute.
- **Custom Limits**: Some routes may have different limits.

### Error Handling

If a user exceeds the allowed request rate, they receive:

```json
{
"statusCode": 429,
"message": "Too Many Requests"
}

<img align="right" width="300" src="./screenshot/Screenshot1.png" alt="successful register user request api endpoint" />

<img align="right" width="300" src="./screenshot/ScreenshotE2.png" alt="throttle limiting " />


[Description]

## Folder Structure

```

|--- src
| |--- database
| |--- modules
| |--- shared
| |--- app.module.ts
| |--- main.ts
| |--- database
| |--- modules
| |--- shared
| |--- app.module.ts
| |--- main.ts
|--- .env.local
|--- .gitignore
|--- package.json
|--- tsconfig.json
```

````

## Dependencies (Dev)

Expand Down Expand Up @@ -63,7 +89,7 @@ Open a terminal and run the following git command:

```bash
git clone "url you just copied"
```
````

where "url you just copied" (without the quotation marks) is the url to this repository (your fork of this project). See the previous steps to obtain the url.

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@nestjs/schedule": "^5.0.1",
"@nestjs/serve-static": "^5.0.3",
"@nestjs/swagger": "^11.0.5",
"@nestjs/throttler": "^6.4.0",
"@nestjs/typeorm": "^11.0.0",
"@types/nodemailer": "^6.4.17",
"@types/speakeasy": "^2.0.10",
Expand Down
Binary file added screenshot/Screenshot1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshot/ScreenshotE2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';
import { MailerModule } from '@nestjs-modules/mailer';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { BullModule } from '@nestjs/bull';
Expand Down Expand Up @@ -71,8 +73,21 @@ import s3Config from '@config/s3.config';
provide: 'APP_GUARD',
useClass: AuthGuard,
},
{
provide: APP_GUARD,
useClass: ThrottlerGuard, // Apply rate limiting globally
},
],
imports: [
// Add ThrottlerModule here
ThrottlerModule.forRoot({
throttlers: [
{
ttl: 60000, //Time window in seconds (1 minute)
limit: 50, // Maximum number of requests within ttl
},
],
}),
ConfigModule.forRoot({
/*
* By default, the package looks for a env file in the root directory of the application.
Expand Down
5 changes: 5 additions & 0 deletions src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import {
ApiTags,
ApiUnauthorizedResponse,
} from '@nestjs/swagger';

import * as SYS_MSG from '@shared/constants/SystemMessages';
import { Body, Controller, HttpCode, Post, Req, Request, Patch } from '@nestjs/common';

import { CreateUserDTO } from './dto/create-user.dto';
import { skipAuth } from '@shared/helpers/skipAuth';
import AuthenticationService from './auth.service';
Expand All @@ -32,6 +34,8 @@ import { GenericAuthResponseDto } from './dto/generic-reponse.dto';
import { UpdatePasswordDto } from './dto/updatePasswordDto';
import { LoginErrorResponseDto } from './dto/login-error-dto';
import { UpdateUserPasswordResponseDTO } from './dto/update-user-password.dto';
import { Throttle } from '@nestjs/throttler';

import { CustomHttpException } from '@shared/helpers/custom-http-filter';

@ApiTags('Authentication')
Expand All @@ -43,6 +47,7 @@ export default class RegistrationController {
@ApiOperation({ summary: 'User Registration' })
@ApiResponse({ status: 201, description: 'Register a new user', type: SuccessCreateUserResponse })
@ApiResponse({ status: 400, description: 'User already exists', type: ErrorCreateUserResponse })
@Throttle({ default: { limit: 25, ttl: 30000 } }) // 25 requests per 30 seconds
@Post('register')
@HttpCode(201)
public async register(@Body() body: CreateUserDTO): Promise<any> {
Expand Down
5 changes: 5 additions & 0 deletions src/modules/auth/tests/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ import { OrganisationsService } from '@modules/organisations/organisations.servi
import { User } from '@modules/user/entities/user.entity';
import { Profile } from '@modules/profile/entities/profile.entity';
import { LoginDto } from '../dto/login.dto';
import { INestApplication } from '@nestjs/common';
import { AppModule } from '../../../app.module';
import * as request from 'supertest';

import UserResponseDTO from '@modules/user/dto/user-response.dto';
import { Otp } from '@modules/otp/entities/otp.entity';
import { Verify2FADto } from '../dto/verify-2fa.dto';
import { DataSource, EntityManager } from 'typeorm';

jest.mock('speakeasy');

describe('AuthenticationService', () => {
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"moduleResolution": "node",
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
Expand Down