The service layer pattern helps organize your code by separating business logic from request handling. It makes your app easier to read and maintain.
Service layer pattern in Express
class ServiceName { async methodName(params) { // business logic here } } module.exports = new ServiceName();
The service class contains methods with your app's main logic.
Route handlers call these service methods to keep routes simple.
class UserService { async getUserById(id) { // fetch user from database } } module.exports = new UserService();
class OrderService { async createOrder(orderData) { // validate and save order } } module.exports = new OrderService();
This example shows a simple Express app using a service layer. The UserService holds user data and logic to find a user by ID. The route handler calls the service method to get the user and sends the response. This keeps the route clean and focused on HTTP details.
const express = require('express'); const app = express(); app.use(express.json()); // Service layer class UserService { constructor() { this.users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]; } async getUserById(id) { return this.users.find(user => user.id === id) || null; } } const userService = new UserService(); // Route handler app.get('/users/:id', async (req, res) => { const id = Number(req.params.id); const user = await userService.getUserById(id); if (user) { res.json(user); } else { res.status(404).json({ message: 'User not found' }); } }); app.listen(3000, () => console.log('Server running on http://localhost:3000'));
Keep service methods focused on business logic only.
Services can be reused in different routes or parts of your app.
Testing services separately is easier than testing routes with logic mixed in.
The service layer pattern separates business logic from route handling.
It helps keep your code organized, clean, and easier to maintain.
Use services to reuse logic and simplify testing.
Practice
service layer pattern in an Express application?Solution
Step 1: Understand the role of route handlers
Route handlers in Express mainly handle HTTP requests and responses, not business logic.Step 2: Identify the service layer role
The service layer contains business logic, keeping route handlers clean and focused on HTTP concerns.Final Answer:
To separate business logic from route handling for cleaner code -> Option CQuick Check:
Service layer = separates logic [OK]
- Confusing service layer with route handlers
- Thinking service layer manages HTTP directly
- Assuming service layer only handles database connections
Solution
Step 1: Understand service function usage
Service functions are called to get results, which are then sent in the response.Step 2: Check syntax correctness
const result = serviceFunction(); res.send(result); calls the function and sends its result correctly. res.send(serviceFunction); sends the function itself, not its result. serviceFunction(res); wrongly passes response object to service. res.send(await serviceFunction); misses parentheses and awaits incorrectly.Final Answer:
const result = serviceFunction(); res.send(result); -> Option AQuick Check:
Call service, then send result [OK]
- Sending the function instead of its result
- Passing Express response object to service
- Incorrect use of async/await syntax
/user/42?
const userService = {
getUserById: (id) => ({ id, name: `User${id}` })
};
app.get('/user/:id', (req, res) => {
const user = userService.getUserById(req.params.id);
res.json(user);
});Solution
Step 1: Analyze service function output
The service returns an object with id as string and name as 'User' plus id string.Step 2: Check route response
The route sends JSON of the user object, so id remains a string "42" and name is "User42".Final Answer:
{"id":"42","name":"User42"} -> Option DQuick Check:
Service returns object with string id [OK]
- Assuming id is a number instead of string
- Expecting an error from correct code
- Ignoring string concatenation in name
app.get('/items', (req, res) => {
const items = itemService.getAllItems;
res.json(items);
});Solution
Step 1: Check function call syntax
The code assigns the function itself to items without calling it (missing parentheses).Step 2: Understand impact on response
res.json(items) sends the function reference, not the data, causing wrong output or error.Final Answer:
Missing parentheses when calling getAllItems function -> Option BQuick Check:
Call functions with () to get results [OK]
- Forgetting parentheses on function calls
- Thinking res.json can't send arrays
- Assuming route path syntax is wrong
// Service function
async function getUserById(id) {
if (!id) throw new Error('ID required');
// simulate async DB call
return { id, name: `User${id}` };
}
// Route handler
app.get('/user/:id', async (req, res) => {
try {
const user = await getUserById(req.params.id);
res.json(user);
} catch (err) {
res.status(400).json({ error: err.message });
}
});Solution
Step 1: Check separation of concerns
The service function only throws errors or returns data, no response handling.Step 2: Verify error handling location
The route handler catches errors and sends HTTP responses, keeping concerns separate.Final Answer:
Correctly separates logic and handles errors in route -> Option AQuick Check:
Service throws errors; route handles responses [OK]
- Putting response logic inside service functions
- Ignoring try/catch for async errors
- Handling errors only in service without route awareness
Ready to prove it?
Pass the test and this pattern moves unknown
