Skip to content

Commit decdaaf

Browse files
committed
add dtos and validation pipes
1 parent 0db5938 commit decdaaf

File tree

17 files changed

+282
-6
lines changed

17 files changed

+282
-6
lines changed

project/apps/api-gateway/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
"test:e2e": "jest --config ./test/jest-e2e.json"
2121
},
2222
"dependencies": {
23+
"@repo/pipes": "workspace:*",
24+
"@repo/dtos": "workspace:*",
2325
"@nestjs/common": "^10.0.0",
2426
"@nestjs/core": "^10.0.0",
2527
"@nestjs/microservices": "^10.4.3",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
2+
import { RpcException } from '@nestjs/microservices';
3+
import { Response } from 'express';
4+
5+
@Catch(RpcException)
6+
export class RpcExceptionFilter implements ExceptionFilter {
7+
catch(exception: RpcException, host: ArgumentsHost) {
8+
const error: any = exception.getError();
9+
const ctx = host.switchToHttp();
10+
const response = ctx.getResponse<Response>();
11+
12+
response.status(error.statusCode).json(error);
13+
}
14+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {
2+
Injectable,
3+
NestInterceptor,
4+
ExecutionContext,
5+
CallHandler,
6+
} from '@nestjs/common';
7+
import { Observable, throwError } from 'rxjs';
8+
import { catchError } from 'rxjs/operators';
9+
import { RpcException } from '@nestjs/microservices';
10+
11+
@Injectable()
12+
export class RpcExceptionInterceptor implements NestInterceptor {
13+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
14+
return next.handle().pipe(
15+
catchError((error) => {
16+
if (error.response) {
17+
// This is likely an RPC error
18+
return throwError(() => new RpcException(error.response));
19+
}
20+
return throwError(() => error);
21+
}),
22+
);
23+
}
24+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { NestFactory } from '@nestjs/core';
22
import { ApiGatewayModule } from './api-gateway.module';
3+
import { RpcExceptionFilter } from './filters/rpc-exception.filter';
4+
import { RpcExceptionInterceptor } from './interceptors/rpc-exception.interceptor';
35

46
async function bootstrap() {
57
const app = await NestFactory.create(ApiGatewayModule);
8+
app.useGlobalFilters(new RpcExceptionFilter());
9+
app.useGlobalInterceptors(new RpcExceptionInterceptor());
610
await app.listen(4000);
711
}
812
bootstrap();

project/apps/api-gateway/src/questions/questions.controller.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// apps/backend/api-gateway/src/questions/questions.controller.ts
22

3-
import { Controller, Get, Param, Inject } from '@nestjs/common';
3+
import { Controller, Get, Param, Inject, Body, Post } from '@nestjs/common';
44
import { ClientProxy } from '@nestjs/microservices';
5-
65
@Controller('questions')
76
export class QuestionsController {
87
constructor(
@@ -14,4 +13,12 @@ export class QuestionsController {
1413
async getQuestionById(@Param('id') id: string) {
1514
return this.questionsServiceClient.send({ cmd: 'get_question' }, id);
1615
}
16+
17+
@Post()
18+
async createQuestion(@Body() createQuestionDto: any) {
19+
return this.questionsServiceClient.send(
20+
{ cmd: 'create_question' },
21+
createQuestionDto,
22+
);
23+
}
1724
}

project/apps/questions-service/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
"test:e2e": "jest --config ./test/jest-e2e.json"
2121
},
2222
"dependencies": {
23+
"@repo/pipes": "workspace:*",
24+
"@repo/dtos": "workspace:*",
2325
"@nestjs/common": "^10.0.0",
2426
"@nestjs/core": "^10.0.0",
2527
"@nestjs/microservices": "^10.4.3",
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { Controller } from '@nestjs/common';
2-
import { MessagePattern } from '@nestjs/microservices';
1+
import { Controller, UsePipes } from '@nestjs/common';
2+
import { MessagePattern, Payload } from '@nestjs/microservices';
33
import { QuestionsService } from './questions.service';
4-
4+
import { ZodValidationPipe } from '@repo/pipes/zod-validation-pipe.pipe';
5+
import { CreateQuestionDto, createQuestionSchema } from '@repo/dtos/questions';
56
@Controller()
67
export class QuestionsController {
78
constructor(private readonly questionsService: QuestionsService) {}
@@ -10,4 +11,10 @@ export class QuestionsController {
1011
getQuestion(id: string) {
1112
return this.questionsService.findById(id);
1213
}
14+
15+
@MessagePattern({ cmd: 'create_question' })
16+
@UsePipes(new ZodValidationPipe(createQuestionSchema))
17+
async create(@Payload() createQuestionDto: CreateQuestionDto) {
18+
return createQuestionDto;
19+
}
1320
}

project/packages/dtos/package.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"name": "@repo/dtos",
3+
"version": "0.0.0",
4+
"private": true,
5+
"license": "MIT",
6+
"scripts": {
7+
"dev": "pnpm build --watch",
8+
"build": "tsc -b -v",
9+
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\""
10+
},
11+
"main": "./dist/index.js",
12+
"types": "./dist/index.d.ts",
13+
"files": [
14+
"./dist/**"
15+
],
16+
"publishConfig": {
17+
"access": "public"
18+
},
19+
"typesVersions": {
20+
"*": {
21+
"*": [
22+
"src/*"
23+
]
24+
}
25+
},
26+
"exports": {
27+
".": {
28+
"import": "./dist/index.js",
29+
"require": "./dist/index.js"
30+
},
31+
"./*": {
32+
"import": "./dist/*.js",
33+
"require": "./dist/*.js"
34+
}
35+
},
36+
"dependencies": {
37+
"zod": "^3.23.8"
38+
},
39+
"devDependencies": {
40+
"@repo/eslint-config": "workspace:*",
41+
"@repo/typescript-config": "workspace:*",
42+
"@types/node": "^20.3.1",
43+
"ts-loader": "^9.4.3",
44+
"ts-node": "^10.9.1",
45+
"typescript": "^5.1.3"
46+
}
47+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { z } from "zod";
2+
3+
export const createQuestionSchema = z
4+
.object({
5+
title: z.string().min(1),
6+
description: z.string().min(1),
7+
category: z.string().min(1),
8+
complexity: z.number().min(1).max(5),
9+
})
10+
.required();
11+
12+
export type CreateQuestionDto = z.infer<typeof createQuestionSchema>;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "@repo/typescript-config/base.json",
3+
"compilerOptions": {
4+
"outDir": "dist",
5+
"rootDir": "src"
6+
},
7+
"include": ["src"],
8+
"exclude": ["node_modules", "dist"]
9+
}

0 commit comments

Comments
 (0)