Self-hosted service
Vivify-Ideas/nestjs-boilerplate avatar
Vivify-Ideas/nestjs-boilerplate

A NestJS boilerplate that refuses to skip the boring setup

NestJS Boilerplate 😻(Authentication, TypeORM, Configuration, Swagger)

648 stars93 forksTypeScriptMIT

At a glance

What is it?
This NestJS boilerplate wires TypeORM, MariaDB, JWT authentication, Swagger, nginx and Docker Compose into a working API in one init script, including generating a real signing secret and running migrations. It is a starting point to edit rather than a package to depend on.
Who is it for?
This boilerplate fits a team starting a new REST API with NestJS who wants authentication, migrations, validation and a containerised stack in place on day one and intends to delete the demo surface once the real domain arrives. It does not fit a team that needs a maintained library, since the repository publishes no GitHub releases, or one that wants to skip MariaDB and nginx.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 43 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The init script does the five things you would otherwise forget

The quick start is one command, and the point of it is that the script handles the steps a new project usually gets wrong on day one:

bash
./init

According to the README it copies .env.example to .env when .env is missing, generates a strong JWT_SECRET_KEY when it is empty or still set to changeme, builds and starts the Docker Compose stack, waits for MariaDB to become ready, and runs the TypeORM migrations. That second step is the one that separates this from a typical boilerplate, because a checked-in .env.example with a placeholder secret is the normal way a boilerplate ends up deployed with a known signing key. After startup, the API is reached through nginx at port 8080, the Swagger docs are at the /api/docs path on the same host, and MariaDB is exposed on port 3306.

The local path without Docker is five short steps

For host development the README gives a manual route that starts with installing dependencies with yarn, then creating the environment file with cp .env.example .env and generating a secret with openssl rand -base64 32 for the JWT_SECRET_KEY value. The database has to be running separately, with DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD and DB_DATABASE pointed at it, then migrations run with yarn migration:run and the app starts with yarn start:dev on port 3000. The requirements are modest: a Node.js version compatible with what package.json declares, Yarn, and Docker with Docker Compose for the containerised workflow. Running yarn on the host is also what gives you IDE autocompletion, since the container mounts the source but the editor reads node_modules from the host.

Twelve environment variables and the two that will bite you

The environment table is short enough to read in full, which is a good sign for a starting point. APP_ENV selects the application environment, with dev used for local development, and APP_URL is the value returned by the authenticated root endpoint. JWT_SECRET_KEY is the required signing secret and must be at least 32 characters, while JWT_EXPIRATION_TIME is an optional expiry in seconds, set to 3600 in the example file. CORS_ALLOW_ORIGIN is a comma separated list of allowed origins and is required outside dev. The database group is DB_TYPE, DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD and DB_DATABASE, with mariadb the configured type and db the host name inside Docker Compose. The last one, DB_SYNC, is the TypeORM synchronize flag, and the note is blunt: keep it false outside throwaway development, because schema synchronisation can cause data loss.

Four routes make up the whole API surface

The API is deliberately minimal, which is what makes it a starting point rather than a product. Registering a user is POST /api/auth/register, logging in is POST /api/auth/login and returns a JWT access token, GET /api/auth/me returns the authenticated user, and the root endpoint GET / is authenticated too and returns the APP_URL value. The returned JWT is used as a bearer token on protected routes. Authentication is built on the Passport and JWT packages with the NestJWT module, and passwords are hashed with bcrypt through a TypeORM value transformer, which means the hash travels with the entity rather than being handled in a service by hand. Validation is enabled globally with the Nest ValidationPipe plus a custom trim pipe, so incoming payloads are checked and trimmed before they reach a controller.

Migrations are the source of truth, and the entity glob changes after build

