Welcome to the final lesson of this course.
So far, you’ve built a fully functional Task Manager API:
- A clear
Taskmodel and in-memory store - A service layer handling all task CRUD
- Validation to keep data safe
- Middleware with API-key protection and logging
- A UI panel to exercise all endpoints
In this last lesson, you’ll polish the API surface itself so it feels consistent and predictable to any client:
- Introduce response helpers that standardize your JSON shape
- Refactor your
/api/tasksand/api/tasks/[id]routes to use those helpers - Add a filter endpoint and a small filtered-tasks page that follow the same response contract
You’ll still be using Codex, but now with a focus on refactoring and fine-grained behavior, rather than raw scaffolding.
Right now, your routes return JSON directly via NextResponse.json, and different handlers may shape responses slightly differently. That works, but it’s not ideal:
- Some responses might be
{ error: '...' }, others raw arrays, others objects with different keys. - Your frontend has to remember multiple formats.
- Adding metadata (like timestamps) requires repeating logic in every route.
This lesson introduces a single, predictable contract.
Success responses look like:
- A
datafield with the payload - A
metafield with atimestamp
Error responses look like:
- An
errorfield with a clear message - A
metafield with atimestamp
Once this contract is in place and wired into all handlers, every client (your own UI, tests, or external consumers) can rely on the same structure—across collection routes, item routes, and the new filter endpoint.
You’ll direct Codex through three main tasks:
- Implement response helpers in
src/lib/responses.ts. - Refactor existing routes (
/api/tasksand/api/tasks/[id]) to use those helpers and map service errors to specific HTTP status codes. - Finish and polish the filter endpoint + UI so they follow the same response shape and behavior patterns.
Your prompts should:
- Explicitly list all files Codex may change (often 1 or 2 files per prompt).
- Describe the response shapes and status code rules clearly.
- Emphasize “do not modify any other files”.
- Ask Codex to show the full updated content for each touched file.
These examples capture the consistent “face” of your API.
First, you’ll define reusable helpers in src/lib/responses.ts. The starter file already sketches the idea:
You’ll guide Codex to:
- Keep the basic structure.
- Replace the
'TODO'placeholder with the actualmessageargument. - Ensure both helpers always return an object with
{ data?, error?, meta: { timestamp } }.
After Codex’s implementation, the helpers should conceptually look like:
From here on, routes don’t call NextResponse.json directly—they call these helpers.
A focused Codex prompt might look like:
Codex, modify only .
Next, you’ll refactor the collection and item routes to use your new helpers consistently.
/api/tasks Collection Route
The starter version of src/app/api/tasks/route.ts is already partway there (shown above).
You’ll ask Codex to:
-
Replace the placeholder
createSuccessResponse([], 200)inGETwithgetAllTasks():const tasks = getAllTasks();return createSuccessResponse(tasks, 200);
-
Preserve the inline validation in
POST, but make sure all outcomes use the helpers:- Validation errors →
createErrorResponse(errors.join(' '), 400) - Successful creation →
createSuccessResponse(newTask, 201)
- Validation errors →
The logic doesn’t change; only the response formatting does.
A prompt you might send:
Codex, modify only
src/app/api/tasks/route.ts.
- In
GET, call and return the result with .
The item-level route already knows about the helpers and service layer (as shown above).
Your refactor will focus on how errors are mapped:
- If
updateTaskByIdreturns{ error: 'Task not found' }→404 - If it returns
{ error: '...' }for validation or payload issues →400 - If it returns
{ task }→200withcreateSuccessResponse(task)
For example, Codex should turn the PUT handler into something like:
PATCH follows the same rules but calls updateTaskById(id, body, true).
DELETE remains the special case: 204 with no JSON body on success, and createErrorResponse('Task not found', 404) when nothing was deleted.
The final piece is your filter-by-status feature, which ties together everything you’ve learned: service layer, helpers, validation, and UI.
The starter API route already captures the desired behavior (shown above).
You’ll use Codex to ensure:
- The
completedquery param is required. - Only
'true'or'false'are allowed values. - Invalid or missing params →
400withcreateErrorResponse. - Valid params → boolean conversion →
filterTaskByStatus→createSuccessResponse(tasks).
Now this endpoint matches the same response contract as /api/tasks and /api/tasks/[id].
A concise prompt:
Codex, modify only
src/app/api/tasks/filter/route.ts.Implement the
GEThandler so that it:
- Reads the
completedquery parameter.- If it is missing, returns
createErrorResponse("'completed' query parameter is required", 400).- If it is not
'true'or'false', returns .
The UI page completes the story by consuming the filter endpoint.
You’ll ask Codex to:
- Validate
completedParamon the client (if it’s not'true'or'false', set an error). - Call
/api/tasks/filter?completed=...when valid. - Remember that responses now come in
{ data, meta }(and possibly{ error, meta }) form, so the UI must readdata.
Conceptually, the useEffect implementation should look like:
This page becomes a neat little “view” on top of your consistent backend.
A good Codex prompt for this page:
Codex, modify only
src/app/tasks/filter/page.tsx.In the
useEffect, validatecompletedParamso that if it is not or , and clear the array. When it is valid, call and parse the JSON response.
In this final lesson, you:
- Created response helpers that standardize JSON responses with
{ data/error, meta }. - Refactored
/api/tasksand/api/tasks/[id]to use those helpers and map service errors to appropriate HTTP status codes. - Finished the filter endpoint and UI, making sure they follow the same response contract and validation patterns.
Across the entire Vibe Coding the Backend: Task Manager API course, you’ve:
- Learned to design a domain model and service layer.
- Used Codex to implement logic safely, one file at a time.
- Added validation, security, logging, and now consistent responses.
- Built a UI panel and a filtered view to fully exercise your API.
You now have a small but realistic backend that looks and behaves like something you’d be proud to ship—and you’ve practiced guiding Codex as a real engineering partner, not a magic code generator.
