Chassis for a REST API using TypeScript, Node.js, Express.js, MongoDB and Redis. Tests are run using Mocha.
node
v22.1.0npm
v10.7.0- MongoDB running locally
- use
node --run start:dev
to run the service in development mode (withNODE_ENV=dev
). - use
node --run nodemon
to run the service in development mode (withNODE_ENV=dev
) using nodemon. - use
node --run lint
for code linting. - use
node --run test
for executing tests.
I also recommend to use ncu
to find outdated dependencies (and run ncu -u
to upgrade package.json
).
App is launched listening on 8080 port by default, set the environment variable PORT to change it.
This folder contains the app source code:
server.js
: functions to start (and stop) app environment: load environment variables, bootstrap app services, set middlewares and when done, init expressmiddlewares
: middlewares to be added to express (i.e. cache, authentication, async error and custom error handlers)routes
: routes definitioncontrollers
: mvc controllersservices
: application servicesmodels
: app models (i.e. mongoose models)dtos
: schemas to validate data received with joi
This folder contains the tests (and the _setup.js
file to start app environment because it's not automatically started when importing server.js
in test files).
This project depends on some environment variables (from .env.[environment]
files):
MONGODB_URI
: MongoDB connection URI used to connect to a MongoDB database server.REDIS_URI
: Redis connection URI used to store cached data returned by the API endpoints.JWT_SECRET
: The secret to sign and verify JWTs.UUID_NAMESPACE
: Namespace for auto-generating UUIDs to use as JWT's JTI.
-
Create a
package.json
file usingnpm init
. Add Node.js and npm versions used:"engines": { "node": ">=22.1.0", "npm": ">=10.7.0" }
Also add
"type": "module"
in order to useimport
instead ofrequire
. -
Install express and mongoose:
npm install express mongoose
. Install also ioredis for caching purposes, and jsonwebtoken and uuid to create an authentication middleware for securing endpoints:npm install ioredis jsonwebtoken uuid
. -
Install dev dependencies such as testing ones (supertest, c8, mocha, chai), linter (eslint, eslint-plugin-json-format) and nodemon:
npm install --save-dev supertest c8 mocha chai
npm install --save-dev eslint eslint-plugin-json-format
npm install --save-dev nodemon
-
Configure eslint:
npx eslint --init
. -
Check the eslint configuration,
.eslintrc.json
file should have:"env": { "node": true, "es2021": true, "mocha": true }
Also add
json-format
plugin (the one installed with the dependencyeslint-plugin-json-format
)"plugins": [ "json-format" ]
Add
.eslintignore
file. -
Create Mocha configuration file
.mocharc.json
. Withexit: true
the server is stopped after executing tests (without the need to click Ctrl+C). -
Create test coverage configuration file
.c8rc.json
. The params set will be needed for the tests to pass successfully. -
Create nodemon configuration file
nodemon.json
including the files that should be ignored when being updated. -
Create npm configuration file
.npmrc
withengine-strict=true
in order to notify with an error alert when trying to install/test/start something without the correct Node.js and npm versions. -
Initialize git repository:
git init
. Add.gitignore
file. -
Install Husky to execute linter fixes and check tests before a commit is created or pushed:
npm install --save-dev husky
. Install husky git hooks (only once):npx husky init
and add it topackage.json
script calledprepare
. If you want to make a commit skipping husky pre-commit git hooks you can usegit commit -m "..." -n
; the same occurs when you want to skip pre-push hooks:git push --no-verify
. -
Install
lint-staged
to check linting only in staged files before making a commit:npm install --save-dev lint-staged
. Add configuration file.lintstagedrc
. -
Install CommitLint dev dependencies to apply Conventional Commits:
npm install --save-dev @commitlint/cli @commitlint/config-conventional
and create its configuration file.commitlintrc.json
:{ "extends": [ "@commitlint/config-conventional" ] }
-
Add
pre-commit
,pre-push
andpre-commit-msg
scripts to be run with husky git hooks:"pre-commit": "npx lint-staged", "pre-commit-msg": "npx --no -- commitlint --edit ${1}", "pre-push": "npx NODE_ENV=test c8 --all mocha",
-
Create
.husky/pre-commit
file to insert command that should be executed before making a commit. This file looks like this:node --run pre-commit
-
Create
.husky/pre-commit-msg
file to insert command that should be executed to check the commit message. This file looks like this:node --run pre-commit-msg
-
Create
.husky/pre-push
file to insert command that should be executed before pushing a commit. This file looks like this:node --run pre-push
If tests fail, commit won't be pushed.
-
Create
index.js
,src/server.js
and the necessary middlewares for authentication, caching and error handling. -
Add routes folder and
routes.js
. Add routing middleware inserver.js
:app.use("/", routes);
. Document routes with OpenAPI Specification. -
Add controllers folder and
controller.js
. -
Add services folder and
service.js
file. -
Add mongoose models in folder models.
-
Add
Dockerfile
and.dockerignore
. After that, you can create de docker image and run the docker container with the following commands:docker build -t [IMAGE_NAME] . docker run --name [CONTAINER_NAME] -p 8080:8080 -t -d [IMAGE_NAME]
-
Configure GitHub Action in
.github/workflows/main.yaml
. This action executes linter and tests and reads the GitHub secrets of the repository to fill the .env file with the secret calledENV_FILE
and use theGITHUB_TOKEN
secret to build and push a Docker image to GitHub Packages. -
Migrate project to TypeScript:
8.1. Install needed dev-dependencies:
npm install typescript tsx @types/node @types/express @types/cors @types/cookie-parser @types/mocha @types/chai @types/supertest --save-dev
.8.2. Add build script to
package.json
:"build": "npx tsc"
8.3. Initialize TypeScript configuration file
tsconfig.json
by runningnode --run build -- --init
.8.4. Migrate
.js
files to.ts
.8.5. Use TypeScript in your database models to define both a document interface and a schema or rely on Mongoose to automatically infer the type from the schema definition.
8.6. Configure eslint:
npm install @typescript-eslint/eslint-plugin @typescript-eslint/parser --save-dev
."extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended" ], "parser": "@typescript-eslint/parser", "plugins": [ "@typescript-eslint", "json-format" ],