Skip to content

Commit

Permalink
feat(js): create nestjs-specific files
Browse files Browse the repository at this point in the history
  • Loading branch information
mrsimonemms committed Aug 27, 2023
1 parent cb4b485 commit 2d1457f
Show file tree
Hide file tree
Showing 28 changed files with 597 additions and 9 deletions.
8 changes: 8 additions & 0 deletions js/hooks/post_gen_project.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

set -e

# Include hidden files
# @link https://askubuntu.com/questions/259383/how-can-i-get-mv-or-the-wildcard-to-move-hidden-files
shopt -s dotglob

mv {{ cookiecutter.type }}/* ./

rm -Rf nestjs svelte

git init -b main
git add .
git commit -m "chore: initial commit"
15 changes: 15 additions & 0 deletions js/{{ cookiecutter.project_name }}/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,19 @@
"settings": {}
}
}
{%- if cookiecutter.type == "nestjs" %},
"postAttachCommand": {
"mysql": "docker run -d -e MYSQL_RANDOM_ROOT_PASSWORD=true -e MYSQL_USER=db -e MYSQL_PASSWORD=password -e MYSQL_DATABASE=db -p 3306:3306 mysql"
},
"containerEnv": {
"DB_TYPE": "mysql",
"DB_HOST": "localhost",
"DB_USERNAME": "db",
"DB_PASSWORD": "password",
"DB_NAME": "db",
"DB_PORT": "3306",
"DB_MIGRATIONS_RUN": "true",
"DB_SYNC": "false"
}
{%- endif %}
}
3 changes: 3 additions & 0 deletions js/{{ cookiecutter.project_name }}/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ Thumbs.db
.commit
.devbox
.envrc

coverage
.nyc_output
7 changes: 1 addition & 6 deletions js/{{ cookiecutter.project_name }}/.licenserc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@ header:
- "go.*"
- "**/*.{json,md,yml,yaml}"
comment: on-failure
language:
Go:
extensions:
- ".go"
comment_style_id: SlashAsterisk

dependency:
files:
- go.mod # If this is a Go project.
- package.json # If this is a npm project.
4 changes: 4 additions & 0 deletions js/{{ cookiecutter.project_name }}/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ repos:
rev: v0.9.2
hooks:
- id: markdownlint-cli2
- repo: https://github.com/pre-commit/mirrors-prettier
rev: "v3.0.2"
hooks:
- id: prettier
4 changes: 4 additions & 0 deletions js/{{ cookiecutter.project_name }}/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.md
*.yml
*.yaml
*.json
8 changes: 8 additions & 0 deletions js/{{ cookiecutter.project_name }}/nest-cli.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
25 changes: 25 additions & 0 deletions js/{{ cookiecutter.project_name }}/nestjs/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
5 changes: 5 additions & 0 deletions js/{{ cookiecutter.project_name }}/nestjs/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80
}
17 changes: 17 additions & 0 deletions js/{{ cookiecutter.project_name }}/nestjs/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"editor.formatOnSave": true,
"editor.rulers": [
80
],
"yaml.schemas": {
"https://json.schemastore.org/github-workflow.json": [
".github/workflows/*.{yml,yaml}"
]
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
36 changes: 36 additions & 0 deletions js/{{ cookiecutter.project_name }}/nestjs/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM node:lts-alpine AS builder
ARG GIT_COMMIT
ARG GIT_REPO
ARG VERSION
USER node
WORKDIR /home/node/app
ENV GIT_REPO="${GIT_REPO}"
ENV GIT_COMMIT="${GIT_COMMIT}"
ENV VERSION="${VERSION}"
COPY --chown=node:node . .
ENV PORT=3000
EXPOSE 3000
RUN npm ci \
&& npm run build
CMD [ "npm", "run", "start:dev" ]

FROM node:lts-alpine
ARG GIT_COMMIT
ARG GIT_REPO
ARG VERSION
WORKDIR /opt/app
ENV GIT_REPO="${GIT_REPO}"
ENV GIT_COMMIT="${GIT_COMMIT}"
ENV VERSION="${VERSION}"
ENV SERVER_PORT=3000
COPY --from=builder /home/node/app/dist dist
COPY --from=builder /home/node/app/node_modules node_modules
COPY --from=builder /home/node/app/package.json package.json
COPY --from=builder /home/node/app/package-lock.json package-lock.json
RUN npm prune --production \
&& npm rebuild \
&& npm dedupe \
&& npm version ${VERSION} --no-git-tag-version --allow-same-version || true
USER node
EXPOSE 3000
CMD [ "npm", "run", "start:prod" ]
54 changes: 54 additions & 0 deletions js/{{ cookiecutter.project_name }}/nestjs/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ConfigModule, ConfigService } from '@nestjs/config';
import { Module } from '@nestjs/common';
import { LoggerModule, Params, PinoLogger } from 'nestjs-pino';
import config from './config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import { PinoTypeOrmLogger } from './lib/pinoTypeOrmLogger';
import { LoggerOptions } from 'typeorm';
import { HealthModule } from './health/health.module';

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: config,
}),
LoggerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService): Promise<Params> =>
configService.get<Params>('logger'),
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule, LoggerModule],
inject: [ConfigService, PinoLogger],
useFactory: async (
configService: ConfigService,
pinoLogger: PinoLogger,
): Promise<TypeOrmModuleOptions> => {
const db = configService.get<TypeOrmModuleOptions>('db');

/* Replace the default TypeOrm logger with PinoTypeOrmLogger */
let logger: 'debug' | PinoTypeOrmLogger = 'debug';
let logging = false;

if (configService.get<LoggerOptions>('db.logging', false)) {
logger = new PinoTypeOrmLogger(pinoLogger);
logging = true;
}

return {
...db,
logging,
logger,
};
},
}),

