The repository pattern helps organize how your app talks to data sources. It keeps data access code in one place, making your app easier to manage and change.
Repository pattern for data access in NextJS
class Repository { async getAll() { // fetch all items } async getById(id) { // fetch one item by id } async create(data) { // add new item } async update(id, data) { // update item by id } async delete(id) { // delete item by id } }
This is a simple class with methods to get, add, update, and delete data.
Each method is async because data fetching usually takes time.
class UserRepository { async getAllUsers() { return fetch('/api/users').then(res => res.json()) } }
class ProductRepository { async getProductById(id) { return fetch(`/api/products/${id}`).then(res => res.json()) } }
class TodoRepository { async createTodo(todo) { return fetch('/api/todos', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(todo) }).then(res => res.json()) } }
This React component uses a UserRepository class to get user data. It shows a list of user names after fetching them asynchronously.
The repository hides the data fetching details from the component, keeping code clean.
import React, { useEffect, useState } from 'react' class UserRepository { async getAllUsers() { // Simulate fetching data return new Promise(resolve => { setTimeout(() => { resolve([ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' } ]) }, 500) }) } } export default function UserList() { const [users, setUsers] = useState([]) const repo = new UserRepository() useEffect(() => { repo.getAllUsers().then(data => setUsers(data)) }, []) return ( <main> <h1>User List</h1> <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> </main> ) }
Keep repository methods focused on data access only, not UI logic.
Use async/await or promises to handle asynchronous data fetching.
Repositories make it easier to change data sources without changing UI code.
The repository pattern organizes data access in one place.
It helps keep UI code clean and easier to maintain.
Use it when you want reusable, testable, and flexible data handling.
Practice
Repository pattern in a Next.js application?Solution
Step 1: Understand the role of the Repository pattern
The Repository pattern centralizes data access logic, separating it from UI code.Step 2: Identify benefits in Next.js context
This separation makes code cleaner, reusable, and easier to maintain.Final Answer:
To organize data access in one place for cleaner and reusable code -> Option BQuick Check:
Repository pattern = centralized data access [OK]
- Confusing repository with UI state management
- Thinking repository handles routing
- Mixing repository with styling concerns
Solution
Step 1: Check class syntax and async usage
class UserRepository { async getAll() { return fetch('/api/users').then(res => res.json()) } } correctly defines a class with an async method returning parsed JSON from fetch.Step 2: Identify errors in other options
function UserRepository() { return fetch('/api/users') } is a function, not a class. const UserRepository = () => fetch('/api/users').json() incorrectly calls .json() directly on fetch result without awaiting. class UserRepository { getAll() { return fetch('/api/users').json() } } misses async and awaits, causing runtime error.Final Answer:
class UserRepository { async getAll() { return fetch('/api/users').then(res => res.json()) } } -> Option AQuick Check:
Async class method with fetch and json parsing = class UserRepository { async getAll() { return fetch('/api/users').then(res => res.json()) } } [OK]
- Forgetting async keyword on methods
- Calling .json() without awaiting fetch response
- Using function instead of class for repository
console.log(users) output?class UserRepository {
async getAll() {
return [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
}
}
const repo = new UserRepository()
const users = await repo.getAll()
console.log(users)Solution
Step 1: Analyze the getAll method
getAll returns a resolved array of user objects immediately (no fetch or delay).Step 2: Understand await usage
Await unwraps the Promise returned by async getAll, so users is the array itself.Final Answer:
[{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }] -> Option AQuick Check:
Await unwraps async return value = array [OK]
- Thinking users is a Promise object
- Forgetting await causes Promise logged
- Assuming undefined without return
class ProductRepository {
getAll() {
const response = fetch('/api/products')
return response.json()
}
}Solution
Step 1: Check fetch usage
fetch returns a Promise, so response is a Promise, not the actual response object.Step 2: Identify missing async/await
To get JSON data, method must be async and await fetch and response.json() calls.Final Answer:
Missing async keyword and await for fetch and json calls -> Option CQuick Check:
Async fetch requires await to get response data [OK]
- Not marking method async
- Not awaiting fetch or json()
- Assuming fetch returns data synchronously
Solution
Step 1: Understand caching in repository context
Caching should be encapsulated inside the repository class to keep data access logic together.Step 2: Evaluate options for caching implementation
class UserRepository { cache = null async getAll() { if (this.cache) return this.cache const res = await fetch('/api/users') this.cache = await res.json() return this.cache } } uses a class property to store cache and returns it if available, avoiding repeated fetch calls.Step 3: Compare other options
Options A and B do not implement caching. const cache = [] async function getAllUsers() { if (cache.length) return cache const res = await fetch('/api/users') cache.push(...await res.json()) return cache } uses an external cache variable, breaking encapsulation.Final Answer:
class UserRepository { cache = null async getAll() { if (this.cache) return this.cache const res = await fetch('/api/users') this.cache = await res.json() return this.cache } } -> Option DQuick Check:
Repository with internal cache property = class UserRepository { cache = null async getAll() { if (this.cache) return this.cache const res = await fetch('/api/users') this.cache = await res.json() return this.cache } } [OK]
- Caching outside repository breaks encapsulation
- Not checking cache before fetching
- Using functions instead of class for caching
Ready to prove it?
Pass the test and this pattern moves unknown
