CodingNeed.

2 · Data and boundaries · 30 MIN

Route handlers and validated input

An endpoint owns its input and output contract.

A route handler receives the Web Request API and returns a Response. Parse JSON inside a try block because malformed input is an expected client failure. Zod checks the runtime shape instead of relying on a TypeScript assertion. The example previews a title and does not persist anything, so it deliberately does not pretend to be an authenticated create endpoint. Before adding writes, enforce session ownership, same-origin protection where relevant, rate limits and bounded body reads on the server.

Verify trimming and both length boundaries.

Read the example

// app/api/preview/route.ts — install zod in the example project
import {z} from 'zod';
const Input=z.object({title:z.string().trim().min(3).max(80)}).strict();
export async function POST(request:Request) {
  let raw:unknown;
  try {raw=await request.json()} catch {return Response.json({error:'Invalid JSON'},{status:400})}
  const parsed=Input.safeParse(raw);
  if(!parsed.success)return Response.json({error:'Invalid title'},{status:400});
  return Response.json({preview:parsed.data.title},{headers:{'Cache-Control':'no-store'}});
}
Check the expected output
POST {"title":" React "} returns {"preview":"React"}; malformed JSON or a short title returns 400.

Your challenge

Add a streaming request-body byte limit and tests for malformed JSON, unknown fields and oversized input before adapting this preview to a write endpoint.

Solution cost: O(b) parsing for a bounded b-byte body. time · O(b) parsed request data. space

Common trap

A TypeScript cast does not validate JSON; Content-Length alone is not a reliable body limit.

Study the project implementation
// app/api/preview/route.ts — install zod in the example project
import {z} from 'zod';
const Input=z.object({title:z.string().trim().min(3).max(80)}).strict();
export async function POST(request:Request) {
  let raw:unknown;
  try {raw=await request.json()} catch {return Response.json({error:'Invalid JSON'},{status:400})}
  const parsed=Input.safeParse(raw);
  if(!parsed.success)return Response.json({error:'Invalid title'},{status:400});
  return Response.json({preview:parsed.data.title},{headers:{'Cache-Control':'no-store'}});
}

Further reading: Official documentation

Next lesson: Loading states and recoverable errors

Essential cookies keep your account signed in. Optional analytics is not configured on this site. Your choice does not affect access to lessons.

Read the Privacy Policy. You can change this choice in the footer.