This refactor modernizes the API response handling, error management, security, and observability features of the box-mgnt-api application.
Location: libs/common/src/interfaces/api-response.interface.ts
New Response Format:
interface ApiResponse<T> {
success: boolean;
code: string;
message: string;
data: T;
timestamp: string;
}
Before:
{
"error": "",
"status": 1,
"data": { ... }
}
After:
{
"success": true,
"code": "OK",
"message": "success",
"data": { ... },
"timestamp": "2025-11-20T10:30:00.000Z"
}
Benefits:
ApiResponse<T>Location: libs/common/src/filters/http-exception.filter.ts
Key Changes:
AllExceptionsFilter (always returned HTTP 200)HttpExceptionFilter preserves actual HTTP status codesStatus Code Mapping:
400 → BAD_REQUEST401 → UNAUTHORIZED403 → FORBIDDEN404 → NOT_FOUND429 → RATE_LIMITED500 → INTERNAL_ERRORExample Error Response:
HTTP/1.1 401 Unauthorized
{
"success": false,
"code": "UNAUTHORIZED",
"message": "User not authenticated",
"data": null,
"timestamp": "2025-11-20T10:30:00.000Z"
}
Location: apps/box-mgnt-api/src/config/env.validation.ts
Validated Variables:
MYSQL_URL - MySQL connection string (required, URL format)MONGO_URL - MongoDB connection string (required, URL format)JWT_SECRET - Secret for signing JWTs (required)JWT_EXPIRES_IN_SECONDS - Token TTL (optional, 60-86400, default: 43200)NODE_ENV - Environment (optional, enum: development/production/test)PORT - Server port (optional, 1024-65535, default: 3000)ENCRYPTION_KEY - For 2FA encryption (optional)Benefits:
Usage in app.module.ts:
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env.mgnt.dev', '.env'],
validate: validateEnvironment, // ← Added
});
Location: libs/common/src/interceptors/correlation.interceptor.ts
How it Works:
x-request-id or x-correlation-id from incoming headersreq.correlationIdResponse Headers:
x-request-id: 550e8400-e29b-41d4-a716-446655440000
x-correlation-id: 550e8400-e29b-41d4-a716-446655440000
Benefits:
Location: libs/common/src/guards/rate-limit.guard.ts
Configuration:
Applied to Endpoints:
POST /auth/loginPOST /auth/login2faPOST /auth/oauth-loginPOST /auth/oauth-login-2faPOST /auth/2fa/setupPOST /auth/2fa/enableRate Limit Response:
HTTP/1.1 429 Too Many Requests
{
"success": false,
"code": "RATE_LIMITED",
"message": "Too many requests. Please try again in 45 seconds.",
"data": null,
"timestamp": "2025-11-20T10:30:00.000Z"
}
Features:
x-forwarded-for, x-real-ip)Location: libs/common/src/guards/mfa.guard.ts
Purpose: Enforce MFA verification for protected endpoints
Logic:
user.twoFA field)req.mfaVerified === trueUNAUTHORIZED with code MFA_REQUIRED if not verifiedUsage Example:
@UseGuards(JwtAuthGuard, MfaGuard)
@Get('sensitive-data')
async getSensitiveData(@AuthUser() user: User) {
// Only accessible after MFA verification
}
libs/common/src/common.module.ts)New Interceptors Order:
CorrelationInterceptor (first - adds request ID)LoggingInterceptor (logs with correlation ID)OperationLogInterceptor (business operation logging)ResponseInterceptor (wraps response in ApiResponse)New Filter:
HttpExceptionFilter (replaces AllExceptionsFilter)Before:
interface HttpResponse {
error: string;
status: 1 | 0;
data: unknown | null;
}
After:
interface ApiResponse<T> {
success: boolean;
code: string;
message: string;
data: T;
timestamp: string;
}
Frontend Update Required:
// Before
if (response.status === 1) {
/* success */
}
// After
if (response.success) {
/* success */
}
response.status (HTTP) not response.body.statusMYSQL_URL, MONGO_URL, JWT_SECRETJWT_EXPIRES_IN_SECONDS, ENCRYPTION_KEY# Should succeed first 10 times, then fail with 429
for i in {1..15}; do
curl -X POST http://localhost:3000/mgnt/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"test"}'
echo "\nRequest $i"
sleep 1
done
# Send request with custom correlation ID
curl -H "x-request-id: my-custom-id-123" \
http://localhost:3000/mgnt/auth/permission
# Check response headers contain same ID
# Should return 401 with proper format
curl -X GET http://localhost:3000/mgnt/users/me
# Response:
# HTTP/1.1 401 Unauthorized
# { "success": false, "code": "UNAUTHORIZED", ... }
# Remove JWT_SECRET from .env and restart
# Should fail with clear error message
pnpm start:mgnt
# Error: Environment validation failed:
# JWT_SECRET should not be empty
Already optimized in login() method:
const [roleIds, userMenus] = await Promise.all([
this.userService.getUserRoleIds(user.id, true),
this.userService.getUserMenus(user.id),
]);
Add database indexes:
CREATE INDEX idx_user_role_user_id ON sys_user_role(user_id);
CREATE INDEX idx_user_role_role_id ON sys_user_role(role_id);
CREATE INDEX idx_role_menu_role_id ON sys_role_menu(role_id);
CREATE INDEX idx_menu_parent_id ON sys_menu(parent_id);
Add caching layer for frequently accessed data:
Implement Redis for rate limiting (production):
// Replace in-memory Map with Redis
await redis.incr(`ratelimit:${key}`);
await redis.expire(`ratelimit:${key}`, windowSeconds);
Add Prometheus metrics for:
libs/common/src/interfaces/api-response.interface.tslibs/common/src/filters/http-exception.filter.tslibs/common/src/interceptors/correlation.interceptor.tslibs/common/src/guards/rate-limit.guard.tslibs/common/src/guards/mfa.guard.tsapps/box-mgnt-api/src/config/env.validation.tslibs/common/src/common.module.ts (wiring)libs/common/src/interceptors/response.interceptor.ts (new format)libs/common/src/interceptors/operation-log.interceptor.ts (type update)libs/common/src/services/exception.service.ts (new format)apps/box-mgnt-api/src/app.module.ts (config validation)apps/box-mgnt-api/src/mgnt-backend/core/auth/auth.controller.ts (rate limiting)If issues arise, revert these commits in order:
AllExceptionsFilter in CommonModuleCheck the following if something doesn't work:
pnpm install and pnpm prisma:generatetsconfig.base.json includes new paths.env file has all required variablesRateLimitGuard is before LocalAuthGuardCorrelationInterceptor is first in provider list