Welcome to the first lesson of our course on building a Task Manager API with Next.js!
Unlike earlier courses, where we built projects incrementally — starting with all logic in the API routes, then introducing validation, and finally extracting logic into a service layer — this project reflects a more realistic and production-ready approach from the beginning. Since you've already practiced each of these patterns individually, this time we'll integrate them immediately and treat the project structure as if you're working on a real-world team project.
Think of this course as a "capstone" for your previous lessons. We'll skip re-explaining the basics like how API routes work or how to validate inputs from scratch — instead, we'll immediately apply those patterns to build a clean, modular backend. If you ever feel lost, you can always revisit earlier modules where we broke each piece down in more detail.
Before we dive into the service layer, let’s quickly review the basic setup for our project. Here’s a summary of how we define a task
and where we store our data:
- Task Type: This defines what a task looks like. Each task has an
id
,title
,content
,completed
status, and an optionaldueDate
. - In-Memory Data Store: We use a simple array called
tasks
to keep track of all our tasks. This is just for learning — real apps use databases, but this keeps things simple for now.
Our files are organized so that all task-related logic lives in the src/lib
folder, and our API routes will use this logic.
A service layer is a part of your backend code that handles the main logic for your application. Think of it as the “brain” that knows how to create, read, update, and delete tasks. The service layer sits between your data (the tasks
array) and your API routes.
Why use a service layer?
- Separation of concerns: Keeps your code organized by separating business logic from request handling.
- Reusability: You can use the same logic in different parts of your app.
- Easier to test and maintain: If you need to change how tasks are managed, you only update the service layer.
In our project, the service layer will be a set of functions that work with the tasks
array.
Let’s build the main functions for our service layer. These functions will help us manage tasks in our in-memory store.
Let’s break down what each function does:
- getAllTasks: Returns the full list of tasks.
- createTask: Adds a new task to the list. It automatically assigns a unique
id
and setscompleted
tofalse
. - getTaskById: Finds a task by its
id
. Returnsnull
if not found. - updateTaskById: Updates a task’s details. It can do a full update or a partial update (only the fields you provide).
Now that you have a service layer, your API routes can use these functions to handle requests. This keeps your route code simple and focused on handling HTTP requests and responses.
Example: Using the service layer in an API route
Here, the API route does not need to know how tasks are stored or managed. It just calls the service layer functions and returns the result. For example, the POST
route validates the input and then calls the createTask
service function. The input validation uses const errors: string[] = []
to collect multiple validation issues before returning a response. While we've covered this pattern in earlier courses, it's worth noting that this allows us to report all issues at once, instead of stopping at the first error.
So far, we’ve used the src/app/api/tasks/route.ts
file to list all tasks or create new ones. But when you want to work with an individual task — such as reading, updating, or deleting it — you use dynamic API routes like src/app/api/tasks/[id]/route.ts
.
Each function here (GET, PUT, PATCH, DELETE) calls the relevant service function. Most notably, updateTaskById
is reused for both PUT and PATCH, and we control the behavior using the partial
flag.
PATCH vs PUT
This lets us reuse the same internal logic for both types of update operations, which keeps our service layer clean and DRY.
** GET and DELETE **
The GET handler fetches a single task by ID. If the task doesn't exist, it returns a 404
using createErrorResponse
.
The DELETE handler removes a task and returns a 204 No Content
if successful — otherwise, it also returns a 404
.
In this lesson, you learned:
- What a
service layer
is and why it’s important for organizing backend code. - How to define and use service functions to manage tasks in your project.
- How API routes can use the service layer to keep code clean and easy to maintain.
You now have a solid foundation for your Task Manager API. In the next set of exercises, you’ll get hands-on practice using and extending these service functions. This will help you become comfortable with the service layer pattern and prepare you for building more advanced features.
