DEV Community

Diego Liascovich
Diego Liascovich

Posted on

Implementing the Repository Pattern with Mongoose in Node.js

By Diego Liascovich

Full-Stack Developer | Microservices | Angular | Node.js

The Repository Pattern serves as an intermediary between your application's business logic and the persistence layer (like MongoDB, PostgreSQL, etc.). This abstraction makes your application:

  • Decoupled from the data source
  • Easier to test (you can mock the repository)
  • More maintainable and flexible

🧾 Real Case: User Management with MongoDB and Mongoose

We will use TypeScript with Node.js and Mongoose to implement the pattern.

πŸ“ Project Structure

src/
β”œβ”€β”€ domain/
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   └── user.entity.ts
β”‚   └── repositories/
β”‚       └── user.repository.interface.ts
β”œβ”€β”€ infrastructure/
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   └── user.model.ts
β”‚   └── repositories/
β”‚       └── user.repository.ts
β”œβ”€β”€ application/
β”‚   └── services/
β”‚       └── user.service.ts
└── main.ts
Enter fullscreen mode Exit fullscreen mode

1. user.entity.ts – Domain Entity

export interface User {
  id: string;
  name: string;
  email: string;
  createdAt: Date;
}
Enter fullscreen mode Exit fullscreen mode

2. user.repository.interface.ts – Repository Interface

import { User } from '../models/user.entity';

export interface IUserRepository {
  findById(id: string): Promise<User | null>;
  findAll(): Promise<User[]>;
  create(user: Omit<User, 'id' | 'createdAt'>): Promise<User>;
  delete(id: string): Promise<boolean>;
}
Enter fullscreen mode Exit fullscreen mode

3. user.model.ts – Mongoose Schema and Model

import mongoose, { Schema } from 'mongoose';

const UserSchema = new Schema({
  name: String,
  email: String,
  createdAt: { type: Date, default: Date.now }
});

export const UserModel = mongoose.model('User', UserSchema);
Enter fullscreen mode Exit fullscreen mode

4. user.repository.ts – Mongoose Implementation

import { IUserRepository } from '../../domain/repositories/user.repository.interface';
import { User } from '../../domain/models/user.entity';
import { UserModel } from '../models/user.model';

export class UserRepository implements IUserRepository {
  async findById(id: string): Promise<User | null> {
    return await UserModel.findById(id).lean();
  }

  async findAll(): Promise<User[]> {
    return await UserModel.find().lean();
  }

  async create(data: Omit<User, 'id' | 'createdAt'>): Promise<User> {
    const created = new UserModel(data);
    const saved = await created.save();
    return saved.toObject();
  }

  async delete(id: string): Promise<boolean> {
    const result = await UserModel.findByIdAndDelete(id);
    return !!result;
  }
}
Enter fullscreen mode Exit fullscreen mode

5. user.service.ts – Business Logic

import { IUserRepository } from '../../domain/repositories/user.repository.interface';
import { User } from '../../domain/models/user.entity';

export class UserService {
  constructor(private readonly userRepo: IUserRepository) {}

  async listUsers(): Promise<User[]> {
    return await this.userRepo.findAll();
  }

  async registerUser(name: string, email: string): Promise<User> {
    return await this.userRepo.create({ name, email });
  }
}
Enter fullscreen mode Exit fullscreen mode

6. main.ts – Example Usage

import mongoose from 'mongoose';
import { UserRepository } from './infrastructure/repositories/user.repository';
import { UserService } from './application/services/user.service';

(async () => {
  await mongoose.connect('mongodb://localhost:27017/repository-example');

  const userRepo = new UserRepository();
  const userService = new UserService(userRepo);

  const newUser = await userService.registerUser('Padie78', 'Padie78@example.com');
  console.log('User created:', newUser);

  const allUsers = await userService.listUsers();
  console.log('All users:', allUsers);
})();
Enter fullscreen mode Exit fullscreen mode

βœ… Benefits of This Architecture

  • Decoupling: You can easily switch the database or mock the repository.
  • Unit Testing: You can mock IUserRepository in your tests.
  • Flexibility: Your business logic is not tied to any specific DB implementation.

Top comments (0)