Skip to content

Commit

Permalink
refactor: refactor code to make code cleaner (#7)
Browse files Browse the repository at this point in the history
* refactor: move app port into env

* refactor: refactor code to make code cleaner
  • Loading branch information
domingo1021 authored Jun 30, 2024
1 parent f62fc27 commit 71ea6fc
Show file tree
Hide file tree
Showing 25 changed files with 86 additions and 134 deletions.
1 change: 1 addition & 0 deletions config/default.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"APP_PORT": 3000,
"REDIS_HOST": "localhost",
"REDIS_PORT": 6379
}
1 change: 1 addition & 0 deletions config/docker.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"APP_PORT": 3000,
"REDIS_HOST": "hero-cache",
"REDIS_PORT": 6379
}
1 change: 1 addition & 0 deletions config/test.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"APP_PORT": 3000,
"REDIS_HOST": "localhost",
"REDIS_PORT": 6379
}
10 changes: 4 additions & 6 deletions src/cores/decorators/local.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import { InternalRequest } from '#cores/types';
*
* Reference: https://github.com/nestjs/nest/issues/913#issuecomment-408822021
*/
export const Local = createParamDecorator(
(key: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest<InternalRequest>();
return key ? request.locals[key] : request.locals;
},
);
export const Local = createParamDecorator((key: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest<InternalRequest>();
return key ? request.locals[key] : request.locals;
});
10 changes: 4 additions & 6 deletions src/cores/decorators/user.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import { InternalRequest } from '#cores/types';
* @description Get the user authentication related object from the request object.
* @param key - The key of the user object to retrieve.
*/
export const User = createParamDecorator(
(key: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest<InternalRequest>();
return key ? request.user[key] : request.user;
},
);
export const User = createParamDecorator((key: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest<InternalRequest>();
return key ? request.user[key] : request.user;
});
15 changes: 5 additions & 10 deletions src/cores/exceptions/general.exception.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpStatus,
HttpException,
} from '@nestjs/common';
import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus, HttpException } from '@nestjs/common';

import { Response } from 'express';

