A production-ready boilerplate for building RESTful APIs with Node.js, Express.js, and PostgreSQL.
- Features
- Project Structure
- Quick Start
- π³ Docker Setup
- Testing
- API Versioning
- Router Configuration
- API Testing Examples
- API Endpoints
- Environment Variables
- Scripts
- Development
- Server Startup Information
- Database Setup
- Troubleshooting
- Testing
- Deployment
- Contributing
- License
- Security
- Support
- π TODO
- Node.js 20.x - Latest LTS version
- Express.js - Fast, unopinionated web framework
- PostgreSQL - Reliable relational database
- Sequelize v6 - Stable ORM with migrations and seeders
- JWT Authentication - Secure token-based authentication
- Email Service - Nodemailer for email verification and password reset
- API Versioning - Flexible API versioning with middleware support
- SWC - Fast TypeScript/JavaScript compiler for building
- Docker - Containerized development and deployment
- ESLint v9 - Modern code linting with flat config
- Mocha v11 + Chai v5 - Latest testing framework
- Morgan - HTTP request logger middleware
- Compression - Response compression middleware
- Security - Helmet, CORS, and rate limiting
- Validation - Express-validator for input validation
βββ src/ # Source code
β βββ bin/ # Application entry point
β β βββ www.js
β βββ config/ # Configuration files
β β βββ config.js
β β βββ database.js
β β βββ router.js
β β βββ router.example.js
β βββ controllers/ # Route controllers
β β βββ authController.js
β β βββ userController.js
β βββ database/ # Database related files
β β βββ migrations/
β β βββ models/
β β β βββ index.js
β β β βββ User.js
β β βββ seeders/
β βββ helpers/ # Helper functions
β β βββ emailService.js
β β βββ tokenGenerator.js
β βββ middlewares/ # Custom middlewares
β β βββ apiVersion.js
β β βββ auth.js
β β βββ errorHandler.js
β β βββ notFound.js
β β βββ validation.js
β βββ routes/ # API routes
β β βββ validations/
β β β βββ auth.js
β β βββ auth.js
β β βββ users.js
β βββ utils/ # Utility functions
β β βββ apiVersion.js
β βββ app.js # Express application
βββ test/ # Test files
β βββ auth.test.js
βββ docker-compose.yml # Docker compose configuration
βββ Dockerfile # Docker configuration
βββ package.json
- Node.js 20.x or higher
- PostgreSQL 12 or higher
- Docker (optional)
- Clone the repository:
git clone <repository-url>
cd node-express-pg-starter
- Clean install dependencies (recommended):
npm run setup
Or install normally:
npm install
Note: If you see deprecation warnings during installation, run
npm run setup
to do a clean install with the latest package versions.
- Set up environment variables:
cp .env.example .env
# Edit .env with your configuration
- Set up the database:
# Create database
npm run db:create
# Run migrations
npm run db:migrate
- Start the development server:
npm run dev
The server will start on http://localhost:3000
.
This project includes a complete Docker setup for containerized development and deployment.
- App: Node.js application running on port 3000
- PostgreSQL: Database running on port 5433 (to avoid conflicts with host PostgreSQL)
# Start all services in detached mode
sudo docker compose up -d
# Check status
sudo docker compose ps
# View logs
sudo docker compose logs app
sudo docker compose logs postgres
# Stop all services
sudo docker compose down
# Build Docker image
sudo docker compose build
# Start services with build
sudo docker compose up --build -d
# Stop and remove volumes (clean restart)
sudo docker compose down -v
# Access running container
sudo docker exec -it node-express-pg-starter-app-1 sh
The Docker setup uses the following environment variables (configured in docker-compose.yml
):
NODE_ENV=production
DB_HOST=postgres
DB_PORT=5432
DB_NAME=node_express_pg_starter
DB_USERNAME=postgres
DB_PASSWORD=password
DB_DIALECT=postgres
JWT_SECRET=your_production_jwt_secret_here
Test if the containerized application is running:
# Test health endpoint
curl http://localhost:3000/health
# Expected response:
# {"status":"OK","timestamp":"...","environment":"production","apiVersion":"v1","supportedVersions":["v1"],"apiPrefix":"none"}
Development: Use npm run dev
for local development with hot reload and debugging.
Production: Use Docker for production deployment with optimized builds and security.
β οΈ Important: Before running tests, you must set up the test environment properly.
- Set NODE_ENV to test:
export NODE_ENV=test
- Set up test database:
npm run test:setup
- Run tests:
npm test
For detailed testing instructions, see TEST_GUIDE.md.
This boilerplate includes comprehensive API versioning support that's ready for production use.
- Flexible Versioning: Support for path-based versioning (e.g.,
/v1/users
) - Backward Compatibility: Routes work both with and without version prefixes
- Version Validation: Automatic validation of supported API versions
- Response Headers: Automatic version headers in all responses
- Deprecation Support: Built-in middleware for deprecation warnings
- No
/api
Prefix: Designed for subdomain-based APIs (e.g.,api.yourapp.com
)
Add to your .env
file:
# API Configuration
API_VERSION=v1 # Current API version
API_PREFIX_ENABLED=false # Enable/disable API prefix (for subdomain usage)
The boilerplate supports two routing modes:
-
Subdomain Mode (Recommended):
API_PREFIX_ENABLED=false
api.yourapp.com/v1/auth/login api.yourapp.com/v1/users/profile
-
Path-based Mode:
API_PREFIX_ENABLED=true
yourapp.com/api/v1/auth/login yourapp.com/api/v1/users/profile
The API supports both versioned and non-versioned endpoints:
With API Prefix Enabled (API_PREFIX_ENABLED=true
):
# Versioned routes (recommended)
GET /api/v1/auth/login
GET /api/v1/users/profile
POST /api/v1/auth/register
# Backward compatibility (without version)
GET /api/auth/login
GET /api/users/profile
POST /api/auth/register
# Version info endpoints (always at root)
GET /health # Includes API version information
GET /version # Detailed version information
With API Prefix Disabled (API_PREFIX_ENABLED=false
):
# Versioned routes (recommended)
GET /v1/auth/login
GET /v1/users/profile
POST /v1/auth/register
# Backward compatibility (without version)
GET /auth/login
GET /users/profile
POST /auth/register
# Version info endpoints (always at root)
GET /health # Includes API version information
GET /version # Detailed version information
All API responses include version information:
X-API-Version: v1
X-API-Supported-Versions: v1
To add a new API version (e.g., v2):
- Update supported versions in
src/app.js
:
app.use(validateApiVersion(['v1', 'v2']))
- Create versioned routes:
app.use(`/v2/auth`, authV2Routes)
app.use(`/v2/users`, userV2Routes)
- Update version headers in
src/middlewares/apiVersion.js
:
'X-API-Supported-Versions': 'v1,v2'
Mark old versions as deprecated:
const { deprecationWarning } = require('./middlewares/apiVersion')
// Add deprecation warning for v1
app.use(deprecationWarning('v1', '2024-12-31', 'API v1 will be removed'))
Response will include deprecation headers:
Deprecation: true
Sunset: 2024-12-31
X-Deprecation-Warning: API v1 will be removed
The src/utils/apiVersion.js
provides helpful utilities:
const { getCurrentVersion, createVersionedPath, isVersion } = require('./utils/apiVersion')
// Get current API version
const version = getCurrentVersion() // 'v1'
// Create versioned paths
const path = createVersionedPath('/users') // '/v1/users'
// Check request version
const isV1 = isVersion(req, 'v1') // true/false
The boilerplate now uses a clean, modular router configuration system that separates routing logic from the main application file.
src/config/router.js
- Main router configurationsrc/config/router.example.js
- Examples for extending routessrc/app.js
- Clean application setup (no route definitions)
- Modular Setup: Routes are organized in logical functions
- Order Management: Ensures middleware and routes are applied in correct order
- Version Management: Easy addition of new API versions
- Error Handling: Centralized error handling setup
- Extensibility: Simple pattern for adding custom routes
To add a new API version (e.g., v2):
- Update supported versions in
src/config/router.js
:
const getSupportedVersions = () => {
return ['v1', 'v2'] // Add v2
}
const setupVersioningMiddleware = (app) => {
app.use(validateApiVersion(['v1', 'v2'])) // Add v2
app.use(addVersionHeaders())
}
- Create v2 routes:
mkdir src/routes/v2
# Create your v2 route files
- Use the router helper:
const { addNewVersion } = require('./config/router')
const authV2Routes = require('./routes/v2/auth')
const userV2Routes = require('./routes/v2/users')
// Add in app.js after setupRoutes(app)
addNewVersion('v2', {
auth: authV2Routes,
users: userV2Routes
}, app)
For environment-specific or custom routes:
// In your main app setup
const { setupRoutes } = require('./config/router')
const { setupCustomRoutes } = require('./config/router.example')
setupRoutes(app) // Main API routes
setupCustomRoutes(app) // Additional custom routes
Function | Description |
---|---|
setupRoutes(app) |
Complete route setup (recommended) |
setupVersioningMiddleware(app) |
API versioning middleware only |
setupUtilityRoutes(app) |
Health and version endpoints |
setupDevelopmentRoutes(app) |
Development-only routes and utilities |
setupVersionedRoutes(app) |
Versioned API routes |
setupBackwardCompatibilityRoutes(app) |
Non-versioned routes |
setupErrorHandling(app) |
Error handling middleware |
addNewVersion(version, routes, app) |
Add new API version |
getSupportedVersions() |
Get array of supported versions |
getApiPrefix() |
Get current API prefix setting |
The boilerplate includes built-in development routes that are automatically enabled when NODE_ENV=development
.
Available Development Endpoints:
-
Development Test (always at root):
GET /dev/test
- Basic development endpoint test- Returns: Environment info, API config, timestamp
-
Development Info (respects API prefix):
- If
API_PREFIX_ENABLED=false
:GET /dev/info
- If
API_PREFIX_ENABLED=true
:GET /api/dev/info
- Returns: Detailed server configuration and environment info
- If
Example Responses:
# Test endpoint
curl http://localhost:3000/dev/test
{
"message": "Development test endpoint - working!",
"environment": "development",
"apiPrefix": "none",
"apiVersion": "v1",
"timestamp": "2024-01-15T10:30:00.000Z",
"note": "This endpoint is always at root level"
}
# Info endpoint
curl http://localhost:3000/dev/info
{
"message": "Development info endpoint - working!",
"environment": "development",
"currentPrefix": "none",
"fullPath": "/dev/info",
"apiVersion": "v1",
"serverConfig": {
"prefixEnabled": false,
"nodeEnv": "development",
"port": 3000
},
"timestamp": "2024-01-15T10:30:00.000Z"
}
Features:
- β Auto-registration - No manual setup required
- β Environment-aware - Only available in development
- β Prefix-aware - Respects API prefix configuration
- β Informative responses - Detailed server and config info
- β Console logging - Registration status shown on startup
# Test if API is running
curl http://localhost:3000/health
# Expected response:
{
"status": "OK",
"timestamp": "2024-01-15T10:30:00.000Z",
"environment": "development",
"apiVersion": "v1",
"supportedVersions": ["v1"],
"apiPrefix": "none"
}
# Get detailed version info
curl http://localhost:3000/version
# Expected response:
{
"currentVersion": "v1",
"requestedVersion": "v1",
"supportedVersions": ["v1"],
"apiPrefix": "none",
"timestamp": "2024-01-15T10:30:00.000Z"
}
# Test basic development endpoint
curl http://localhost:3000/dev/test
# Test development info (prefix-aware)
curl http://localhost:3000/dev/info
# or if API_PREFIX_ENABLED=true:
curl http://localhost:3000/api/dev/info
# Register new user (versioned - recommended)
curl -X POST http://localhost:3000/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "password123",
"name": "Test User"
}'
# Login (backward compatibility)
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "password123"
}'
With Prefix Disabled (API_PREFIX_ENABLED=false
):
curl http://localhost:3000/v1/users/profile
curl http://localhost:3000/auth/login
With Prefix Enabled (API_PREFIX_ENABLED=true
):
curl http://localhost:3000/api/v1/users/profile
curl http://localhost:3000/api/auth/login
All API responses include version headers:
# Check response headers
curl -I http://localhost:3000/health
# Expected headers include:
# X-API-Version: v1
# X-API-Supported-Versions: v1
GET /health
- Health check endpoint with API version infoGET /version
- Detailed API version information
Note: Health and version endpoints are always available at root level, regardless of API prefix setting.
With API Prefix Enabled (API_PREFIX_ENABLED=true
):
Versioned (Recommended):
POST /api/v1/auth/register
- Register a new userPOST /api/v1/auth/login
- Login userPOST /api/v1/auth/verify-email
- Verify email addressPOST /api/v1/auth/forgot-password
- Request password resetPOST /api/v1/auth/reset-password
- Reset passwordPOST /api/v1/auth/change-password
- Change password (authenticated)POST /api/v1/auth/resend-verification
- Resend verification email (authenticated)
Backward Compatibility:
POST /api/auth/register
- Register a new userPOST /api/auth/login
- Login userPOST /api/auth/verify-email
- Verify email addressPOST /api/auth/forgot-password
- Request password resetPOST /api/auth/reset-password
- Reset passwordPOST /api/auth/change-password
- Change password (authenticated)POST /api/auth/resend-verification
- Resend verification email (authenticated)
With API Prefix Disabled (API_PREFIX_ENABLED=false
):
Versioned (Recommended):
POST /v1/auth/register
- Register a new userPOST /v1/auth/login
- Login userPOST /v1/auth/verify-email
- Verify email addressPOST /v1/auth/forgot-password
- Request password resetPOST /v1/auth/reset-password
- Reset passwordPOST /v1/auth/change-password
- Change password (authenticated)POST /v1/auth/resend-verification
- Resend verification email (authenticated)
Backward Compatibility:
POST /auth/register
- Register a new userPOST /auth/login
- Login userPOST /auth/verify-email
- Verify email addressPOST /auth/forgot-password
- Request password resetPOST /auth/reset-password
- Reset passwordPOST /auth/change-password
- Change password (authenticated)POST /auth/resend-verification
- Resend verification email (authenticated)
With API Prefix Enabled (API_PREFIX_ENABLED=true
):
Versioned (Recommended):
GET /api/v1/users/profile
- Get user profile (authenticated)PUT /api/v1/users/profile
- Update user profile (authenticated)DELETE /api/v1/users/account
- Deactivate account (authenticated)GET /api/v1/users
- Get all users (authenticated)
Backward Compatibility:
GET /api/users/profile
- Get user profile (authenticated)PUT /api/users/profile
- Update user profile (authenticated)DELETE /api/users/account
- Deactivate account (authenticated)GET /api/users
- Get all users (authenticated)
With API Prefix Disabled (API_PREFIX_ENABLED=false
):
Versioned (Recommended):
GET /v1/users/profile
- Get user profile (authenticated)PUT /v1/users/profile
- Update user profile (authenticated)DELETE /v1/users/account
- Deactivate account (authenticated)GET /v1/users
- Get all users (authenticated)
Backward Compatibility:
GET /users/profile
- Get user profile (authenticated)PUT /users/profile
- Update user profile (authenticated)DELETE /users/account
- Deactivate account (authenticated)GET /users
- Get all users (authenticated)
Note: These endpoints are only available when NODE_ENV=development
Always Available (No Prefix):
GET /dev/test
- Development test endpoint with config info
Prefix-Aware Endpoints:
With API Prefix Enabled (API_PREFIX_ENABLED=true
):
GET /api/dev/info
- Detailed development info endpoint
With API Prefix Disabled (API_PREFIX_ENABLED=false
):
GET /dev/info
- Detailed development info endpoint
Variable | Description | Default |
---|---|---|
NODE_ENV |
Environment (development/test/production) | development |
PORT |
Server port | 3000 |
HOST |
Server host | localhost |
API_VERSION |
Current API version | v1 |
API_PREFIX_ENABLED |
Enable/disable API prefix for subdomain usage | false |
DB_HOST |
Database host | localhost |
DB_PORT |
Database port | 5432 |
DB_NAME |
Database name | node_express_pg_starter |
DB_USERNAME |
Database username | postgres |
DB_PASSWORD |
Database password | password |
DB_DIALECT |
Database type (postgres/mysql/sqlite/mssql) | postgres |
JWT_SECRET |
JWT secret key | your_jwt_secret_key_here |
JWT_EXPIRES_IN |
JWT expiration time | 7d |
MAIL_HOST |
Email SMTP host | smtp.gmail.com |
MAIL_PORT |
Email SMTP port | 587 |
MAIL_USERNAME |
Email username | - |
MAIL_PASSWORD |
Email password | - |
MAIL_FROM |
Email from address | noreply@yourapp.com |
RATE_LIMIT_WINDOW_MS |
Rate limit window in milliseconds | 900000 |
RATE_LIMIT_MAX_REQUESTS |
Maximum requests per window | 100 |
FRONTEND_URL |
Frontend URL for email links | http://localhost:3000 |
npm run setup
- Clean install all dependencies (recommended)npm run clean
- Remove node_modules and package-lock.jsonnpm run dev
- Start development server with auto-restart (nodemon + SWC)npm run dev:swc
- Start development server without auto-restart (SWC only)npm run dev:inspect
- Start development server with debugger supportnpm start
- Start production servernpm run build
- Build application with SWCnpm test
- Run testsnpm run test:watch
- Run tests in watch modenpm run lint
- Run ESLintnpm run lint:fix
- Fix ESLint errorsnpm run db:create
- Create databasenpm run db:migrate
- Run database migrationsnpm run db:migrate:undo
- Undo last migrationnpm run db:seed
- Run database seedersnpm run db:seed:undo
- Undo database seeders
- Recommended: Auto-restart with file watching
npm run dev
This uses nodemon + SWC for the best development experience:
- β Auto-restart when files change
- β Fast compilation with SWC
- β
Watches only
src/
directory - β Colorful output with restart notifications
- Simple: One-time run without watching
npm run dev:swc
- Debugging: With Chrome DevTools support
npm run dev:inspect
Then open Chrome and go to chrome://inspect
to debug your application.
The development server watches for changes in:
- All
.js
files insrc/
directory - All
.json
files insrc/
directory - Ignores test files, node_modules, and build output
Create your .env
file from the example:
cp .env.example .env
Edit .env
with your specific configuration.
When you start the development server, you'll see detailed information about your API configuration and available endpoints.
The server provides comprehensive startup information including:
π ===== SERVER STARTED SUCCESSFULLY ===== π
π Server URL: http://localhost:3000
π Environment: development
βοΈ API Version: v1
πͺ API Prefix: DISABLED (no prefix)
βοΈ ===== CONFIGURATION SUMMARY ===== βοΈ
Port: 3000
Host: localhost
Node Environment: development
API Version: v1
API Prefix Enabled: false
Frontend URL: http://localhost:3000
π ===== AVAILABLE ENDPOINTS ===== π
π₯ Health & Info:
GET http://localhost:3000/health
GET http://localhost:3000/version
π API Endpoints (no prefix):
π Versioned Routes (Recommended):
POST http://localhost:3000/v1/auth/login
POST http://localhost:3000/v1/auth/register
GET http://localhost:3000/v1/users/profile
π Backward Compatibility:
POST http://localhost:3000/auth/login
POST http://localhost:3000/auth/register
GET http://localhost:3000/users/profile
π‘ TIP: Your API is configured for subdomain-style routing.
In production, use: api.yourapp.com instead of yourapp.com/api
π― ===== QUICK TEST COMMANDS ===== π―
curl http://localhost:3000/health
curl http://localhost:3000/version
π οΈ ===== DEVELOPMENT MODE ===== π οΈ
Auto-restart: β
Enabled
File watching: src/**/*.js
π§ͺ Development Endpoints:
GET http://localhost:3000/dev/test
GET http://localhost:3000/dev/info
π§ͺ Development routes registered:
β
GET /dev/test
β
GET /dev/info
==================================================
- π Complete URLs - Ready-to-copy endpoint URLs
- βοΈ Configuration Summary - Current server settings at a glance
- π Route Overview - All available endpoints organized by type
- π‘ Smart Tips - Context-aware suggestions based on your config
- π― Quick Tests - Copy-paste curl commands for immediate testing
- π οΈ Development Info - Development-specific features and routes
- π§ͺ Route Registration - Confirmation of successfully registered routes
This makes it easy to understand your API structure and start testing immediately after server startup.
This project uses Sequelize v6 (stable version) with PostgreSQL.
- Create database:
npm run db:create
- Run migrations:
npm run db:migrate
- Seed demo data (optional):
npm run db:seed
After seeding, you can login with:
- Admin:
admin@example.com
/password123
- User:
user@example.com
/password123
This boilerplate uses Sequelize v6 (not v7 alpha) for stability. Key features:
- β UUID v4 primary keys
- β
Proper operators import (
Op.gt
,Op.in
, etc.) - β Async/await syntax throughout
- β Auto-timestamps (createdAt, updatedAt)
- β Indexes for performance
- β Hooks for password hashing
Note: If you prefer Sequelize v7, update package.json and check for any breaking changes in the migration guide.
If you encounter deprecation warnings during installation:
npm warn deprecated inflight@1.0.6: This module is not supported...
npm warn deprecated glob@8.1.0: Glob versions prior to v9 are no longer supported
Solution: Run a clean install:
npm run setup
This will remove old cached packages and install the latest versions.
This project uses ESLint v9 with the new flat config format. The configuration file is eslint.config.js
(not .eslintrc.js
).
Run the test suite:
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run specific test file
npm test -- --grep "Authentication"
- Build the application:
npm run build
-
Set production environment variables
-
Run database migrations:
NODE_ENV=production npm run db:migrate
- Start the application:
npm start
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for your changes
- Ensure all tests pass
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
For security concerns, please email ikhwanulabiyu@gmail.com instead of using the issue tracker.
If you have any questions or need help, please open an issue on GitHub.
-
GitHub Actions CI/CD Pipeline
- Automated testing with PostgreSQL service
- Docker build and push
- Security scanning (npm audit + Snyk)
- Deployment automation
-
API Features
- Rate limiting configuration
- API documentation (Swagger/OpenAPI)
- Request/response logging
- API metrics and monitoring
-
Database
- Database connection pooling optimization
- Database backup strategies
- Performance monitoring
-
Security
- Two-factor authentication (2FA)
- OAuth integration (Google, GitHub, etc.)
- API key management
- Security headers optimization
-
Testing
- Integration test improvements
- Performance testing
- Test coverage reporting
- E2E testing with Playwright/Cypress
-
DevOps
- Kubernetes deployment configurations
- Environment-specific configurations
- Health check improvements
- Logging aggregation (ELK stack)
-
Developer Experience
- Hot reload improvements
- Debugging tools
- Code generation scripts
- Development utilities