// Load the internal modules
HealthModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
42 changes: 42 additions & 0 deletions js/{{ cookiecutter.project_name }}/nestjs/src/config/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { registerAs } from '@nestjs/config';
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import * as path from 'path';
import { LoggerOptions } from 'typeorm/logger/LoggerOptions';

export default registerAs('db', (): TypeOrmModuleOptions => {
/* SSL configuration should default to undefined */
let ssl: any;
if (process.env.DB_USE_SSL === 'true') {
ssl = {
ca: process.env.DB_SSL_CA,
cert: process.env.DB_SSL_CERT,
key: process.env.DB_SSL_KEY,
};
}

let logging: boolean | string | undefined = false;
const loggingVar = process.env.DB_LOGGING;

if (loggingVar === 'true') {
logging = true;
} else if (loggingVar === 'false') {
logging = false;
} else {
logging = loggingVar;
}

return {
ssl,
type: (process.env.DB_TYPE as 'mysql' | 'mariadb') ?? 'mysql',
host: process.env.DB_HOST,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
port: process.env.DB_PORT ? Number(process.env.DB_PORT) : undefined,
migrationsRun: process.env.DB_MIGRATIONS_RUN !== 'false',
synchronize: process.env.DB_SYNC === 'true',
autoLoadEntities: true,
logging: (logging as LoggerOptions) ?? true,
migrations: [path.join(__dirname, '..', 'migrations', '*{.ts,.js}')],
};
});
5 changes: 5 additions & 0 deletions js/{{ cookiecutter.project_name }}/nestjs/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import db from './db';
import logger from './logger';
import server from './server';

export default [db, logger, server];
26 changes: 26 additions & 0 deletions js/{{ cookiecutter.project_name }}/nestjs/src/config/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { registerAs } from '@nestjs/config';
import { Params } from 'nestjs-pino';
import * as pino from 'pino';
import * as uuid from 'uuid';

export default registerAs(
'logger',
(): Params => ({
pinoHttp: {
level: process.env.LOG_LEVEL ?? 'info',
serializers: pino.stdSerializers,
genReqId: () => uuid.v4(),
customLogLevel: (_, res, err) => {
const { statusCode } = res;
if (statusCode >= 400 && statusCode !== 404 && statusCode < 500) {
return 'warn';
}
if (statusCode >= 500 || err) {
return 'error';
}

return 'debug';
},
},
}),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { registerAs } from '@nestjs/config';

export default registerAs('server', () => ({
enableShutdownHooks: process.env.SERVER_ENABLE_SHUTDOWN_HOOKS === 'true',
port: Number(process.env.SERVER_PORT ?? 3000),
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { HealthController } from './health.controller';
import {
HealthCheckService,
TerminusModule,
TypeOrmHealthIndicator,
} from '@nestjs/terminus';
import { Test, TestingModule } from '@nestjs/testing';

describe('HealthController', () => {
let controller: HealthController;
let service: any;
let dbCheck: any;

beforeEach(async () => {
service = {
check: jest.fn(),
};

dbCheck = {
pingCheck: jest.fn(),
};

const module: TestingModule = await Test.createTestingModule({
imports: [TerminusModule],
controllers: [HealthController],
providers: [
{
provide: HealthCheckService,
useValue: service,
},
{
provide: TypeOrmHealthIndicator,
useValue: dbCheck,
},
],
}).compile();

controller = module.get<HealthController>(HealthController);
});

it('should return a successful result', async () => {
service.check.mockResolvedValue('OK');
dbCheck.pingCheck.mockResolvedValue('DB OK');

await expect(controller.check()).resolves.toBe('OK');

expect(service.check).toHaveBeenCalled();

await expect(service.check.mock.calls[0][0][0]()).resolves.toBe('DB OK');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Controller, Get, VERSION_NEUTRAL } from '@nestjs/common';
import {
HealthCheckService,
HealthCheck,
TypeOrmHealthIndicator,
} from '@nestjs/terminus';

@Controller({
path: 'health',
version: VERSION_NEUTRAL,
})
export class HealthController {
constructor(
private health: HealthCheckService,
private db: TypeOrmHealthIndicator,
) {}

@Get()
@HealthCheck()
check() {
return this.health.check([
() =>
this.db.pingCheck('database', {
timeout: 30,
}),
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { HealthController } from './health.controller';
import { Module } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';

@Module({
imports: [TerminusModule],
controllers: [HealthController],
})
export class HealthModule {}
Loading

0 comments on commit 2d1457f

Please sign in to comment.