Welcome to the Node-Express Starter Template, a modular and scalable foundation for building modern server-side applications using Express and TypeScript.
This template is thoughtfully designed with a strong emphasis on structured logging, standardized responses, global error handling, multi-environment configuration, and database abstraction. It features production-ready configurations for Docker, seamless ORM integration (default: TypeORM), and a robust middleware pipelineβensuring a clean, maintainable, and scalable architecture right from the start.
- π¦ Modular Architecture β Clean, scalable structure for large projects.
- π§ TypeScript Support β Full type safety and modern JS features.
- π± Multi-Environment Configuration β Load
.env
files per environment. - π©΅ Structured Logging with Pino β Fast, readable, and structured logs.
- π Unified Response Dispatcher β Standardized response format across the app.
- π₯ Global Error Handling β Catch and transform all errors consistently.
- π§© Middleware Pipeline β Logger, response formatter, and error catcher.
- π Route Auto-Assembly β Auto-register routes with
RouteAssembly
. - π Aliased Paths β Use concise
@/
and@lib/
imports via TypeScript paths. - π Database Abstraction β Easy-to-switch ORM support (TypeORM, Sequelize, MikroORM).
- π³ Docker Support β Docker + Compose for dev & prod environments.
- π Production-Ready Code β Lean, optimized setup for deployment.
- π Database Initialization β Auto-create/init databases from scripts.
node-express-starter-template/
βββ src/
β βββ config/ # App configuration and env loader
β βββ controllers/ # Route logic controllers
β βββ database/ # Database client and migrations
β βββ dispatchers/ # Response dispatchers (default: response.dispatcher.ts)
β βββ enums/ # Common enums (status codes, messages)
β βββ errors/ # Custom error classes
β βββ handlers/ # Error handler middleware
β βββ middlewares/ # Logger, formatter, error catcher
β βββ models/ # Database models (if any)
β βββ routes/ # Route definitions
β βββ serializers/ # Response serializers
β βββ utils/ # Helpers and utilities
β βββ app.ts # Application assembly
β βββ server.ts # Application entry point
βββ logs/ # Generated application logs
βββ .env.example # Sample environment variables
βββ .gitignore # Files to ignore in Git repository
βββ init-db.sql # Database initialization script
βββ Dockerfile # Dockerfile for building the image
βββ docker-compose.yml # Docker Compose file
βββ .env.example # Environment variables (example file)
βββ nodemon.json # Nodemon configuration file
βββ LICENSE # License file
βββ package.json # Dependencies and scripts
βββ pnpm-lock.yaml # Package lock file for pnpm
βββ tsconfig.json # TypeScript configuration
βββ README.md # Youβre reading this π
Supports per-environment .env
files. Example for development:
NODE_ENV="development"
APP_NAME="my-app"
PORT=3000
LOG_LEVEL="debug"
LOG_FILES_DIRECTORY_NAME="logs"
LOG_FILE_NAME="app.log"
DB_TYPE="postgres"
DB_HOST="localhost"
DB_PORT=5429
DB_USERNAME="postgres"
DB_PASSWORD="postgres"
DB_NAME="my_app"
DB_LOGGING=true
DB_SSL=false
DB_USEUTC=true
π Managed by
src/utils/env.util.ts
.
Uses Pino for fast and structured logs.
- Console + file output support
- Middleware logs incoming requests/responses
- Log level controlled via
.env
The ResponseDispatcher
provides a consistent structure for sending API responses across the application.
- Ensures all responses follow a uniform format
- Handles both success and error responses
- Returns standard fields like
timestamp
,status
,message
, anddata
- Supports optional
details
and errorstack
trace - Designed to integrate with serializers for custom output (TODO: add serializers)
- All errors (operational or unknown) are caught globally.
- Custom errors (e.g.,
NotFoundError
,ForbiddenError
,AppError
) are well formatted. - In production, sensitive error info is hidden.
No more long relative imports!
@/
βsrc/
@lib/
βlib/
Example:
import { logger } from '@/utils/logger.util';
import { IDatabaseClient } from '@lib/database';
Defined in
tsconfig.json
and supported by tooling like ESLint, Jest, etc.
To run the application with Docker, use the following command:
docker compose --env-file .env.development up -d --build
.env.development
(or.env.production
, depending on the environment) is injected into both the app and database containers. Make sure your environment file contains the correct configurations.- Volume mounts & port mappings are pre-configured, enabling the application to be accessed through the mapped ports.
- Environment Switching: The setup works for both development and production environments. The environment is controlled through the
.env
files:.env.development
for development mode..env.production
for production mode.
-
If you're in development mode, you can enable hot-reloading by mounting the
src
directory in thedocker-compose.yml
:volumes: - ./src:/app/src # Dev hot-reload (optional)
This allows automatic code updates without rebuilding the container.
To stop the running containers, use:
docker compose down
This command will stop and remove the containers.
- Easily plug in TypeORM, MikroORM, Prisma, Sequelize, etc.
- Built around an interface (
IDatabaseClient
) to decouple logic from ORM. - Default integration: TypeORM
Example database-client.ts
:
import { DataSource } from 'typeorm';
import { databaseConfiguration } from '@/config';
import { logger } from '@/utils/logger.util';
class DatabaseClient {
private _dataSource = new DataSource(databaseConfiguration);
async connect() {
try {
await this._dataSource.initialize();
logger.debug('Database connection established successfully π');
} catch (error: any) {
logger.error('Error connecting to database', error.code);
process.exit(1);
}
}
async disconnect() {
await this._dataSource.destroy();
}
get dataSource() {
return this._dataSource;
}
}
export const databaseClient = new DatabaseClient();
git clone https://github.com/AhmedDiaab/node-express-starter-template.git
cd node-express-starter-template
pnpm install
cp .env.example .env.development
# Edit values as needed
pnpm dev
- Add routes in
src/routes/
& auto-register viaRouteAssembly
. - Write logic in
src/controllers/
, send responses viaResponseDispatcher
. - Customize responses in
responseFormatter
undersrc/middlewares/
. - Add new middleware under
src/middlewares/
. - Create reusable serializers or error types as needed.
Contributions are welcome! Submit issues, feature ideas, or PRs π
GPL-3.0 Β© Ahmed Diaab