import { InternalRequest, CustomErrorCodes, ErrorResponse } from '#cores/types';
Expand Down Expand Up @@ -42,9 +37,9 @@ export class AllExceptionsFilter implements ExceptionFilter {

this.logger.info(
requestId,
`Response, ${statusCode}, ${request.url}, ${JSON.stringify(
response.getHeaders(),
)}, Error: ${JSON.stringify(errorResponse)}`,
`Response, ${statusCode}, ${request.url}, ${JSON.stringify(response.getHeaders())}, Error: ${JSON.stringify(
errorResponse,
)}`,
);

response.status(statusCode).json(errorResponse);
Expand Down
1 change: 1 addition & 0 deletions src/cores/guards/local.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { InternalRequest } from '#cores/types';
export class LocalAuthGuard implements CanActivate {
private readonly USERNAME_KEY = 'Username';
private readonly PASSWORD_KEY = 'Password';

constructor(private readonly authService: AuthService) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
Expand Down
11 changes: 4 additions & 7 deletions src/cores/interceptors/requestId.interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import {
ExecutionContext,
CallHandler,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { ExecutionContext, CallHandler, Injectable, NestInterceptor } from '@nestjs/common';

import { Observable } from 'rxjs';
import { v4 as uuidv4 } from 'uuid';
import { InternalRequest } from '../types';

import { InternalRequest } from '#cores/types';

/**
* @description RequestIdInterceptor is a interceptor to help generate request id.
Expand Down
12 changes: 3 additions & 9 deletions src/cores/interceptors/responseLog.interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';

import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Response } from 'express';
Expand Down Expand Up @@ -38,9 +34,7 @@ export class ResponseLoggerInterceptor implements NestInterceptor {
const { statusCode } = response;
this.logger.log(
requestId,
`Response, ${statusCode}, ${url}, ${JSON.stringify(
response.getHeaders(),
)}, ${JSON.stringify(data)}`,
`Response, ${statusCode}, ${url}, ${JSON.stringify(response.getHeaders())}, ${JSON.stringify(data)}`,
);
}),
);
Expand Down
1 change: 1 addition & 0 deletions src/cores/middlewares/requestId.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Injectable, NestMiddleware } from '@nestjs/common';

import { v4 as uuidv4 } from 'uuid';
import { Request, Response, NextFunction } from 'express';

Expand Down
6 changes: 2 additions & 4 deletions src/cores/middlewares/requestLog.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Injectable, NestMiddleware } from '@nestjs/common';

import { Response, NextFunction } from 'express';

import { InternalRequest } from '#cores/types';
Expand All @@ -12,10 +13,7 @@ export class RequestLoggerMiddleware implements NestMiddleware {
const requestId = req.locals.requestId;
const { method, url, headers, body } = req;

this.logger.log(
requestId,
`Request, ${method}, ${url}, ${JSON.stringify(headers)}, ${JSON.stringify(body)}`,
);
this.logger.log(requestId, `Request, ${method}, ${url}, ${JSON.stringify(headers)}, ${JSON.stringify(body)}`);

next();
}
Expand Down
6 changes: 5 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { NestFactory } from '@nestjs/core';

import * as config from 'config';

import { AppModule } from '#app/app.module';
import { SystemLog } from '#logger/systemLog.service';

async function bootstrap() {
const APP_PORT = +config.get<string>('APP_PORT') || 3000;

const app = await NestFactory.create(AppModule, {
logger: new SystemLog(),
});
await app.listen(3000);
await app.listen(APP_PORT);
}
bootstrap();
7 changes: 1 addition & 6 deletions src/modules/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
MiddlewareConsumer,
Module,
NestModule,
RequestMethod,
} from '@nestjs/common';
import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common';
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
import { ConfigModule } from '@nestjs/config';

Expand Down
2 changes: 1 addition & 1 deletion src/modules/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Global, Module } from '@nestjs/common';

import { ExternalHttpModule } from '#http/http.module';
import { CacheConfigModule } from '#cache/cache.module';
import { AuthService } from './auth.service';
import { AuthService } from '#auth/auth.service';

@Global()
@Module({
Expand Down
9 changes: 3 additions & 6 deletions src/modules/cache/cache.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CacheModule, CacheModuleAsyncOptions } from '@nestjs/cache-manager';

import { redisStore } from 'cache-manager-redis-store';

import { CacheService } from './cache.service';
import { CacheService } from '#cache/cache.service';

// Referece: https://github.com/dabroek/node-cache-manager-redis-store/issues/53
@Module({
Expand All @@ -20,11 +20,8 @@ import { CacheService } from './cache.service';
return {
store: await redisStore({
socket: {
host:
configService.get<string>('REDIS_HOST') || DEFAULT_REDIS_HOST,
port: +(
configService.get<number>('REDIS_PORT') || DEFAULT_REDIS_PORT
),
host: configService.get<string>('REDIS_HOST') || DEFAULT_REDIS_HOST,
port: +(configService.get<number>('REDIS_PORT') || DEFAULT_REDIS_PORT),
},
}),
ttl: 5000,
Expand Down
5 changes: 1 addition & 4 deletions src/modules/hero/hero.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ export class HeroController {

@UseGuards(LocalAuthGuard)
@Get('heroes/:id')
async getHeroById(
@Param() params: GetSingleHeroReqParams,
@User() user: LocalUser,
): Promise<Hero> {
async getHeroById(@Param() params: GetSingleHeroReqParams, @User() user: LocalUser): Promise<Hero> {
const { id } = params;
const { isAuthenticated } = user;
return this.heroService.findById(id, isAuthenticated);
Expand Down
19 changes: 5 additions & 14 deletions src/modules/hero/hero.repository.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export class HeroRepositoryImpl implements HeroRepository {
private readonly GET_ALL_HEROES_WITH_PROFILE_KEY = 'heroes_with_profile';
private readonly GET_HERO_BY_ID_KEY = 'hero_'; // hero_{id}
private readonly GET_HERO_WITH_PROFILE_BY_ID_KEY = 'hero_with_profile_'; // hero_with_profile_{id}

constructor(
private readonly cacheService: CacheService,
private readonly httpApi: ExternalHttpService,
Expand All @@ -21,18 +22,13 @@ export class HeroRepositoryImpl implements HeroRepository {
* @throws {InternalServerErrorException}
*/
async getAllHeroes(): Promise<Array<Hero>> {
const cachedHeroes = await this.cacheService.get<string>(
this.GET_ALL_HEROES_KEY,
);
const cachedHeroes = await this.cacheService.get<string>(this.GET_ALL_HEROES_KEY);
if (cachedHeroes) {
return JSON.parse(cachedHeroes);
}

const heroes: Array<Hero> = await this.httpApi.getHeroes();
await this.cacheService.set(
this.GET_ALL_HEROES_KEY,
JSON.stringify(heroes),
);
await this.cacheService.set(this.GET_ALL_HEROES_KEY, JSON.stringify(heroes));

return heroes;
}
Expand All @@ -44,9 +40,7 @@ export class HeroRepositoryImpl implements HeroRepository {
* @throws {InternalServerErrorException}
*/
async getAllHeroesWithProfile(): Promise<Array<Hero>> {
const cachedHeroes = await this.cacheService.get<string>(
this.GET_ALL_HEROES_WITH_PROFILE_KEY,
);
const cachedHeroes = await this.cacheService.get<string>(this.GET_ALL_HEROES_WITH_PROFILE_KEY);
if (cachedHeroes) {
return JSON.parse(cachedHeroes);
}
Expand All @@ -58,10 +52,7 @@ export class HeroRepositoryImpl implements HeroRepository {
hero.profile = profile;
}),
);
await this.cacheService.set(
this.GET_ALL_HEROES_WITH_PROFILE_KEY,
JSON.stringify(heroes),
);
await this.cacheService.set(this.GET_ALL_HEROES_WITH_PROFILE_KEY, JSON.stringify(heroes));

return heroes;
}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/hero/hero.validator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Hero, HeroProfile } from './dto';
import { Hero, HeroProfile } from '#hero/dto';

/**
* A class that contains static utils methods to validate hero data.
Expand Down
50 changes: 22 additions & 28 deletions src/modules/http/http.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';

import { AxiosError } from 'axios';
import { firstValueFrom, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
Expand Down Expand Up @@ -32,10 +33,7 @@ export class ExternalHttpService {
}),
map((response) => response.data),
map((heroes) => {
if (
!Array.isArray(heroes) ||
!heroes.every(HeroDataValidator.isHero)
) {
if (!Array.isArray(heroes) || !heroes.every(HeroDataValidator.isHero)) {
throw new InternalServerErrorException({
code: CustomErrorCodes.THIRDPARTY_API_RESPONSE_MISMATCH,
message: `Invalid response format from upstream ${this.EXTERNAL_ENDPOINT.GET_HEROES}`,
Expand Down Expand Up @@ -84,27 +82,25 @@ export class ExternalHttpService {
*/
async getHeroProfileById(id: string): Promise<HeroProfile> {
return firstValueFrom(
this.httpService
.get(`${this.EXTERNAL_ENDPOINT.GET_HEROES}/${id}/profile`)
.pipe(
catchError((error: AxiosError) => {
this.httpService.get(`${this.EXTERNAL_ENDPOINT.GET_HEROES}/${id}/profile`).pipe(
catchError((error: AxiosError) => {
throw new InternalServerErrorException({
code: CustomErrorCodes.THIRDPARTY_SERVER_ERROR,
message: error.message,
});
}),
map((response) => response.data),
map((heroProfile) => {
if (!HeroDataValidator.isHeroProfile(heroProfile)) {
throw new InternalServerErrorException({
code: CustomErrorCodes.THIRDPARTY_SERVER_ERROR,
message: error.message,
code: CustomErrorCodes.THIRDPARTY_API_RESPONSE_MISMATCH,
message: `Invalid response format from upstream ${this.EXTERNAL_ENDPOINT.GET_HEROES}/${id}/profile`,
});
}),
map((response) => response.data),
map((heroProfile) => {
if (!HeroDataValidator.isHeroProfile(heroProfile)) {
throw new InternalServerErrorException({
code: CustomErrorCodes.THIRDPARTY_API_RESPONSE_MISMATCH,
message: `Invalid response format from upstream ${this.EXTERNAL_ENDPOINT.GET_HEROES}/${id}/profile`,
});
}
}

return heroProfile;
}),
),
return heroProfile;
}),
),
);
}

Expand All @@ -114,12 +110,10 @@ export class ExternalHttpService {
*/
async authenticate(username: string, password: string): Promise<boolean> {
return firstValueFrom(
this.httpService
.post(this.EXTERNAL_ENDPOINT.AUTHENTICATE, { name: username, password })
.pipe(
map(() => true),
catchError(() => of(false)),
),
this.httpService.post(this.EXTERNAL_ENDPOINT.AUTHENTICATE, { name: username, password }).pipe(
map(() => true),
catchError(() => of(false)),
),
);
}
}
3 changes: 2 additions & 1 deletion src/modules/logger/appLog.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Global, Injectable } from '@nestjs/common';
import { BaseLog } from './type/baseLog';

import { BaseLog } from '#logger/type/baseLog';

/**
* @description Custom log implement Nest LoggerService, it will log all endpoint related logs.
Expand Down
5 changes: 3 additions & 2 deletions src/modules/logger/logger.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Module, Provider } from '@nestjs/common';

import * as winston from 'winston';

import { CustomLog } from './appLog.service';
import { WinstonConfig } from './logger.config';
import { CustomLog } from '#logger/appLog.service';
import { WinstonConfig } from '#logger/logger.config';

const WinstonProvider: Provider = {
provide: 'Winston',
Expand Down
6 changes: 4 additions & 2 deletions src/modules/logger/systemLog.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as winston from 'winston';
import { LoggerService } from '@nestjs/common';
import { BaseLog } from './type/baseLog';

import * as winston from 'winston';

import { BaseLog } from '#logger/type/baseLog';

/**
* @description System log implement Nest LoggerService, it will log all server related system logs.
Expand Down
1 change: 1 addition & 0 deletions test/modules/app/app.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';

import * as request from 'supertest';

import { AppModule } from '#app/app.module';
Expand Down
Loading

0 comments on commit 71ea6fc

Please sign in to comment.