The repository pattern helps organize how your app talks to data sources. It keeps data access code separate and easy to manage.
Repository pattern for data access in Express
class Repository { constructor(model) { this.model = model; } async getAll() { return await this.model.find(); } async getById(id) { return await this.model.findById(id); } async create(data) { return await this.model.create(data); } async update(id, data) { return await this.model.findByIdAndUpdate(id, data, { new: true }); } async delete(id) { return await this.model.findByIdAndDelete(id); } }
This example uses a class to wrap data access methods.
The model is usually a database model like a Mongoose model.
class UserRepository extends Repository { async findByEmail(email) { return await this.model.findOne({ email }); } }
const userRepository = new UserRepository(UserModel);
const users = await userRepository.getAll();This Express app uses the repository pattern to manage user data. The UserRepository class handles all database calls. Routes call repository methods to get or create users.
This keeps the route code clean and focused on handling requests.
import express from 'express'; import mongoose from 'mongoose'; // Define a simple User schema const userSchema = new mongoose.Schema({ name: String, email: String }); const UserModel = mongoose.model('User', userSchema); // Repository class class Repository { constructor(model) { this.model = model; } async getAll() { return await this.model.find(); } async getById(id) { return await this.model.findById(id); } async create(data) { return await this.model.create(data); } async update(id, data) { return await this.model.findByIdAndUpdate(id, data, { new: true }); } async delete(id) { return await this.model.findByIdAndDelete(id); } } // UserRepository extends Repository class UserRepository extends Repository { async findByEmail(email) { return await this.model.findOne({ email }); } } const app = express(); app.use(express.json()); const userRepository = new UserRepository(UserModel); // Connect to MongoDB (replace with your connection string) mongoose.connect('mongodb://localhost:27017/testdb', { useNewUrlParser: true, useUnifiedTopology: true }); // Routes app.get('/users', async (req, res) => { const users = await userRepository.getAll(); res.json(users); }); app.post('/users', async (req, res) => { const user = await userRepository.create(req.body); res.status(201).json(user); }); app.get('/users/email/:email', async (req, res) => { const user = await userRepository.findByEmail(req.params.email); if (user) res.json(user); else res.status(404).send('User not found'); }); app.listen(3000, () => { console.log('Server running on http://localhost:3000'); });
Always handle errors in real apps to avoid crashes.
The repository pattern helps if you change your database later; just update the repository code.
Use async/await to keep code easy to read and avoid callback hell.
The repository pattern separates data access from app logic.
It makes your code cleaner and easier to maintain.
You can add custom data methods by extending the base repository.
Practice
Repository pattern in an Express app?Solution
Step 1: Understand the role of repository pattern
The repository pattern is designed to isolate data access logic from the rest of the application.Step 2: Identify the correct purpose in Express context
In Express apps, it helps keep business logic clean by moving database queries into repositories.Final Answer:
To separate data access logic from business logic -> Option DQuick Check:
Repository pattern = separate data access [OK]
- Confusing repository with routing or middleware
- Thinking it handles UI or styling
- Mixing it with authentication logic
Solution
Step 1: Identify ES6 class syntax
ES6 classes use theclasskeyword with methods inside without thefunctionkeyword.Step 2: Match the correct syntax
class UserRepository { getAll() { /* code */ } } correctly defines a class with a methodgetAll()using ES6 syntax.Final Answer:
class UserRepository { getAll() { /* code */ } } -> Option AQuick Check:
ES6 class syntax = class UserRepository { getAll() { /* code */ } } [OK]
- Using function keyword inside class methods
- Confusing arrow functions with class methods
- Using old object syntax instead of class
console.log(users) output?class UserRepository {
constructor() {
this.users = [{id:1, name:'Anna'}, {id:2, name:'Bob'}];
}
getAll() {
return this.users;
}
}
const repo = new UserRepository();
const users = repo.getAll();
console.log(users);Solution
Step 1: Understand the constructor initialization
The constructor setsthis.usersto an array of two user objects.Step 2: Check the getAll method return value
ThegetAll()method returnsthis.users, so calling it returns the array.Final Answer:
[{id:1, name:'Anna'}, {id:2, name:'Bob'}] -> Option AQuick Check:
getAll returns users array [OK]
- Assuming getAll returns undefined or empty
- Confusing method call with property access
- Expecting an error due to missing binding
class ProductRepository {
constructor() {
this.products = [];
}
add(product) {
this.products.push(product);
}
getAll() {
return products;
}
}Solution
Step 1: Check variable usage in getAll method
The method returnsproductsbut this variable is not defined in the method scope.Step 2: Correct usage of instance variable
It should returnthis.productsto access the class property.Final Answer:
Using undefined variable 'products' instead of 'this.products' -> Option BQuick Check:
Use this.products to access class property [OK]
- Forgetting 'this.' prefix for class properties
- Assuming variables are global inside class methods
- Ignoring constructor initialization
findByEmail(email) for users. Which approach correctly implements this in Express?Solution
Step 1: Understand class inheritance in ES6
Extending a base class usesextendskeyword andsuper()if constructor is needed.Step 2: Check method implementation accessing instance data
class UserRepository extends BaseRepository { findByEmail(email) { return this.data.find(u => u.email === email); } } correctly extendsBaseRepositoryand usesthis.datato find user by email.Final Answer:
class UserRepository extends BaseRepository { findByEmail(email) { return this.data.find(u => u.email === email); } } -> Option CQuick Check:
Extend base class and add method using this.data [OK]
- Not using 'extends' for inheritance
- Accessing base class data incorrectly
- Overwriting prototype without proper inheritance
Ready to prove it?
Pass the test and this pattern moves unknown
