Securing Your API in Next.js π
Exposing API routes in Next.js is easy. Securing them takes intention. Without safeguards, your API is vulnerable to abuse, scraping, and unauthorized data access.
The Problem I Faced
I noticed my public API was receiving thousands of requests from a single IP, causing my database costs to spike. I realized I had no protection against bots or spam.
Understanding the Layers of Defense
- Authentication: Who is this user?
- Authorization: Are they allowed to do this?
- Rate Limiting: Are they doing this too much?
Code Example 1: Server-Side Authentication Check
Using a helper function to guard API routes.
ts
// lib/auth.ts
import { auth } from "@clerk/nextjs";
export function requireAuth() {
const { userId } = auth();
if (!userId) {
throw new Error("Unauthorized");
}
return userId;
}Code Example 2: Implementing Rate Limiting (Upstash/Redis)
ts
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { NextResponse } from "next/server";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"), // 10 requests per 10s
});
export async function POST(req: Request) {
const ip = req.headers.get("x-forwarded-for") ?? "127.0.0.1";
const { success } = await ratelimit.limit(ip);
if (!success) {
return NextResponse.json({ error: "Too many requests" }, { status: 429 });
}
// Proceed with logic...
}Common Pitfalls
- Mistake 1: Trusting client-side logic for security.
- Don't: Hide a "Delete" button and assume the user won't call the API directly.
- Do: Check permissions inside the API route every single time.
Pro Tips
- Service Accounts: If you have other services calling your API, use a secret API Key header strategy distinct from user sessions.
- Sanitization: Always sanitize inputs (Zod does this well) to prevent injection attacks.
Wrapping Up
Security is layers. Rate limiting stops the bots, and strict auth checks stop the hackers.
Tech Stack: Next.js, Upstash Redis, Clerk
