The Anthropic JavaScript SDK makes it straightforward to integrate Claude into Node.js, React, and Next.js applications. This guide covers everything from basic setup to production patterns.


Installation and Setup

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

const client = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

Never expose your API key in frontend code. Use environment variables and server-side calls only.


Basic Message

const message = await client.messages.create({
  model: 'claude-opus-4-7',
  max_tokens: 1024,
  messages: [
    { role: 'user', content: 'Explain promises in JavaScript in 3 sentences.' }
  ],
});

console.log(message.content[0].text);

System Prompts

const message = await client.messages.create({
  model: 'claude-opus-4-7',
  max_tokens: 2048,
  system: 'You are a helpful JavaScript tutor. Explain concepts clearly with code examples. Always include runnable examples.',
  messages: [
    { role: 'user', content: 'How do async/await work?' }
  ],
});

Streaming Responses

For better UX, stream responses as they generate:

const stream = await client.messages.create({
  model: 'claude-opus-4-7',
  max_tokens: 1024,
  stream: true,
  messages: [{ role: 'user', content: 'Write a short story.' }],
});

for await (const event of stream) {
  if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
    process.stdout.write(event.delta.text);
  }
}

Streaming with the Helper

const stream = client.messages.stream({
  model: 'claude-opus-4-7',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'Explain React hooks.' }],
});

stream.on('text', (text) => {
  process.stdout.write(text);
});

const finalMessage = await stream.finalMessage();
console.log('\n\nStop reason:', finalMessage.stop_reason);

Multi-Turn Conversations

class ConversationManager {
  constructor() {
    this.client = new Anthropic();
    this.messages = [];
  }

  async chat(userMessage) {
    this.messages.push({ role: 'user', content: userMessage });

    const response = await this.client.messages.create({
      model: 'claude-opus-4-7',
      max_tokens: 1024,
      system: 'You are a helpful assistant.',
      messages: this.messages,
    });

    const assistantMessage = response.content[0].text;
    this.messages.push({ role: 'assistant', content: assistantMessage });
    
    return assistantMessage;
  }

  reset() {
    this.messages = [];
  }
}

const conversation = new ConversationManager();
const reply1 = await conversation.chat('My name is Alice.');
const reply2 = await conversation.chat('What is my name?'); // Remembers Alice

Tool Use (Function Calling)

const tools = [
  {
    name: 'get_weather',
    description: 'Get the current weather for a location',
    input_schema: {
      type: 'object',
      properties: {
        location: {
          type: 'string',
          description: 'City and country, e.g. "London, UK"',
        },
        unit: {
          type: 'string',
          enum: ['celsius', 'fahrenheit'],
          description: 'Temperature unit',
        },
      },
      required: ['location'],
    },
  },
];

async function getWeather(location, unit = 'celsius') {
  // Your real weather API call here
  return { temperature: 22, condition: 'sunny', unit };
}

async function chatWithTools(userMessage) {
  const messages = [{ role: 'user', content: userMessage }];

  while (true) {
    const response = await client.messages.create({
      model: 'claude-opus-4-7',
      max_tokens: 1024,
      tools,
      messages,
    });

    if (response.stop_reason === 'tool_use') {
      const toolUse = response.content.find(b => b.type === 'tool_use');
      const toolResult = await getWeather(
        toolUse.input.location,
        toolUse.input.unit
      );

      messages.push({ role: 'assistant', content: response.content });
      messages.push({
        role: 'user',
        content: [{
          type: 'tool_result',
          tool_use_id: toolUse.id,
          content: JSON.stringify(toolResult),
        }],
      });
    } else {
      return response.content[0].text;
    }
  }
}

const result = await chatWithTools("What's the weather in Tokyo?");

Vision: Image Analysis

import fs from 'fs';

// From file
const imageData = fs.readFileSync('screenshot.png').toString('base64');

const response = await client.messages.create({
  model: 'claude-opus-4-7',
  max_tokens: 1024,
  messages: [{
    role: 'user',
    content: [
      {
        type: 'image',
        source: {
          type: 'base64',
          media_type: 'image/png',
          data: imageData,
        },
      },
      {
        type: 'text',
        text: 'Describe what you see in this image.',
      },
    ],
  }],
});

