9.9 KiB
Development rules
Project configuration rules
If you create your own project, do not forget to write a runbook. It should contain detailed instructions on how to run your project.
Runbook example:
## Service build information
There are different stages of building the application for this service. Based on the environment you want to deploy we have different ways to build the application. following information may help with building the service.
### Regular user
```bash
npm install
npm run build
npm run test:ci
npm start:{dev || debug || prod}
```
### Advanced user
```bash
cd scripts
bash run.sh -h
2022.05.30.14.43
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-buildDocker] [-runDocker] [-runApp] [-runDoc] [-packageHelm]
This script helps you to run the application in different forms. below you can get the full list of available options.
Available options:
-h, --help Print this help and exit
-buildDocker Build the docker image called "imageName:latest"
-runDocker Build the docker image and run on local machine
-runApp Run application with npm in usual way for development
-runDoc Generate the code documentation
-packageHelm makes a helm package from the helm chart.
```
package.json configuration
package.json
example:
{
"name": "My favourite microservice",
"version": "0.0.1",
"description": "Microservice not responsible for anything",
"author": "Dipal Team",
"private": true,
"license": "Apache 2.0",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:pipeline": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"compodoc": "./node_modules/.bin/compodoc -p tsconfig.json -w -s -r 8005 --theme 'readthedocs'",
"rei": "rm -r node_modules/ dist/ package-lock.json && npm i "
},
"dependencies": {},
"devDependencies": {},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
If you install package that not supposed on production (like compodoc, faker or types for TS) add it in
devDependencies
, notdependencies
. It can be the reason why you can not pass deploying on pipeline.
Enviromental variables file
Environment variables offer information on the process's operating environment (producton, development, build pipeline, and so on). Environment variables in Node are used to store sensitive data such as passwords, API credentials, and other information that should not be written directly in code. Environment variables must be used to configure any variables or configuration details that may differ between environments.
You should always provide .env.example
with all the enviromental variables with empty values. This is an example:
DB_USERNAME=
DB_PASSWORD=
DB_HOST=
DB_PORT=
DB_NAME=
Do not forget to add
.env
file inside the.gitignore
file.
Inside your code you should import environment variables using the dotenv
lib. You must do this before all your code, especially importing configs in the main.js
file:
import * as dotenv from "dotenv";
dotenv.config();
import { config } from "./infrastructure/config/config";
Compodoc
NestJS, like Angular, supports Compodoc to automatically create documentation based on developers' comments. This is internal documentation for backend developers. In it you can see the project structure, modules, endpoints, variables, functions, etc.
You need to set the port inside package.json and be sure that it is free:
"compodoc": "./node_modules/.bin/compodoc -p tsconfig.json -w -s -r 8005 --theme 'readthedocs'",
So always write comments on your code.
Lint
ESLint statically analyzes your code to quickly find problems in JS/TS code. You can integrate it inside your VSCode.
We have a .eslintrc.json
file with standart rules:
{
"env": {
"browser": false,
"es2021": true
},
"extends": [
"prettier",
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"overrides": [],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"rules": {
"indent": ["warn", 2],
"linebreak-style": ["warn", "unix"],
"quotes": ["warn", "double"],
"semi": ["warn", "always"]
}
}
And the .prettierrc
file":
{
"arrowParens": "avoid",
"bracketSpacing": false,
"endOfLine": "lf",
"insertPragma": false,
"singleAttributePerLine": false,
"bracketSameLine": true,
"printWidth": 120,
"proseWrap": "always",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false,
"parser": "typescript"
}
Coding style
In our company we keep a close eye on the quality of the code. Since we do not have much time for refactoring, your code must follow to all principles and be easy to read for all members of our team. Configure your VSCode according on style that we have.
You can find coding style rules here.
HTTP REST
You need to write standartized requests and return correct responses with your APIs.
You can find HTTP requests and responses rules here.
Test-driven development
We stick to test-driven development (TDD) because we need to be 100% sure that our code works perfectly and there are no silly bugs in it. You alone are responsible for unit tests of your code. When you write tests first, you can better understand the functionality you are implementing. While writing tests, you must think as a user of your module. You should predict all the situations that can happen and check if the code works correctly.
Here are the common rules you must follow writing unit tests:
One test checks one thing. Create mocks and stubs for dependencies. Try as many different types of input as possible. Create a factory for input and output information.
Test example:
it("Should encrypt according to args", async () => {
let algorithm: any, securityKey: any, initVector: any, data: any;
const args = test_cases.crypto.args;
const expectation = test_cases.crypto.expectation;
jest.spyOn(crypto, "createCipheriv").mockImplementation((x: any, y: any, z: any) => {
algorithm = x;
securityKey = y;
initVector = z;
return {
update: (_x: any, _y: any, _z: any) => {
data = _x;
return expectation.update;
},
final: (_x: any) => expectation.final,
} as Cipher;
});
let result = service.encrypt(args.data, args.initVector, args.algorithm);
expect(result).toBe(expectation.update + expectation.final);
expect(algorithm).toBe(args.algorithm);
expect(initVector).toBe(args.initVector);
expect(data).toBe(args.data);
expect(securityKey).toBeDefined();
expect(crypto.createCipheriv).toBeCalledTimes(1);
});
});
When you start writing code, all the tests will fail. Your task is to make them successful.
NestJS application architecture and rules for module creation
For clean code, we use Domain-Driven design. The simple structure of project is:
- Application.
- Controllers. Handle incoming requests and returning responses to the client
- DTOs. Define how the data will be sent over the network.
- Domain.
- Decorators.
- Constants.
- Enums.
- Filters. Process all unhandled exceptions across an application.
- Guards. Determine whether a given request will be handled by the route handler or not (for example, authefication).
- Interceptors. Process the request before handling and the response after handling.
- Interfaces.
- Modules.
- Repositories. Mediate between the domain and data mapping layers.
- Services. Implement business logic.
Always write a Swagger documentation for your endpoints.
When you write DTO, validate all the fields with
class-validator
.
DTO example:
export class SetRegistrationTokenDTO {
/**
* Registration token for the user's app
*/
@IsNotEmpty()
@IsString()
@Matches(/[A-Za-z0-9\-_]{22}:[A-Za-z0-9\-_]{140}/)
@ApiProperty({
description: "Registration firebase token",
example:
"fwcHeh8bSh6KRGQPd-6B2H:APA91bEV1KGorVt7bPHcgbuRCJrxWVmgXdUZvVmnOfB9rYhDGAudTtSvZu8qwT_hErYp0ONWR8MQIzpN6B7FlwdpMMYG2vjU1T1KXE0NEKhZc1d8hc9YwKWqXqNgyMnRuhN074Wziw9Q",
})
registration_token: string;
}
Don't forget to use NestJS features (e.g. filters) and design patterns.
When creating any new environment variable always notify your supervisor.
You can read full NestJS documentation here.
MongoDB and query rules
You can read full MongoDB documentation here.
Documentation and comments writing rules
- Follow the rules of the English language.
- Don't write a lot of text, write as concisely and clearly as possible.
- Put the Swagger documentation in a separate file in the
docs
folder. - You can see code documentation using
compodoc
.
Good comments example:
/**
** matches US social security number
**/
let pattern = Pattern("^\\{3}-\\d{2}-\\d{4}$")
//=============================================================================================================
// use this thing to divide different functions and methods
// TODO: implement functionality