Guides

How to Protect API Routes in Next.js

An API route is reachable directly, regardless of what the frontend hides — so every route needs to independently validate the caller's identity and permissions, not assume a request only ever comes from your own authenticated frontend.

Prerequisites

  • arrow_rightA Next.js project with authentication already set up
  • arrow_rightRoute Handlers (app/api/*/route.ts) to secure

Step 1: Validate the Session in the Route Handler

app/api/orders/route.ts

export async function GET(request: Request) {
  const session = await getSession(request);
  if (!session) {
    return new Response('Unauthorized', { status: 401 });
  }
  // proceed with authorized logic
}

Step 2: Check Authorization, Not Just Authentication

Being logged in isn't the same as being allowed to perform a specific action — check that the session's user actually has permission for the requested resource (e.g. this order belongs to this user) before returning or modifying data.

Step 3: Validate Input and Add Rate Limiting

Validate the request body against a schema (Zod is a common choice) before using it, and add rate limiting on any endpoint that's expensive or could be abused (login attempts, AI API calls, email sending).

Common Errors

  • arrow_rightAssuming middleware-level auth checks cover API routes too — middleware matchers need to explicitly include API paths, or each route needs its own check
  • arrow_rightTrusting a user ID sent in the request body instead of deriving it from the validated session
  • arrow_rightNo rate limiting on expensive operations, leaving them open to abuse or runaway cost

Security Considerations

  • arrow_rightNever trust client-supplied identifiers for authorization decisions — always derive identity from the verified session
  • arrow_rightReturn generic error messages for auth failures — don't reveal whether a user ID exists or not
  • arrow_rightLog authentication failures for monitoring, without logging sensitive payload data

Frequently Asked Questions

Is middleware enough to protect API routes?add

Only if its matcher explicitly covers the API paths — many middleware configs are written with pages in mind and accidentally exclude /api routes, leaving them unchecked.

How do I add rate limiting?add

Track request counts per user or IP over a time window, either with a lightweight in-memory approach for small scale or a dedicated service (like Upstash's rate limiter) for anything running across multiple serverless instances.

Should error messages reveal what went wrong?add

For authentication and authorization failures, keep messages generic — detailed error messages can leak information useful to an attacker, like whether a given user account exists.

BUILDING SOMETHING SIMILAR?

If you'd rather have this built and shipped than build it yourself, I'm available for freelance and contract work.