// From URL
const responseFromUrl = await client.messages.create({
  model: 'claude-opus-4-7',
  max_tokens: 1024,
  messages: [{
    role: 'user',
    content: [
      {
        type: 'image',
        source: {
          type: 'url',
          url: 'https://example.com/image.jpg',
        },
      },
      { type: 'text', text: 'What does this image show?' },
    ],
  }],
});

Next.js API Route

// app/api/chat/route.ts
import Anthropic from '@anthropic-ai/sdk';
import { NextRequest } from 'next/server';

const client = new Anthropic();

export async function POST(req: NextRequest) {
  const { messages, systemPrompt } = await req.json();

  const encoder = new TextEncoder();
  const stream = new ReadableStream({
    async start(controller) {
      const anthropicStream = client.messages.stream({
        model: 'claude-opus-4-7',
        max_tokens: 2048,
        system: systemPrompt || 'You are a helpful assistant.',
        messages,
      });

      for await (const event of anthropicStream) {
        if (
          event.type === 'content_block_delta' &&
          event.delta.type === 'text_delta'
        ) {
          controller.enqueue(encoder.encode(event.delta.text));
        }
      }
      controller.close();
    },
  });

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/plain; charset=utf-8',
      'Transfer-Encoding': 'chunked',
    },
  });
}
// app/components/ChatInterface.tsx
'use client';
import { useState } from 'react';

export function ChatInterface() {
  const [messages, setMessages] = useState<{ role: string; content: string }[]>([]);
  const [input, setInput] = useState('');
  const [streaming, setStreaming] = useState('');

  async function sendMessage() {
    const newMessages = [...messages, { role: 'user', content: input }];
    setMessages(newMessages);
    setInput('');
    setStreaming('');

    const response = await fetch('/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ messages: newMessages }),
    });

    const reader = response.body!.getReader();
    const decoder = new TextDecoder();
    let full = '';

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      const chunk = decoder.decode(value);
      full += chunk;
      setStreaming(full);
    }

    setMessages([...newMessages, { role: 'assistant', content: full }]);
    setStreaming('');
  }

  return (
    <div>
      {messages.map((m, i) => (
        <div key={i}><strong>{m.role}:</strong> {m.content}</div>
      ))}
      {streaming && <div><strong>assistant:</strong> {streaming}</div>}
      <input value={input} onChange={e => setInput(e.target.value)} />
      <button onClick={sendMessage}>Send</button>
    </div>
  );
}

Error Handling

import Anthropic, { APIError } from '@anthropic-ai/sdk';

try {
  const message = await client.messages.create({ /* ... */ });
} catch (error) {
  if (error instanceof Anthropic.APIError) {
    console.error('Status:', error.status);
    console.error('Message:', error.message);

    if (error.status === 429) {
      // Rate limited — implement exponential backoff
      await new Promise(resolve => setTimeout(resolve, 60000));
    } else if (error.status === 529) {
      // Overloaded — retry
    } else if (error.status === 401) {
      // Auth error — check API key
    }
  }
}

Prompt Caching

For repeated context (system prompts, documents), enable caching to save costs:

const response = await client.messages.create({
  model: 'claude-opus-4-7',
  max_tokens: 1024,
  system: [
    {
      type: 'text',
      text: largeSystemPrompt, // Your large repeated context
      cache_control: { type: 'ephemeral' }, // Cache this block
    },
  ],
  messages: [{ role: 'user', content: userQuestion }],
});

Cached tokens cost 10% of base price on subsequent calls. Works best when the cached content is >1024 tokens and reused frequently.


TypeScript Types

import Anthropic from '@anthropic-ai/sdk';
import type {
  Message,
  MessageParam,
  TextBlock,
  ToolUseBlock,
  ContentBlock,
} from '@anthropic-ai/sdk/resources/messages';

function extractText(message: Message): string {
  const textBlock = message.content.find(
    (block): block is TextBlock => block.type === 'text'
  );
  return textBlock?.text ?? '';
}