Boilerplate code is computer language text that you can reuse with little or no alteration in several different contexts.
This is a boilerplate using NestJS, a popular NodeJS framework, reducing time to write CRUD method by generating code.
$ git clone https://github.com/duyanhitbe/nestjs-boilerplate.git
$ cd nestjs-boilerplate
Now, you can choose kind of ORM you want to use.
- Typeorm for SQL (typeorm)
- Mongoose for MongoDB (mongoose)
And, you can choose what type of API (Application Programming Interface):
- RESTful (rest)
- GraphQL (graphql)
Example:
$ git checkout rest/typeorm
$ pnpm install
# Install pnpm if you haven't
$ npm i -g pnpm
$ cp .env.example .env
Then replace your owner config
# dev mode
$ pnpm start:dev
# prod mode
$ pnpm build
$ pnpm start:prod
# pm2
$ pm2 start ecosystem.config.js
# Unit test
$ pnpm test
# End-to-End test
$ pnpm test:e2e
Using joi package to validate env variable. Example:
src/modules/config/config.module.ts
@Module({
imports: [
NestConfigModule.forRoot({
isGlobal: true,
envFilePath: '.env',
validationSchema: Joi.object({
NODE_ENV: Joi.string().required(), //<- Must be a string and required
PORT: Joi.number().required() //<- Must be a number and required
})
})
]
})
export class ConfigModule {}
Strong of this boilerplate code that you can generate CRUD module quickly with one command.
$ make crud name=example
? What transport layer do you use? (Use arrow keys)
❯ REST API #<-- Choose this one if you are using Restful
GraphQL (code first) #<-- Choose this one if you are using GraphQL
GraphQL (schema first)
Microservice (non-HTTP)
WebSockets
? Would you like to generate CRUD entry points? (Y/n) #<--Yes or Y
If you're using TypeORM for your project, you can create or generate migration to run for SQL.
$ make generate-migration name=migration_name
$ make create-migration name=migration_name
When you run one of two command abort, it will generate a new file in src/modules/database/migrations. Let's import it to DatabaseModule
@Module({
imports: [
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
migrations: [
User1706412751363,
Book1707635652785
NewMigrationFile //<-- Right here
]
})
})
]
})
export class DatabaseModule {}
Now, you can run that migration by
$ make run-migration
Or just start the server, migration will run automatically
$ make revert-migration
Base on package @nestjs/jwt, you can inject JwtService through its interface. Example:
import { Injectable } from '@nestjs/common';
import { IJwtService } from '@modules/jwt';
@Injectable()
export class FooService {
constructor(private readonly jwtService: IJwtService) {}
async login(username: string, password: string) {
//Validate user
...
return this.jwtService.sign({ username });
}
}
Using redis from ioredis package to improve your application
import { Injectable } from '@nestjs/common';
import { IRedisService } from '@modules/redis';
@Injectable()
export class BarService {
constructor(private readonly redisService: IRedisService) {}
async getHugeData() {
//Get data from cache
const key = 'HUGE_DATA';
const cachedData = await this.redisService.get(key);
if (cachedData) {
return cachedData;
}
const expireTime = 60 * 60; //1m
const veryHugeData = getData();
//Cache data
this.redisService.set(key, veryHugeData, expireTime);
return veryHugeData;
}
}
Using @nestjs/schedule you can run multiple cron-job in CronService
import { Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
@Injectable()
export class CronService {
private readonly logger = new Logger(CronService.name);
/* When the number of second is 45, this method will be executed */
@Cron('45 * * * * *')
helloWorld() {
console.log('Hello World');
}
}
Check this link to more information
Base on package nestjs-i18n
to translate multiple language. Let's check it out
src/modules/i18n/i18n.module.ts
@Module({
imports: [
I18nModule.forRoot({
// Default language
fallbackLanguage: Language.VI,
// Load JSON file
loaderOptions: {
path: join(__dirname, '/translations/'),
watch: true
},
// Resolver that I18nModule can verify which language do you want to use?
// In this case, that is pass "x-lang" into header
resolvers: [new HeaderResolver(['x-lang'])],
// Path to generate ts file
typesOutputPath: join(
__dirname,
'/../../../../src/modules/i18n/generated/i18n.generated.ts'
)
})
]
})
export class I18NModule {}
src/modules/i18n/translations/vi/errors.json
{
"WRONG_PASSWORD": "Wrong password"
}
Example:
import { Injectable } from '@nestjs/common';
import { I18nService } from 'nestjs-i18n';
@Injectable()
export class CronService {
constructor(private readonly i18n: I18nService) {}
getHello(): string {
return this.i18n.t('test.WRONG_PASSWORD');
}
}
Validation with class-validator
import { IsString, IsNotEmpty } from '@common'; // Import from '@common' not 'class-validator'
class CreateUserDto {
@IsString()
@IsNotEmpty()
name!: string;
}
src/common/decorators/validation.decorator.ts
import { translate } from '@app/modules/i18n/i18n.helper';
import { applyDecorators } from '@nestjs/common';
import * as validator from 'class-validator';
export const IsString = (validationOptions?: validator.ValidationOptions) =>
applyDecorators(
validator.IsString({ ...validationOptions, message: translate('validation.IS_STRING') }) //Automatically translate in validation.json
);
export const IsNotEmpty = (validationOptions?: validator.ValidationOptions) =>
applyDecorators(
validator.IsNotEmpty({
...validationOptions,
message: translate('validation.IS_NOT_EMPTY')
})
);
- Author - Duy Anh (doba)
- Email - [email protected]
- Linked - profile