📝 🚀 Checklist for the express project initial setup
- Git setup
- Node version manager setup
- NodeJS project setup
- Typescript setup
- Prettier setup
- Eslint setup
- Git hooks setup
- Application config setup
- ExpressJS app setup
- Logger setup
- Error handling setup
- Tests setup
Start by cd'ing into your template folder
Git setup
Setting up .gitignore: you can either get it manually from this repo OR if you're using VSCode, use this extension to generate it for you.
Do a
git init
, add & then commit your changes.Push this repository to the remote.
Node version manager setup
Install nvm for your platform.
Use the latest LTS version of the time.
Create a file called
.nvmrc
and write the version you're using eg:v20.15.0
Then open a terminal and run
nvm use
, if it fails with an error saying "version not yet installed" then donvm install
followed bynvm use
.Add, commit & push your changes.
NodeJS project setup
Do an
npm init
and answer all the questions.Create a
src
folder.Add, commit & push your changes.
Typescript setup
Run
npm i -D typescript
.Run
npx tsc --init
.Uncomment
rootDir
intsconfig.json
& set it's value to "./src".Uncomment
outDir
intsconfig.json
& set it's value to "./dist".Install types for node
npm i -D @types/node
.Add, commit & push your changes.
Prettier setup (follow official docs)
Install it with the
--save-exact
flag:npm i -D --save-exact prettier
.- Create an empty
.prettierrc
file and add empty object{}
to it. Create an empty
.prettierignore
file and add:build coverage
Now, you can manually check for formatting issues by running:
npx prettier . --check
& fix them all by running:npx prettier . --write
.You can add in
package.json
"scripts" to aid in CI/CD pipelines:"format:check": "prettier . --check", "format:fix": "prettier . --write"
Optionally add the following config to
.prettierrc
(and more):{ "arrowParens": "avoid", "printWidth": 80, "tabWidth": 2, "semi": false, "singleQuote": true, "jsxSingleQuote": true, "trailingComma": "none", "proseWrap": "always" }
Add, commit & push your changes.
Eslint setup (follow official docs)
Run
npm install --save-dev eslint @eslint/js @types/eslint__js typescript typescript-eslint eslint-config-prettier
Create a file
.eslint-config.js
ans paste the following:// @ts-check import eslint from "@eslint/js"; import tseslint from "typescript-eslint"; import eslintConfigPrettier from "eslint-config-prettier"; export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, eslintConfigPrettier, { languageOptions: { parserOptions: { project: true, tsconfigRootDir: import.meta.dirname, }, }, }, { ignores: ["dist/*", "eslint.config.js"], }, { rules: { "no-console": "error", }, } );
Add the followings to scripts in
package.json
:"lint": "eslint .", "lint:fix": "eslint . --fix"
Add, commit & push your changes.
Git hooks setup
Install husky
npm install --save-dev husky
Run
npx husky init
Modify
.husky/pre-commit
file to whatever you want to run before every commit. We'll replace all it's content with:npx lint-staged
Install lint-staged to only run pre-commit hooks on staged code:
npm install --save-dev lint-staged
Add this to your
package.json
:"lint-staged": { "*.ts": [ "npm run lint:fix", "npm run format:fix" ] }
Add, commit & push your changes.
Application config setup
Install dotenv:
npm i dotenv
Create a
.env
file & put all your secrets as key-value pairs.Optionally, also create a
.env.example
file & put all your secrets but with fake values.Create
src/config/index.ts
and paste the following(add your own values):import { config } from "dotenv"; config(); const { PORT, NODE_ENV } = process.env; // later if you want to change the way you're getting env variables (eg. from a file), you can just change this line export const Config = { PORT, NODE_ENV, };
Add, commit & push your changes.
ExpressJS App Setup
- Create an
app.ts
file inside thesrc
folder. - Install express & it's type declarations:
npm i express
&npm i -D @types/express
Put minimal code inside the
app.ts
and export the app.import express from "express"; const app = express(); app.get("/", (req, res) => { res.send("Welcome to Auth service"); }); export default app;
If you see ESLint, try ctrl+shift+p
-> Restart ESLint server.
Create a new file
src/server.ts
and import the app there.import app from "./app"; import { Config } from "./config"; const startServer = () => { const PORT = Config.PORT; try { // eslint-disable-next-line no-console app.listen(PORT, () => console.log(`Listening on port ${PORT}`)); } catch (error) { // eslint-disable-next-line no-console console.error(error); process.exit(1); } }; startServer();
Change the dev script in
package.json
to:"dev": "nodemon src/server.ts",
Install
nodemon
&ts-node
npm i -D ts-node nodemon
Try running
npm run dev
to see if it works.Add, commit & push your changes.
Logger setup
Install Winston & its types.
npm i winston npm i -D @types/winston
Create
src/config/logger.ts
and paste:import winston from 'winston' import { Config } from '.' const logger = winston.createLogger({ level: 'info', defaultMeta: { serviceName: 'auth-service' }, transports: [ new winston.transports.File({ dirname: 'logs', filename: 'combined.log', level: 'info', silent: Config.NODE_ENV !== 'production' }), new winston.transports.File({ dirname: 'logs', filename: 'error.log', level: 'error', silent: Config.NODE_ENV !== 'production' }), new winston.transports.Console({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), silent: Config.NODE_ENV !== 'production' }) ] }) export default logger
In your server.ts
file, replace the console.log
with logger.info
and console.error
with logger.error
. The file will now look like this:
import app from './app'
import { Config } from './config'
import logger from './config/logger'
const startServer = () => {
const PORT = Config.PORT
try {
app.listen(PORT, () => logger.info(`Listening on port ${PORT}`))
} catch (error: unknown) {
if (error instanceof Error) {
logger.error(error.message)
setTimeout(() => process.exit(1), 1000)
}
}
}
startServer()
- Add, commit & push your changes.
Error handling setup
Install
http-errors
npm i http-errors
Add the following global error handler as the last middleware in
app.ts
:// eslint-disable-next-line @typescript-eslint/no-unused-vars app.use((err: HttpError, req: Request, res: Response, next: NextFunction) => { logger.error(err.message) const statusCode = err.statusCode || 500 res.status(statusCode).json({ errors: [ { type: err.name, msg: err.message, path: '', location: '' } ] }) })
Add, commit & push your changes.
Tests setup
Run:
npm i -D jest ts-jest @types/jest supertest @types/supertest npx ts-jest config:init
Step 1 will result in a
jest.config.js
file, rename it's extension to.mjs
and replace it's contents with:/** @type {import('ts-jest').JestConfigWithTsJest} */ export default { preset: 'ts-jest', testEnvironment: 'node' }
In your
package.json
, add another script:"test": "jest --watch --runInBand"
Create a new file called either
app.spec.ts
orapp.test.ts
at the root of your project to test the jest setup.import request from 'supertest' import app from './src/app' describe('App', () => { it('should return 200 status', async () => { const response = await request(app).get('/').send() expect(response.statusCode).toBe(200) }) })
Now run
npm run test
to check if it runs. It should run as well as pass.Add, commit & push your changes.
That's it! Your template is ready, you can use it for all your future projects.