From harness-claude
Create typed HTTP endpoints in Next.js App Router using route.ts files with named method exports. For REST APIs, webhooks (Stripe/GitHub with signature verification), streaming, file downloads, and external integrations.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Create HTTP endpoints in the App Router using route.ts with typed method exports
Builds Next.js App Router API routes with Route Handlers for HTTP methods, request bodies (JSON/form/text), dynamic params, queries, headers, and JSON responses.
Build fully-typed server API endpoints in Nuxt using Nitro's event-handler model and H3 utilities. Covers file-based routing, request/query/body handling, errors, and responses.
Designs production-ready Next.js App Router API routes with auth guards, Zod validation, typed responses, and error handling for RESTful endpoints and schemas.
Share bugs, ideas, or general feedback.
Create HTTP endpoints in the App Router using route.ts with typed method exports
app/api/[resource]/route.ts and export named async functions GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS.(request: NextRequest) as the first parameter — use request.json(), request.text(), or request.formData() to parse the body.Response or NextResponse — use NextResponse.json(data, { status }) for JSON responses.({ params }: { params: { id: string } }).request.text() to read the raw body (needed for HMAC verification), then parse JSON manually.runtime export to 'edge' for global low-latency endpoints; omit it (defaults to Node.js) when you need Node.js APIs.export const dynamic = 'force-dynamic' if a GET handler should never be cached (e.g., it reads cookies or returns live data).// app/api/posts/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
const updateSchema = z.object({ title: z.string().min(1) });
export async function GET(_request: NextRequest, { params }: { params: { id: string } }) {
const post = await db.post.findUnique({ where: { id: params.id } });
if (!post) return NextResponse.json({ error: 'Not found' }, { status: 404 });
return NextResponse.json(post);
}
export async function PATCH(request: NextRequest, { params }: { params: { id: string } }) {
const body = await request.json();
const parsed = updateSchema.safeParse(body);
if (!parsed.success) {
return NextResponse.json({ errors: parsed.error.flatten() }, { status: 422 });
}
const post = await db.post.update({ where: { id: params.id }, data: parsed.data });
return NextResponse.json(post);
}
Route Handlers replace pages/api/ routes in the App Router. They live at app/api/**/route.ts (or any nested location in app/ that does not conflict with a page.tsx). A route.ts file and a page.tsx file cannot coexist in the same directory.
Caching behavior: GET Route Handlers are statically cached by default when they do not read dynamic data (cookies(), headers(), request body). Use export const dynamic = 'force-dynamic' or read from request to opt out of caching.
Webhook pattern: Stripe and GitHub require reading the raw request body for HMAC signature verification. Read with await request.text(), verify the signature, then JSON.parse() the body manually. Using request.json() first consumes the stream and makes raw body unavailable.
Streaming responses: Return a ReadableStream in the Response body for streaming endpoints. This pattern is common for AI streaming responses (OpenAI, Anthropic SDK).
CORS: Route Handlers need explicit CORS headers for cross-origin requests. Handle OPTIONS requests and set Access-Control-Allow-* headers in both the OPTIONS handler and the main method handlers.
tRPC integration: tRPC's Next.js adapter mounts a single Route Handler at app/api/trpc/[trpc]/route.ts and delegates all procedure calls through it.
https://nextjs.org/docs/app/building-your-application/routing/route-handlers