Skip to main content
Use this guide as your blueprint for building API routes under src/app/api/*. It covers input validation with Zod, optional authentication guards with Supabase, consistent responses, security, and testing. For a concrete, fully implemented example, see Contact Form API.
To decide between protecting at the edge (middleware) vs inside the route (server guard), see Routing & Middleware. In general, use route-level guards for most APIs, and reserve middleware for global page gating or cross‑cutting concerns.

Route structure

Each endpoint lives in its own folder with a route.ts file:
src/app/api/contact/route.ts
Prefer one responsibility per route. Split complex features into multiple endpoints with clear names.

Validation with Zod

  1. Define a schema near the top of your file:
src/app/api/example/route.ts
  1. Parse and handle validation errors consistently:
src/app/api/contact/route.ts
The error shape above matches the Contact endpoint, keeping your API responses predictable across routes.

Handling non-JSON payloads

Some integrations (Stripe Checkout, webhooks, file uploads) submit data as application/x-www-form-urlencoded or multipart/form-data. Read those bodies with request.formData() before passing values into your schema:
src/app/api/checkout_sessions/route.ts
When you only need a few fields from formData, pull them out explicitly (as above) and run them through the same Zod schema. This keeps validation identical regardless of transport format.

Optional: Authentication guard (Supabase)

For private endpoints, use the server client to read the session and return 401 when missing:
src/app/api/private/route.ts
Avoid leaking sensitive details in error messages. Return generic messages for 401/403.

Consistent responses

  • Success (200/201): { success: true, ... }
  • Validation error (400): { success: false, message: "Validation failed", errors: [...] }
  • Unauthorized (401/403): { success: false, message: "Unauthorized" }
  • Server error (500): { success: false, message: "An error occurred while processing your request" }
Keep the top-level fields stable so clients can handle errors uniformly.

Security checklist

  • Accept only needed methods; return 405 for others.
  • Validate and sanitize all inputs (Zod).
  • Do not log secrets or PII. Redact when necessary.
  • Use auth guards for protected resources.
  • Avoid open redirects and untrusted origins.
src/app/api/contact/route.ts

Testing locally

Use Contact Form API as a reference implementation for validation, error handling, and method handling.