API using Express and Typescript
An MVC architecture based NodeJS API skeleton using Express and Typescript
Table of Contents
Technologies and Libraries used
- NodeJS
- Typescript
- Yarn
- bcrypt
- cors
- dotenv
- express
- jsonwebtoken
- winston
- eslint
- husky
- lint-staged
- prettier
- tsx
Initial setup using yarn
Initialize the project
yarn --init
Add Editor config
This is to provide similar editor configuration for all developers.
Install VS Code extension EditorConfig for VS Code
Add a file named .editorconfig
with the following contents
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
[*.{diff,md}]
trim_trailing_whitespace = false
Add typescript support
yarn add -D typescript
npx tsc --init
Use the following default settings for .tsconfig.json
{
"compilerOptions": {
"lib": ["ES2018", "dom"],
"target": "ES2018",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
Create a folder src
. This will be our main folder which will contain all files. Create a index.ts
inside this with some simple code like
console.log('Hello world');
Add Eslint
This is used to add linting support i.e, automated checking of source code for programmatic and stylistic errors.
yarn add -D eslint
yarn add -D @typescript-eslint/eslint-plugin
yarn add -D @typescript-eslint/parser
Create a file .eslintrc
and add the following config
{
"parser": "@typescript-eslint/parser",
"extends": ["plugin:@typescript-eslint/recommended"],
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {}
}
Also add the following scripts in package.json
{
"scripts": {
"lint": "eslint src/**/*.ts",
"format": "eslint src/**/*.ts --fix"
}
}
Add Prettier
This helps to achieve similar formatting of code even if different person have written it as per their way, like different tab length.
yarn add -D prettier
Create a file .prettierrc
and add the following config
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 120,
"tabWidth": 2
}
Install Prettier - Code formatter
extension in VS Code.
Choose Prettier as your default code formatter and enable Format on save optionally for better experience.
OR
Press Ctrl
+ Shift
+ P
to open command palette and click on "Open User Settings (JSON)". This opens the settings.json
file. Add the following settings
{
...
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.formatOnSave": true
}
Add Husky
This is to add some git hooks so that we can do some checks like running test cases or linting before pushing to remote and failing there. (or just manually edit .git/hooks)
npx husky-init && yarn
This also adds a "prepare" script in package.json
.
To avoid this one can use
yarn add -D husky
./node_modules/.bin/husky install
Husky should create a .husky
folder. Create a file named pre-commit
here if not already present. This will work as pre-commit hook. Add/modify the content to:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo -e "\n[Husky] pre-commit hook"
yarn lint
Add lint-staged
This can be used to run Prettier on all the staged file which is usefull if someone does not want to use "Format documnet on Save" feature.
To install use:
yarn add -D lint-staged
Then create a new file named .lintstagedrc
with following content to get started:
{
"*.ts": ["eslint src/**/*.ts --fix --max-warnings=0", "prettier --write"],
"*.{js,md}": ["prettier --write"]
}
This instructs lint-staged to run eslint as well as prettier on staged ts files. --max-warnings=0
is used to abort commit if eslint just gives warnings like unused imports or variables. Changes done by lint-staged
are automatically added to git, so no need to run git add .
Then modify the package.json
lint script to use lint-stage instead of eslint
{
"scripts": {
"lint": "lint-staged"
}
}
Project Structure
The main folder here is the src
folder with following structure:
src
├───api
│ ├───helper
│ ├───middleware
│ └───v1
│ └───components
│ ├───auth
│ └───user
├───config
├───services
└───test
Here there are 4 main folders: api
, config
, services
and test
. There will be one more folder models
but I have not added that in this skeleton.
api
folder
This folder contains all API related stuff.
api
│ server.ts
│
├───helper
│ component-routes.ts
│
├───middleware
│ auth-middleware.ts
│ index.ts
│
└───v1
│ index.ts
│
└───components
│ components.ts
│
├───auth
│ auth-controller.ts
│ auth-routes.ts
│
└───user
user-controller.ts
user-routes.ts
The helper
folder contains some interfaces.
middleware
folder contains the common middlewares like cors and auhenticate.
I have used v1
to represent API version. For new version add v2 folder when needed. Try to avoid creating new version and instead update v1 with backward compatibility.
components
folder
components
folder is the main folder containing all the api routes. Each component like user, auth are in their own respective folder. Each component contains controller
and routes
file.
routes
file define all the routes while controller
defines all the logic. Test files can also be placed here
config
folder
This folder contains all global configuration related stuff and constants.
services
folder
This is also an important folder that provides classes/functions that act as an interface to 3rd party libraries or some common functions. It is usually advised to use services from other services or from controller only to create a proper separation of concerns and for better testability. This also makes sure that in future we can easily switch any 3rd party library with another if required.
test
folder
This contains the test files.