Welcome back. In the previous lesson, you used Codex to design the Task core: a shared Task model, an in-memory tasks array, and a service layer that knows how to create, read, update, and delete tasks. Your API can now do real work—but so far it mostly assumes that incoming JSON is correct.
In this lesson, you’ll upgrade your Task Manager API from “accept anything and hope” to “trust but verify.” You’ll:
- Build a central task payload validator in
src/lib/validateTaskPayload.ts - Plug that validator into
updateTaskByIdinsrc/lib/services/taskService.ts - Wire validation into PUT and PATCH for
src/app/api/tasks/[id]/route.ts, returning proper HTTP status codes
You’ll continue to rely on Codex for implementation, but now your prompts will describe more nuanced rules: allowed fields, required fields, strict types, and how to propagate errors back to the client.
So far, your API can:
- Store tasks in memory
- Expose
/api/tasksand/api/tasks/[id]routes - Perform basic CRUD via the service layer
What it does not do yet is enforce the rules of your domain. Examples:
- A client could send
"completed": "yes"instead of a boolean. - A task update could include random fields like
"hack": "oops". - A PUT could omit required fields entirely.
Without validation, bad data sneaks into your in-memory store and everything built on top becomes fragile.
This lesson introduces a clear separation of concerns:
validateTaskPayloadchecks if incoming data is shaped correctly.taskService.updateTaskByIddecides whether to mutate state based on validator results.- The
/api/tasks/[id]route translates service results into HTTP responses:200,400,404,204.
Once this pipeline is in place, every future feature you add (logging, persistence, UI) can assume the data is clean.
You’ll use Codex in three focused ways:
-
To implement the reusable validator
- One file:
src/lib/validateTaskPayload.ts - Well-defined function:
validateTaskPayload(payload: any, partial = false): string[] - Return array of human-readable error messages
- One file:
-
To integrate validation into the service layer
- One function:
updateTaskByIdinsrc/lib/services/taskService.ts - Call
validateTaskPayloadand decide whether to update or return an error object
- One function:
-
To enforce validation in PUT/PATCH routes
- File:
src/app/api/tasks/[id]/route.ts - Handlers use
updateTaskByIdand map{ error } / { task }to HTTP status codes
- File:
Across all three, your prompts should:
- Restrict Codex to one file at a time
- Describe allowed fields, required fields, and type expectations clearly
- Explain how to treat partial updates vs full replacements
- Ask Codex to show the full updated file content
Your first job is to give the backend a single place that knows what a valid task payload looks like. The starter file already tells Codex what to do, but you’ll turn that into a concrete implementation:
Conceptually, validateTaskPayload should:
- Ensure the payload is a non-null object (no arrays, no primitives)
- Allow only
title,content,completed, anddueDate - When
partialisfalse(full update), requiretitle,content, andcompleted - Enforce types:
title/content→ stringscompleted→ boolean
With the validator in place, you’ll make sure the service layer refuses invalid updates before they touch the in-memory tasks array.
The starter updateTaskById already hints at what should happen (as shown in src/lib/services/taskService.ts).
The core behavior you want is:
- Find the task by
id. - If not found, return
{ error: 'Task not found' }. - Validate the incoming data with
validateTaskPayload(data, partial). - If there are errors, join them into a single string (e.g.
errors.join('; ')) and return{ error: combined }. - Do not mutate the task when validation fails.
- Apply the update only when validation passes:
- For partial updates (
partial = true): merge only the provided fields. - For full updates (
partial = false): treat the payload as a replacement fortitle,content,completed, anddueDate.
- For partial updates (
- Return the updated task as .
The last step is to wire everything into the item-level route so HTTP callers see the right status codes and messages.
You’ll instruct Codex to:
- Parse
idfromcontext.params.idas a number - Use
getTaskByIdto implement GET with200/404 - Use
updateTaskByIdto implement PUT and PATCH with validation-aware behavior- PUT uses
partial = false(full replacement) - PATCH uses
partial = true(partial update)
- PUT uses
- Map service results to HTTP codes:
- If
{ error: 'Task not found' }→404 - If
{ error: '...' }for validation issues →400 - If
{ task }→200with the updated task
- If
- Use
deleteTaskByIdfor :
In this lesson, you:
- Created a central task validator that enforces shape and type rules for
title,content,completed, anddueDate. - Hooked
validateTaskPayloadintoupdateTaskByIdso invalid updates never reach your in-memory store. - Translated service-level results into clear HTTP responses in PUT and PATCH (and fully coded GET/DELETE) for
/api/tasks/[id].
You now have an API that not only performs CRUD, but also defends its own data and reports errors clearly.
In upcoming lessons, you’ll continue refining your Task Manager API—evolving response patterns, improving error consistency, and preparing the backend for more realistic production-style scenarios, all while guiding Codex with increasingly precise prompts.
