C.W.K.
Stream
Lesson 05 of 06 · published

Next.js + Claude: Auth, Edge, and Server Components

~16 min · next-js, edge, server-components, rsc

Level 0Observer
0 XP0/64 lessons0/13 achievements
0/150 XP to next level150 XP to go0% complete

Where the SDK call lives

In Next.js App Router, the SDK call belongs in a Server Component, a Server Action, or a Route Handler — never a Client Component. The API key never reaches the browser. cwk-site treats app/api/*/route.ts as the canonical home for SDK calls.

Edge vs Node runtime

Edge runtime is faster cold-starts and runs on Vercel's edge network. It restricts to web-standard APIs (fetch, ReadableStream, no Node fs). The SDK works on Edge for streaming and non-streaming Messages calls. Some helpers (file uploads via Files API) require Node runtime. Pick Edge by default; fall back to Node when an SDK feature requires it.

Caching at the right layer

Combine three layers: Next.js fetch cache for static prompts, prompt caching at the Anthropic API for long stable prefixes, and your own response cache for repeated identical user inputs. Each layer handles a different failure mode; do not collapse them into one.

Principle: The SDK call is server-side, period. If you find yourself importing it in a 'use client' component, stop and move the call.

Code

Server Action with prompt caching·typescript
// app/actions/summarize.ts
'use server';

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();
const SYSTEM_PERSONA = await fetch(new URL('./persona.md', import.meta.url)).then(r => r.text());

export async function summarize(text: string) {
  const resp = await client.messages.create({
    model: 'claude-sonnet-4-6',
    max_tokens: 512,
    system: [
      {
        type: 'text',
        text: SYSTEM_PERSONA,
        cache_control: { type: 'ephemeral' },
      },
    ],
    messages: [{ role: 'user', content: text }],
  });
  const block = resp.content.find(b => b.type === 'text');
  return block && block.type === 'text' ? block.text : '';
}
Edge runtime streaming Route Handler·typescript
// app/api/chat/route.ts
import Anthropic from '@anthropic-ai/sdk';

export const runtime = 'edge';
export const dynamic = 'force-dynamic';

const client = new Anthropic();

export async function POST(req: Request) {
  const { messages } = (await req.json()) as { messages: Anthropic.MessageParam[] };

  const stream = await client.messages.stream({
    model: 'claude-sonnet-4-6',
    max_tokens: 1024,
    messages,
    signal: req.signal,
  });

  return new Response(stream.toReadableStream(), {
    headers: { 'content-type': 'text/event-stream' },
  });
}

External links

Exercise

Move one Anthropic SDK call from a Client Component (where you smuggled it via a hook) into a Server Action or Route Handler. Confirm the API key is no longer reachable from the browser bundle.
Hint
Search your built .next/static for ANTHROPIC_API_KEY — it should not be there.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.