A production-ready Express.js REST API template built with 3 years of real-world experience
Crafted with best practices, scalability, and developer experience in mind.
This template represents 3 years of hands-on experience building and scaling Express.js applications. It consolidates the best practices, architectural patterns, and production-ready features I've learned while working on numerous projects.
What makes this different?
- β¨ Battle-tested architecture - Used in real production applications
- ποΈ True OOP principles - Clean, maintainable, and scalable code structure
- π Production-ready - Not just a starter, but a complete foundation
- π Full documentation - Including Swagger API docs out of the box
- π Security-first - Rate limiting, JWT auth, input validation, and more
- π οΈ Developer experience - TypeScript, hot reload, logging, error handling
- Clean OOP Architecture - Repository β Service β Controller pattern
- TypeScript - Full type safety across the entire codebase
- Modular Structure - Easy to extend with new modules
- Base Classes - Reduce boilerplate with
BaseRepositoryandBaseService - Dependency Injection - Loosely coupled, testable code
- JWT Authentication - Secure token-based auth
- Password Hashing - Bcrypt with salt rounds
- Role-Based Access Control (RBAC) - Admin, User, and custom roles
- Rate Limiting - Prevent API abuse with configurable limits
- Security Headers - Helmet.js for HTTP security
- CORS Configuration - Cross-origin resource sharing control
- Input Validation - Express-validator for request validation
- Mongoose ODM - Elegant MongoDB object modeling
- Base Repository - Generic CRUD operations with pagination
- Soft Delete Support - Mark as deleted without removing data
- Query Builder - Fluent interface for complex queries
- Pagination - Built-in pagination support
- Indexing - Optimized database queries
- RESTful Design - Industry-standard REST API principles
- Swagger Documentation - Auto-generated interactive API docs
- Custom Response Methods -
res.sendSuccess(),res.sendError(),res.sendCreated() - Request Correlation ID - Track requests across logs
- File Upload - Multer integration for file handling
- Search & Filter - Advanced query capabilities
- Winston Logger - Production-grade logging with daily rotation
- Health Checks -
/health,/health/live,/health/ready - Request Logging - Morgan HTTP request logger
- Error Tracking - Centralized error handling with stack traces
- Performance Monitoring - CPU, memory, and database health
- TypeScript - IntelliSense, type checking, and refactoring support
- Hot Reload - Nodemon for automatic server restart
- Environment Variables - Dotenv for configuration management
- Code Organization - Clear folder structure and naming conventions
- Example Modules - Auth, User, and Product modules included
βββ src/
β βββ modules/ # Feature modules
β β βββ auth/ # Authentication (register, login, JWT)
β β β βββ auth.types.ts # TypeScript interfaces
β β β βββ auth.repository.ts # Data access layer
β β β βββ auth.service.ts # Business logic
β β β βββ auth.controller.ts # HTTP handlers
β β β βββ auth.routes.ts # Route definitions
β β βββ user/ # User management
β β β βββ user.types.ts
β β β βββ user.repository.ts
β β β βββ user.service.ts
β β β βββ user.controller.ts
β β β βββ user.routes.ts
β β βββ product/ # Product CRUD (example)
β β βββ product.types.ts
β β βββ product.repository.ts
β β βββ product.service.ts
β β βββ product.controller.ts
β β βββ product.routes.ts
β β
β βββ shared/ # Shared utilities
β β βββ config/ # Configuration
β β β βββ index.ts # Environment config
β β β βββ swagger.ts # Swagger/OpenAPI config
β β βββ database/ # Database layer
β β β βββ BaseRepository.ts # Generic CRUD repository
β β β βββ BaseService.ts # Generic service layer
β β β βββ models/ # Mongoose models
β β β βββ User.model.ts
β β β βββ Product.model.ts
β β βββ middlewares/ # Express middlewares
β β β βββ auth.middleware.ts # JWT & RBAC
β β β βββ validation.middleware.ts # Input validation
β β β βββ errorHandler.middleware.ts # Error handling
β β β βββ responseHandler.middleware.ts # Response methods
β β β βββ correlationId.middleware.ts # Request tracking
β β β βββ rateLimiter.middleware.ts # Rate limiting
β β βββ utils/ # Utility functions
β β βββ logger.ts # Winston logger
β β βββ response.helper.ts # Response formatting
β β βββ healthCheck.ts # Health monitoring
β β βββ queryBuilder.ts # Query builder
β β βββ fileUpload.ts # File upload utility
β β
β βββ types/ # TypeScript definitions
β β βββ index.ts # Common types
β β βββ express.d.ts # Express augmentation
β β
β βββ app.ts # Express app setup
β βββ index.ts # Server entry point
β
βββ uploads/ # Uploaded files
βββ logs/ # Log files
βββ dist/ # Compiled JavaScript
βββ .env.example # Environment template
βββ tsconfig.json # TypeScript config
βββ package.json # Dependencies
βββ README.md # This file
- Node.js >= 18.x
- MongoDB >= 6.x
- npm or yarn
- Clone the repository
git clone https://github.com/itsusif/express-mongoose-oop-template.git
cd express-mongoose-oop-template- Install dependencies
npm install- Set up environment variables
cp .env.example .env- Configure your
.envfile
# Server Configuration
PORT=3000
NODE_ENV=development
# Database
MONGODB_URI=mongodb://localhost:27017/your-database-name
# JWT Authentication
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
JWT_EXPIRES_IN=7d
# CORS
CORS_ORIGIN=http://localhost:3000
# Rate Limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100- Start the development server
npm run dev- Visit the API documentation
http://localhost:3000/api-docs
This template includes Swagger/OpenAPI documentation out of the box.
Access the interactive API docs:
http://localhost:3000/api-docs
POST /api/auth/register # Register new user
POST /api/auth/login # Login and get JWT tokenGET /api/users/profile # Get current user profile (Auth required)
PUT /api/users/profile # Update profile (Auth required)
GET /api/users # Get all users with pagination (Admin only)
DELETE /api/users/:id # Delete user (Admin only)POST /api/products # Create product (Auth required)
GET /api/products # Get all products (paginated)
GET /api/products/:id # Get product by ID
PUT /api/products/:id # Update product (Owner only)
DELETE /api/products/:id # Delete product (Owner only)
GET /api/products/category/:category # Filter by category
GET /api/products/search?q=keyword # Search productsGET /health # Detailed system health
GET /health/live # Liveness probe
GET /health/ready # Readiness probeβββββββββββββββββββββββββββββββββββββββ
β HTTP Request β
ββββββββββββββββ¬βββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Middlewares β
β (Auth, Validation, CORS, etc.) β
ββββββββββββββββ¬βββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Controller β
β (Handle HTTP, call service) β
ββββββββββββββββ¬βββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Service β
β (Business logic, validation) β
ββββββββββββββββ¬βββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Repository β
β (Database operations) β
ββββββββββββββββ¬βββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β MongoDB Database β
βββββββββββββββββββββββββββββββββββββββ
1. Repository Layer (Data Access)
export class ProductRepository extends BaseRepository<IProduct> {
constructor() {
super(ProductModel);
}
async findByCategory(category: string): Promise<IProduct[]> {
return await this.model.find({ category, isActive: true }).exec();
}
}2. Service Layer (Business Logic)
export class ProductService {
private repository: ProductRepository;
async createProduct(userId: string, data: CreateProductDTO): Promise<ProductResponseDTO> {
// Validate business rules
if (data.price < 0) {
throw new AppError('Price cannot be negative', 400);
}
const product = await this.repository.create({
...data,
createdBy: userId
});
return this.mapToDTO(product);
}
}3. Controller Layer (HTTP Handling)
export class ProductController {
private service: ProductService;
createProduct = async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const userId = req.user!.id;
const result = await this.service.createProduct(userId, req.body);
// Clean response with custom method
res.sendCreated(result, 'Product created successfully');
} catch (error) {
next(error);
}
}
}4. Routes (Endpoint Definition)
export class ProductRoutes {
initializeRoutes() {
this.router.post(
'/',
AuthMiddleware.authenticate,
ValidationMiddleware.validate(createValidation),
RateLimiterMiddleware.moderate,
this.controller.createProduct
);
}
}Let's create a Category module:
- Create the structure:
mkdir -p src/modules/category- Define types (
category.types.ts):
export interface CreateCategoryDTO {
name: string;
slug: string;
description?: string;
}
export interface CategoryResponseDTO {
id: string;
name: string;
slug: string;
description?: string;
createdAt: Date;
}- Create repository (
category.repository.ts):
import { BaseRepository } from '../../shared/database/BaseRepository';
import { CategoryModel, ICategory } from '../../shared/database/models/Category.model';
export class CategoryRepository extends BaseRepository<ICategory> {
constructor() {
super(CategoryModel);
}
}- Create service using BaseService (
category.service.ts):
import { BaseService } from '../../shared/database/BaseService';
import { CategoryRepository } from './category.repository';
import { CreateCategoryDTO, CategoryResponseDTO } from './category.types';
export class CategoryService extends BaseService<
ICategory,
CreateCategoryDTO,
any,
CategoryResponseDTO
> {
constructor() {
super(new CategoryRepository());
}
protected mapToDTO(item: ICategory): CategoryResponseDTO {
return {
id: item._id.toString(),
name: item.name,
slug: item.slug,
description: item.description,
createdAt: item.createdAt
};
}
}import { QueryBuilder } from '../shared/utils/queryBuilder';
// Complex query made simple
const activeUsers = await new QueryBuilder(UserModel)
.where('isActive', true)
.where('role', 'user')
.greaterThan('createdAt', new Date('2024-01-01'))
.search('john')
.sort('createdAt', 'desc')
.paginate(1, 10)
.populate('profile', 'name email')
.select('name email role createdAt')
.execute();import { FileUploadUtil } from '../shared/utils/fileUpload';
// In routes
router.post(
'/upload-avatar',
AuthMiddleware.authenticate,
FileUploadUtil.uploadImage.single('avatar'),
userController.uploadAvatar
);
// In controller
uploadAvatar = async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
if (!req.file) {
return res.sendError('No file uploaded', 400);
}
const fileUrl = FileUploadUtil.getFileUrl(
req.file.filename,
`${req.protocol}://${req.get('host')}`
);
await this.userService.updateAvatar(req.user!.id, fileUrl);
res.sendSuccess({ url: fileUrl }, 'Avatar uploaded successfully');
} catch (error) {
next(error);
}
};# Development
npm run dev # Start dev server with hot reload
npm run build # Build for production
npm start # Start production server
# Code Quality
npm run lint # Run ESLint
npm run format # Format code with Prettier
npm test # Run tests (if configured)All configuration is done through environment variables. See .env.example for all available options.
Key configurations:
| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 3000 |
NODE_ENV |
Environment (development/production) | development |
MONGODB_URI |
MongoDB connection string | - |
JWT_SECRET |
JWT signing secret | - |
JWT_EXPIRES_IN |
Token expiration | 7d |
RATE_LIMIT_WINDOW_MS |
Rate limit window | 900000 (15 min) |
RATE_LIMIT_MAX_REQUESTS |
Max requests per window | 100 |
- π API Documentation - Interactive Swagger docs
Contributions are welcome! If you have suggestions for improvements:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Youssef Naguib
This template is the result of 3 years of building and scaling Express.js applications in production. It consolidates patterns, practices, and solutions that have proven effective across multiple real-world projects.
- Website: usif.me
- GitHub: github.com/itsusif
- LinkedIn: linkedin.com/in/itsusif
- Discord: u.si
If this template helped you build better Express.js applications, please give it a βοΈ on GitHub!
This template incorporates best practices from:
- Express.js official documentation
- Node.js design patterns
- Clean Architecture principles
- Real-world production experience