Welcome back, API Engineer! In the previous lesson, we created a simple UsersController that returned hardcoded user data. While this works for a small example, controllers shouldn’t handle logic like data storage or retrieval. Their job is to respond to requests, not to “know” how to fetch users. That’s where the service layer comes in.
The service layer is responsible for the business logic of your application. It interacts with data sources (like databases or APIs), applies rules, and returns processed data to the controller. By moving logic to services, we keep controllers lean and make our code modular, testable, and easy to extend.
Here’s what we currently have in users.controller.ts:
This is fine for a small demo, but as we add features like “find a user by ID,” this logic will get messy. Time to delegate these responsibilities to a UsersService.
Let’s build the service layer to handle user data and business logic.
First, create a new file called users.service.ts and add the following code:
Explanation:
@Injectable()marks this class as a service that NestJS can manage. It tells NestJS that this class can be injected as a dependency.- The
usersarray holds the user data. findAll()returns the full list of users.findOne(id: number)looks for a user by their ID. If the user is not found, it throws aNotFoundException. This is a built-in NestJS exception that will return a 404 error to the client.
Example Output:
- Calling
findAll()returns: - Calling
findOne(1)returns: - Calling
findOne(99)throws an error:
Now, let’s update the controller to use the service instead of handling data directly.
Here’s the updated users.controller.ts:
New Concepts Introduced
@Param('id'): Extracts theidvalue from the routeGET /users/:id. For example, calling/users/2will setid = '2'.+id:Converts the string parameter into a number (because URL parameters are always strings).- Dynamic
:idroutes allow us to fetch resources individually (e.g.,/users/1for Alice). - The controller now receives an instance of
UsersServicethrough its constructor. This is called dependency injection. - When a request comes in, the controller calls the appropriate method on the service.
Testing Our Endpoints:
GET /users/all→ Returns all users.GET /users/1→ Returns .
We’ve added buttons to fetch both /users/all and /users/:id:
You are encouraged to test the same routes directly in your browser or with curl. The static HTML preview is just a helper.
- User calls
GET /users/1. - NestJS finds
UsersController.findOne()due to@Get(':id'). @Param('id')extracts the ID from the URL.findOne(+id)callsUsersService.findOne()to look for a match in the mock array.- The result (or 404 error) is sent back to the client.
In this lesson, you:
- Created the
UsersServiceto handle data and logic. - Updated
UsersControllerto delegate to the service. - Learned how to use
@Param()and dynamic:idroutes. - Tested your endpoints with both
/users/alland/users/:id.
Next, we’ll explore services in more depth — including adding new users and connecting to a mock database. Keep pushing forward — you’re building real backend skills here! 🚀
