Skip to main content
Watch: Claude Code Just Killed After Effects

Auth & Data

Clerk Auth Setup (Next.js)

Use when adding Clerk authentication to a Next.js App Router app, protecting routes behind sign-in, or reading the signed-in user in server code. Also use when auth() throws in a route handler, a protected route lets an unauthenticated request through, or the provider and middleware disagree about which routes are public. If a request needs to know who the user is, use this skill.

clerkauthnextjsmiddleware

1 file

Description

Use when adding Clerk authentication to a Next.js App Router app, protecting routes behind sign-in, or reading the signed-in user in server code. Also use when auth() throws in a route handler, a protected route lets an unauthenticated request through, or the provider and middleware disagree about which routes are public. If a request needs to know who the user is, use this skill.

Install
SKILL.md

name: clerk-auth-setup description: Use when adding Clerk authentication to a Next.js App Router app, protecting routes, or debugging why auth() throws in a route handler. Covers provider, middleware, and safe server-side user reads.

Clerk Auth Setup (Next.js App Router)

When to trigger

Adding sign in, gating a route or API behind auth, or fixing a 500 that traces back to auth() in a server context.

Setup steps

  1. Install: pnpm add @clerk/nextjs.

  2. Put keys in .env.local only, never in code:

    NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...
    CLERK_SECRET_KEY=sk_...
    
  3. Wrap the root layout:

    import { ClerkProvider } from "@clerk/nextjs";
    export default function RootLayout({ children }) {
      return <ClerkProvider><html><body>{children}</body></html></ClerkProvider>;
    }
    
  4. Add middleware.ts using clerkMiddleware() (not the retired authMiddleware):

    import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
    const isProtected = createRouteMatcher(["/dashboard(.*)", "/api/private(.*)"]);
    export default clerkMiddleware(async (auth, req) => {
      if (isProtected(req)) await auth.protect();
    });
    export const config = { matcher: ["/((?!_next|.*\\..*).*)", "/(api|trpc)(.*)"] };
    

Reading the user server-side

auth() from @clerk/nextjs/server is async: const { userId } = await auth();.

Pitfalls

  • If middleware skips clerkMiddleware() for some environments (for example dev, when keys are domain-locked to production), auth() throws instead of returning null. Wrap server reads in a helper that returns null when Clerk is not active, and call that helper from API routes.
  • auth.protect() in middleware redirects browser requests but returns 404 for API routes. For APIs, read userId and return your own 401.
  • Do not call auth() in a client component. Use useAuth() / useUser() there.
AI Development Stack

Get Smarter About AI Dev

New tutorials, open-source projects, and deep dives on coding agents - delivered weekly.

One email per weekReal code, not theoryFree forever