TypeORM configuration lives in src/database/data-source.ts, and the entity loading is worth knowing because it differs between development and production. Entities are loaded from the TypeScript entity files under src in development and from the compiled entity files under dist after the build, because the compiled output is JavaScript in a different directory. The migration scripts are wired through a ts-typeorm wrapper that runs the TypeORM CLI with ts-node, so there is no separate global install to manage. Creating a migration is yarn migration:create with a target path under src/migrations, running them is yarn migration:run, and yarn migration:revert undoes the last one. Inside Docker the same commands need the exec prefix, for example docker exec -it nest yarn migration:run, which is the one small friction point when you work inside the container.

Docker files for development, production, swarm and nginx

The container story is separated into distinct files rather than one multi-stage file, which is a deliberate choice worth noting. There is a Dockerfile for the development app image, a Dockerfile-prod for production, and a Dockerfile-nginx for the reverse proxy. The compose file for local development runs three services: the nest app built from the repository root, nginx built from Dockerfile-nginx, and a MariaDB 10 image with named volumes for the data. The app service mounts the source directory, package.json, yarn.lock and .env into the container, so editing on the host and reloading in the container is the intended loop, while the database is not mounted for code changes. The nginx service publishes 8080 on the host and passes the upstream host and port plus a maximum body size of 100M to the app. A separate docker-compose-swarm.yml file is provided as a deployment template for swarm-oriented setups.

Against starting from a bare Nest application

The alternative is to generate a fresh Nest project and add each piece yourself, or to use an off-the-shelf framework with its own opinions. The difference in approach is where the decisions are made. A bare Nest project gives you the framework and nothing else, so the database layer, the auth strategy, the validation setup, the migration scripts and the container topology all become your choices to make and to maintain. This boilerplate makes them for you in a shape that is easy to read and easy to delete. The cost is that the choices are baked in: MariaDB rather than Postgres, bcrypt through a TypeORM transformer, nginx in front of the app, and a specific script naming convention. And because the repository publishes no GitHub releases and is at version 1.0.0 in package.json, you should treat it as a template to copy and adapt rather than a dependency to upgrade.

Editorial conclusion

This boilerplate fits a team starting a new REST API with NestJS who wants authentication, migrations, validation and a containerised stack in place on day one and intends to delete the demo surface once the real domain arrives. It does not fit a team that needs a maintained library, since the repository publishes no GitHub releases, or one that wants to skip MariaDB and nginx. Verify first that the TypeORM entity glob and the migration folder match your project layout, that you change the DB_SYNC flag and the JWT secret before any shared environment, and that the CORS origin variable is set outside dev, since the default configuration only permits open CORS in development.

Frequently asked questions

How do I start the NestJS boilerplate quickly?

Run the init script, which copies .env.example to .env if missing, generates a strong JWT_SECRET_KEY when it is empty or still changeme, builds and starts the Docker Compose stack, waits for MariaDB and runs the TypeORM migrations. The API is then served through nginx at http://localhost:8080 with Swagger at /api/docs.

What environment variables does the boilerplate require?

APP_ENV, APP_URL, JWT_SECRET_KEY of at least 32 characters, the optional JWT_EXPIRATION_TIME, CORS_ALLOW_ORIGIN which is required outside dev, and the database group DB_TYPE, DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD, DB_DATABASE and DB_SYNC. The example file sets DB_SYNC to false, and synchronisation can cause data loss.

How do I create and run TypeORM migrations here?

Use yarn migration:create with a path under src/migrations, run them with yarn migration:run, and revert the last one with yarn migration:revert. Inside Docker, prefix the command with docker exec -it nest, for example docker exec -it nest yarn migration:run.

Which authentication and validation does the boilerplate use?

Authentication uses the Nest JWT and Passport packages, with bcrypt hashing applied through a TypeORM value transformer, and the returned JWT is used as a bearer token on protected routes. Validation is enabled globally with the Nest ValidationPipe and a custom trim pipe.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Vivify-Ideas/nestjs-boilerplate on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vivify-ideas-nestjs-boilerplate.svg)](https://hysenlabs.com/projects/vivify-ideas-nestjs-boilerplate)

Was this useful?