FULL_STACK_DEVELOPER_2026_COMPLETE - Phase 05 Build the In-Memory Todo API
VOLUME 02 - FRONTEND + BACKEND FOUNDATIONS
Build the In-Memory Todo API
Turn HTTP semantics into observable GET, POST, PATCH and DELETE behavior
HTTP is the contract between independent programs.
REQUEST
HTTP Client TaskFlow API
PowerShell / browser / later Next.js NestJS + Fastify
RESPONSE
method + target + headers + optional body
status + headers + optional body
Figure - HTTP request/response exchange. Phase 05 studies the protocol before Phase 06 introduces a browser cross-origin call.
Book 5 of Phase 05
FULL_STACK_DEVELOPER_2026_COMPLETE - Phase 05 Build the In-Memory Todo API
Build a temporary in-memory Todo API
We now need a concrete protocol surface to exercise. The data deliberately lives in memory so a server restart resets it. That
limitation is not a bug in this phase - it isolates HTTP behavior from database persistence. Volume 03 will solve persistence
properly.
PowerShell Client
Invoke-RestMethod
Fastify
receives HTTP request
TodosController
method + route + HTTP boundary
TodosService
temporary in-memory behavior
HTTP Response
status + JSON / no body
Figure - The Phase 05 Todo API is intentionally in-memory. PostgreSQL and Prisma arrive later; this phase isolates HTTP semantics.
Step 1 - Generate the Todo feature
From: taskflow\apps\api
pnpm exec nest generate module todos
pnpm exec nest generate controller todos --no-spec
pnpm exec nest generate service todos --no-spec
Step 2 - Give the service temporary state
apps/api/src/todos/todos.service.ts - first read operations
FULL_STACK_DEVELOPER_2026_COMPLETE - Phase 05 Build the In-Memory Todo API
import { Injectable, NotFoundException } from '@nestjs/common';
interface Todo {
id: number;
title: string;
completed: boolean;
}
@Injectable()
export class TodosService {
private nextId = 3;
private readonly todos: Todo[] = [
{ id: 1, title: 'Learn HTTP', completed: false },
{ id: 2, title: 'Build the API contract', completed: false },
];
findAll() {
return this.todos;
}
findOne(id: number) {
const todo = this.todos.find((item) => item.id === id);
if (!todo) throw new NotFoundException('Todo not found');
return todo;
}
}
The service owns temporary application state. The controller should not own an array just because it receives HTTP requests.
Later, Prisma replaces this in-memory persistence without changing the conceptual controller responsibility.
Step 3 - Add read routes
apps/api/src/todos/todos.controller.ts - read routes
@Controller('todos')
export class TodosController {
constructor(private readonly todosService: TodosService) {}
@Get()
findAll() {
return this.todosService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.todosService.findOne(Number(id));
}
}
We convert the route parameter to a number for this protocol exercise. Formal validation and typed DTO boundaries are
deliberately deferred. The book should make that technical debt visible rather than pretend raw input is trustworthy.
Step 4 - Add creation
todos.service.ts - temporary creation behavior
create(title: string) {
const todo = {
id: this.nextId++,
title,
completed: false,
};
this.todos.push(todo);
return todo;
}
todos.controller.ts - POST /todos
FULL_STACK_DEVELOPER_2026_COMPLETE - Phase 05 Build the In-Memory Todo API
@Post()
create(@Body() body: { title?: string }) {
if (!body.title?.trim()) {
throw new BadRequestException('title is required');
}
return this.todosService.create(body.title.trim());
}
Nest defaults successful POST handlers to 201 Created. We use only a tiny manual guard here so the protocol example is
usable; this is not the validation architecture taught later.
Step 5 - Add partial update
todos.service.ts - patch behavior
update(id: number, changes: { title?: string; completed?: boolean }) {
const todo = this.findOne(id);
if (changes.title !== undefined) todo.title = changes.title;
if (changes.completed !== undefined) todo.completed = changes.completed;
return todo;
}
todos.controller.ts - PATCH /todos/:id
@Patch(':id')
update(
@Param('id') id: string,
@Body() body: { title?: string; completed?: boolean },
) {
return this.todosService.update(Number(id), body);
}
Step 6 - Add deletion with 204
todos.service.ts - removal behavior
remove(id: number) {
const todo = this.findOne(id);
const index = this.todos.indexOf(todo);
this.todos.splice(index, 1);
}
todos.controller.ts - DELETE /todos/:id
@Delete(':id')
@HttpCode(204)
remove(@Param('id') id: string) {
this.todosService.remove(Number(id));
}
A 204 response intentionally has no content. Returning a JSON success object would conflict with the meaning we are
teaching. Nest allows explicit status selection through @HttpCode(204).
Temporary limitation
The array disappears when the server stops. That is useful right now because the learner can focus on request/response
semantics. Do not add Prisma early to "fix" it. Persistence is a separate architectural problem and gets its own phase.
Official source notes
Primary protocol reference: RFC 9110 - HTTP Semantics (RFC Editor). Supplemental developer reference: MDN HTTP Reference and
HTTP Request Methods. Nest-specific routing and status behavior reference: NestJS Controllers documentation.
FULL_STACK_DEVELOPER_2026_COMPLETE - Phase 05 Build the In-Memory Todo API
This phase teaches HTTP semantics independent of a specific transport version. HTTP/2 and HTTP/3 change framing and transport details,
while request methods, target resources, headers, status codes and representation semantics remain the important application-level concepts
for this course.