Episode 2 — React Frontend Architecture NextJS / 2.20 — Building APIs with NextJS

2.20 — Exercise Questions: Building APIs with Next.js

Build these in a small App Router project (or sketch requests with curl / REST Client first).


<< 2.20 Overview


A. File conventions (2.20.a)

  1. Create GET /api/ping returning { "data": { "pong": true } }.

  2. Add app/api/items/[id]/route.ts supporting GET by id. Return 404 with your standard error JSON when missing.

  3. Explain what happens if a client calls POST /api/ping when you only exported GET.


B. HTTP verbs (2.20.b)

  1. Implement POST /api/items — validates { "name": string } with Zod, returns 201 + Location header.

  2. Implement PUT /api/items/[id] full replace vs PATCH partial update — document the behavioral difference in comments.

  3. Implement DELETE /api/items/[id] returning 204 on success.


C. Database (2.20.c)

  1. Wire GET /api/items to your database through a listItems service function (no raw queries in route.ts).

  2. Add a unique index on email and map duplicate insert to 409 with code EMAIL_IN_USE.


D. Errors & consistency (2.20.d)

  1. Introduce a shared jsonError(code, message, status, details?) helper and use it in three different handlers.

  2. Add X-Request-Id in middleware (or inside handlers) and include it in every JSON meta block.


E. Cross-cutting

  1. Write curl examples for GET list (with query), POST create, and DELETE.

  2. Compare your Route Handler for “create comment” with a Server Action version (2.21) — one paragraph on when you would keep only the Route Handler.


F. Stretch

  1. Implement OPTIONS + CORS headers for a trusted origin calling your API from the browser.

  2. Load-test locally (e.g. autocannon) and observe DB connection behavior — write three bullet takeaways.


Answers & guidance (self-check)

1. GET /api/ping

  • app/api/ping/route.ts exporting GET returning NextResponse.json({ data: { pong: true } }).

2. GET by id + 404 JSON

  • Service getItem(id) returns nullNextResponse.json({ error: { code: 'NOT_FOUND', message: 'Item not found' } }, { status: 404 }).

3. POST with only GET

  • Next returns 405 Method Not Allowed (default) because no POST export exists for that segment.

4. POST create

  • Validate body with Zod; on success return NextResponse.json(entity, { status: 201, headers: { Location: /api/items/${entity.id} } }).

5. PUT vs PATCH

  • Document: PUT replaces entire record (missing fields cleared or rejected); PATCH merges provided fields only.

6. DELETE 204

  • return new NextResponse(null, { status: 204 })no JSON body on 204.

7. Service layer

  • route.ts should be thin: parse request → call listItems → map to DTO → return response. Keeps tests fast and DB logic reusable from Server Actions too.

8. Unique email → 409

  • Catch DB unique violation (Prisma P2002 etc.) and map to { error: { code: 'EMAIL_IN_USE' } } with 409.

9. jsonError helper

  • Centralizes shape { error: { code, message, details? } }; ensures you never leak stack traces to clients.

10. Request id

  • Generate UUID per request; set response header; include in meta.requestId for correlating client reports with server logs.

11. curl examples (sketch)

  • curl -sS 'http://localhost:3000/api/items?page=2'
  • curl -sS -X POST http://localhost:3000/api/items -H 'Content-Type: application/json' -d '{"name":"Tea"}' -i
  • curl -sS -X DELETE http://localhost:3000/api/items/123 -i

12. Route Handler only

  • Keep when you need public HTTP for mobile/partners, webhooks, OpenAPI docs, unusual streaming/status semantics, or cross-origin clients not using Next’s action protocol.

13. CORS + OPTIONS

  • export function OPTIONS() returns NextResponse with Access-Control-Allow-Origin: https://trusted.example (not * if cookies), allow methods/headers you need; mirror on actual responses or use middleware helper.

14. Load test takeaways (examples)

  • Connection pool exhaustion under concurrency; need for PgBouncer/Accelerate; p95 latency spikes when sequential DB in hot handlers.

<< 2.20 Overview