A production-ready REST API built with Node.js, TypeScript, and Clean Architecture principles. This project demonstrates how to create a maintainable, scalable, and testable backend application with proper separation of concerns.
- β Clean Architecture - Clear separation of concerns across 4 layers
- β Database-Agnostic - Easy to switch from MongoDB to PostgreSQL, MySQL, etc.
- β TypeScript - Full type safety with strict mode enabled
- β Repository Pattern - Abstracted data access layer
- β Dependency Injection - Manual DI using factory pattern
- β Validation - Input validation using Joi schemas
- β Error Handling - Centralized error handling middleware
- β Logging - Structured logging with Winston
- β Security - Helmet, CORS, and security best practices
- β Production-Ready - Optimized for deployment
- Architecture
- Project Structure
- Getting Started
- API Endpoints
- Environment Variables
- Development
- Production Build
- Testing the API
- Database-Agnostic Design
- Contributing
- License
This project follows Clean Architecture principles with 4 distinct layers:
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Interfaces Layer (HTTP) β
β Controllers β Routes β Middleware β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββ¬ββ
β β
βΌ β
ββββββββββββββββββββββββββββββββββββββββββββββ β
β Application Layer (Business Logic) β
β Services β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββ
β Domain Layer (Core Business) β
β Entities β Repository Interfaces β
βββββββββββββββββββββββββββββββββββββββββββββββ
β²
β
βββββββββββββββββββββββββββββββββββββββββββββββ
β Infrastructure Layer (External I/O) β
β Database β Email β File Storage β
βββββββββββββββββββββββββββββββββββββββββββββββ
1. Domain Layer (Core)
- Contains business entities and repository interfaces
- No dependencies on other layers or external frameworks
- Pure TypeScript interfaces defining the domain model
2. Application Layer
- Contains business logic in services
- Depends only on the domain layer
- Orchestrates use cases using domain repositories
3. Interface Layer
- Contains HTTP controllers, routes, and middleware
- Depends on application and domain layers
- Translates between HTTP requests/responses and domain concepts
4. Infrastructure Layer
- Contains implementation details (database, email, etc.)
- Depends on domain interfaces
- Implements repository interfaces with concrete technology
src/
βββ config/ # Configuration files
β βββ database.ts # Database connection
β βββ logger.ts # Winston logger
β βββ env.ts # Environment variables
β
βββ domain/ # Domain Layer (Core)
β βββ entities/
β β βββ IBaseEntity.ts # Base entity interface
β β βββ IUser.ts # User entity
β βββ repositories/
β β βββ IGenericRepository.ts # Generic CRUD interface
β β βββ IUserRepository.ts # User repository interface
β βββ services/
β β βββ IUserService.ts # User service interface
β βββ shared/
β βββ ApiResponse.ts # Standard API response
β βββ AppError.ts # Custom error class
β
βββ application/ # Application Layer
β βββ request-dtos/
β β βββ IUserRequest.ts # User request DTOs
β βββ services/
β βββ UserService.ts # User business logic
β
βββ interfaces/ # Interface Layer
β βββ http/
β βββ controllers/
β β βββ UserController.ts # HTTP handlers
β βββ middlewares/
β β βββ ErrorMiddleware.ts # Error handling
β βββ routes/
β β βββ UserRoutes.ts # Route definitions
β βββ validation/
β βββ schemas/
β β βββ UserSchemas.ts # Joi validation
β βββ validator.ts # Validation middleware
β
βββ infrastructure/ # Infrastructure Layer
β βββ database/
β βββ mongodb/
β βββ models/
β β βββ IBaseDocument.ts # Base Mongoose doc
β β βββ UserModel.ts # User Mongoose model
β βββ repositories/
β βββ BaseRepository.ts # Generic repo implementation
β βββ UserRepository.ts # User repo implementation
β
βββ app.ts # Express app setup
βββ server.ts # HTTP server
βββ container.ts # Dependency injection
βββ bootstrap.ts # Application bootstrap
βββ index.ts # Entry point
- Node.js >= 20.x
- MongoDB >= 6.x (or Docker)
- npm or yarn
- Clone the repository
git clone https://github.com/yourusername/clean-architecture-nodejs.git
cd clean-architecture-nodejs- Install dependencies
npm install- Setup environment variables
cp .env.example .env
# Edit .env with your configuration- Start MongoDB (if using Docker)
docker run -d -p 27017:27017 --name mongodb mongo:latest- Run the application
# Development mode
npm run dev
# Production build
npm run build
npm startThe server will start on http://localhost:3000
POST /api/users/register
Content-Type: application/json
{
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"password": "Password@123",
"confirmPassword": "Password@123",
"phoneNumber": "+1234567890"
}GET /api/users?page=1&limit=10GET /api/users/:idPUT /api/users/:id
Content-Type: application/json
{
"firstName": "Jane",
"phoneNumber": "+9876543210"
}DELETE /api/users/:idGET /healthResponse:
{
"status": "OK",
"timestamp": "2025-01-15T10:30:00.000Z",
"uptime": 123.456
}Create a .env file in the root directory:
# Application
NODE_ENV=development
PORT=3000
# Database
MONGODB_URI=mongodb://localhost:27017/clean-architecture-db
# Logging
LOG_LEVEL=info
# JWT (for future authentication)
JWT_SECRET=your-super-secret-key-change-in-production
JWT_EXPIRES_IN=7d
# CORS
CORS_ORIGIN=*# Development with hot reload
npm run dev
# Development with tsx (faster)
npm run dev:tsx
# Build for production
npm run build
# Start production server
npm start
# Type checking
npm run compile
# Linting
npm run lint
# Format code
npm run formatThis project uses:
- ESLint for linting
- Prettier for code formatting
- TypeScript strict mode for type safety
Build the project for production:
npm run buildThis will:
- Compile TypeScript to JavaScript
- Resolve path aliases using
tsc-alias - Output to the
dist/directory
Run in production:
npm startCreate a Dockerfile:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]Build and run:
docker build -t clean-architecture-api .
docker run -p 3000:3000 --env-file .env clean-architecture-apiRegister a user:
curl -X POST http://localhost:3000/api/users/register \
-H "Content-Type: application/json" \
-d '{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"password": "Password@123",
"confirmPassword": "Password@123"
}'Get all users:
curl http://localhost:3000/api/users?page=1&limit=10Import the following collection:
{
"info": {
"name": "Clean Architecture API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Register User",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"john@example.com\",\n \"password\": \"Password@123\",\n \"confirmPassword\": \"Password@123\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:3000/api/users/register",
"protocol": "http",
"host": ["localhost"],
"port": "3000",
"path": ["api", "users", "register"]
}
}
}
]
}This architecture is completely database-agnostic. The business logic has zero knowledge of MongoDB, PostgreSQL, or any specific database.
Example: Adding PostgreSQL Support
- Install TypeORM and PostgreSQL driver:
npm install typeorm pg- Create PostgreSQL Repository:
// src/infrastructure/database/postgresql/repositories/UserRepository.ts
import { IUserRepository } from '@domain/repositories/IUserRepository';
import { Repository } from 'typeorm';
export class PostgresUserRepository implements IUserRepository {
constructor(private repository: Repository<User>) {}
async create(data: Partial<IUser>): Promise<IUser> {
const user = this.repository.create(data);
return await this.repository.save(user);
}
// ... implement all interface methods
}- Update Factory:
// src/infrastructure/database/factory.ts
export function createUserRepository(dbType: string): IUserRepository {
switch (dbType) {
case 'mongodb':
return new MongoUserRepository();
case 'postgresql':
return new PostgresUserRepository(getTypeORMRepo());
default:
throw new Error(`Unsupported database: ${dbType}`);
}
}- Update Environment:
DATABASE_TYPE=postgresql
DATABASE_URL=postgresql://user:pass@localhost:5432/dbThat's it! Your business logic remains completely unchanged.
You can use any ORM you prefer:
- Mongoose (MongoDB) - Current implementation
- TypeORM (PostgreSQL, MySQL, SQLite, etc.)
- Prisma (PostgreSQL, MySQL, SQLite, etc.)
- Sequelize (PostgreSQL, MySQL, MariaDB, etc.)
The Repository Pattern ensures your business logic stays clean regardless of your database choice.
- Clean Architecture - Separation of concerns
- Repository Pattern - Data access abstraction
- Dependency Injection - Loose coupling
- Factory Pattern - Object creation
- DTO Pattern - Data transfer objects
- Middleware Pattern - Request/response processing
- Separation of Concerns - Each layer has a single responsibility
- Dependency Inversion - High-level modules don't depend on low-level modules
- Database Independence - Easy to switch databases
- Testability - Business logic is isolated and easily testable
- Maintainability - Clear structure makes code easy to understand
- Scalability - Well-organized for growing codebases
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the project
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Clean Architecture by Robert C. Martin (Uncle Bob)
- Node.js Best Practices
- TypeScript Community
- πΌ LinkedIn: https://www.linkedin.com/in/sandesh-rathnayake-511694141
- π Facebook: https://www.facebook.com/sandesh.rathnayake
Project Link: https://github.com/sandeshR98/nodejs-clean-architecture
β If you find this project helpful, please give it a star!