Files
agent-skills/skills/next-best-practices/route-handlers.md
Jason Woltje f6bcc86881 feat: Add 5 curated skills for Mosaic Stack
New skills:
- next-best-practices: Next.js 15+ RSC, async patterns, self-hosting (vercel-labs)
- better-auth-best-practices: Official Better-Auth with Drizzle adapter (better-auth)
- verification-before-completion: Evidence-based completion claims (obra/superpowers)
- shadcn-ui: Component patterns with Tailwind v4 adaptation note (developer-kit)
- writing-skills: TDD methodology for skill authoring (obra/superpowers)

README reorganized by category with Mosaic Stack alignment section.
Total: 9 skills (4 existing + 5 new).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:17:40 -06:00

3.2 KiB

Route Handlers

Create API endpoints with route.ts files.

Basic Usage

// app/api/users/route.ts
export async function GET() {
  const users = await getUsers()
  return Response.json(users)
}

export async function POST(request: Request) {
  const body = await request.json()
  const user = await createUser(body)
  return Response.json(user, { status: 201 })
}

Supported Methods

GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS

GET Handler Conflicts with page.tsx

A route.ts and page.tsx cannot coexist in the same folder.

app/
├── api/
│   └── users/
│       └── route.ts    # /api/users
└── users/
    ├── page.tsx        # /users (page)
    └── route.ts        # Warning: Conflicts with page.tsx!

If you need both a page and an API at the same path, use different paths:

app/
├── users/
│   └── page.tsx        # /users (page)
└── api/
    └── users/
        └── route.ts    # /api/users (API)

Environment Behavior

Route handlers run in a Server Component-like environment:

  • Yes: Can use async/await
  • Yes: Can access cookies(), headers()
  • Yes: Can use Node.js APIs
  • No: Cannot use React hooks
  • No: Cannot use React DOM APIs
  • No: Cannot use browser APIs
// Bad: This won't work - no React DOM in route handlers
import { renderToString } from 'react-dom/server'

export async function GET() {
  const html = renderToString(<Component />)  // Error!
  return new Response(html)
}

Dynamic Route Handlers

// app/api/users/[id]/route.ts
export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params
  const user = await getUser(id)

  if (!user) {
    return Response.json({ error: 'Not found' }, { status: 404 })
  }

  return Response.json(user)
}

Request Helpers

export async function GET(request: Request) {
  // URL and search params
  const { searchParams } = new URL(request.url)
  const query = searchParams.get('q')

  // Headers
  const authHeader = request.headers.get('authorization')

  // Cookies (Next.js helper)
  const cookieStore = await cookies()
  const token = cookieStore.get('token')

  return Response.json({ query, token })
}

Response Helpers

// JSON response
return Response.json({ data })

// With status
return Response.json({ error: 'Not found' }, { status: 404 })

// With headers
return Response.json(data, {
  headers: {
    'Cache-Control': 'max-age=3600',
  },
})

// Redirect
return Response.redirect(new URL('/login', request.url))

// Stream
return new Response(stream, {
  headers: { 'Content-Type': 'text/event-stream' },
})

When to Use Route Handlers vs Server Actions

Use Case Route Handlers Server Actions
Form submissions No Yes
Data mutations from UI No Yes
Third-party webhooks Yes No
External API consumption Yes No
Public REST API Yes No
File uploads Both work Both work

Prefer Server Actions for mutations triggered from your UI. Use Route Handlers for external integrations and public APIs.