Claude API Anthropic Node.js Streaming Tool Use Prompt Caching

Claude AI API in Node.js: Streaming, Tool Use & Prompt Caching

Anthropic's Claude is one of the most capable LLMs available today, with a 200K token context window, strong instruction following, and unique features like prompt caching and extended thinking. In this guide I'll show how to integrate Claude into a Node.js backend with streaming responses, tool use (function calling), and prompt caching — the three features that matter most in production.

1. Install the Anthropic SDK

npm install @anthropic-ai/sdk

Set your API key in .env:

ANTHROPIC_API_KEY=sk-ant-...

2. Basic Message

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

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

const message = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [
    { role: "user", content: "Explain REST vs GraphQL in 3 sentences." }
  ],
});

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

3. Streaming Responses

Streaming is essential for chat UIs — users see text appear immediately rather than waiting for the full response. Claude streams using Server-Sent Events (SSE).

import express from "express";
const app = express();
app.use(express.json());

app.post("/chat", async (req, res) => {
  const { message } = req.body;

  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");

  const stream = client.messages.stream({
    model: "claude-sonnet-4-6",
    max_tokens: 2048,
    system: "You are a helpful assistant.",
    messages: [{ role: "user", content: message }],
  });

  stream.on("text", (text) => {
    res.write(`data: ${JSON.stringify({ text })}\n\n`);
  });

  stream.on("message", () => {
    res.write("data: [DONE]\n\n");
    res.end();
  });

  stream.on("error", (err) => {
    res.write(`data: ${JSON.stringify({ error: err.message })}\n\n`);
    res.end();
  });
});

Frontend consumption: Use the browser's EventSource API or fetch with ReadableStream to consume the SSE stream and append text tokens to the UI in real time.

4. Tool Use (Function Calling)

Tool use lets Claude call your own functions — database lookups, API calls, calculations — and incorporate the results into its response. This is how you build reliable AI features that operate on live data.

const tools = [
  {
    name: "get_order_status",
    description: "Get the current status of a customer order by order ID.",
    input_schema: {
      type: "object",
      properties: {
        order_id: {
          type: "string",
          description: "The order ID to look up, e.g. ORD-12345"
        }
      },
      required: ["order_id"]
    }
  }
];

async function getOrderStatus(orderId) {
  // your real database call here
  return { orderId, status: "shipped", estimatedDelivery: "2026-06-14" };
}

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

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

    if (response.stop_reason === "end_turn") {
      return response.content.find(b => b.type === "text")?.text;
    }

    if (response.stop_reason === "tool_use") {
      messages.push({ role: "assistant", content: response.content });

      const toolResults = [];
      for (const block of response.content) {
        if (block.type === "tool_use") {
          let result;
          if (block.name === "get_order_status") {
            result = await getOrderStatus(block.input.order_id);
          }
          toolResults.push({
            type: "tool_result",
            tool_use_id: block.id,
            content: JSON.stringify(result),
          });
        }
      }

      messages.push({ role: "user", content: toolResults });
    }
  }
}

// Usage
const answer = await chatWithTools("Where is my order ORD-98765?");
console.log(answer);
// "Your order ORD-98765 has been shipped and is estimated to arrive on June 14, 2026."

5. Prompt Caching

Prompt caching is Claude's most impactful cost-saving feature. When you mark content with cache_control, Anthropic caches that prefix for 5 minutes. Subsequent requests that hit the cache are 90% cheaper and up to 85% faster for the cached portion.

Cost impact: A 10,000-token system prompt costs $0.03 uncached per request. With caching at 100 req/min, that's $1.80/min → $2,592/day. With cache hits, it drops to ~$0.003/min → ~$4.32/day.

const response = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  system: [
    {
      type: "text",
      text: `You are an expert customer support agent for Acme Corp.

      ## Product Catalog
      ${largeProductCatalogText}  // could be 50,000+ tokens

      ## Policies
      ${detailedPoliciesText}`,
      cache_control: { type: "ephemeral" }  // cache this prefix
    }
  ],
  messages: [
    { role: "user", content: userQuestion }  // only this changes per request
  ],
});

Caching rules: Cache breakpoints must be stable across requests. The cached prefix is everything up to and including the cache_control block. Only the content after the breakpoint is processed fresh.

6. Multi-turn Conversation with History

// In-memory store — use Redis in production
const sessions = new Map();

app.post("/chat/:sessionId", async (req, res) => {
  const { sessionId } = req.params;
  const { message } = req.body;

  const history = sessions.get(sessionId) || [];
  history.push({ role: "user", content: message });

  const response = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 2048,
    system: [{ type: "text", text: "You are a helpful assistant.", cache_control: { type: "ephemeral" } }],
    messages: history,
  });

  const assistantMessage = response.content[0].text;
  history.push({ role: "assistant", content: assistantMessage });

  // Keep last 20 turns to avoid context overflow
  if (history.length > 40) history.splice(0, 2);
  sessions.set(sessionId, history);

  res.json({ reply: assistantMessage });
});

Production Best Practices

Conclusion

Streaming, tool use, and prompt caching are the three features that distinguish a toy Claude integration from a production-grade one. Streaming keeps UX responsive. Tool use grounds Claude in your real data. Prompt caching cuts API costs by orders of magnitude at scale. Together they let you build AI features that are fast, accurate, and economically viable.

Building an AI-powered product on Claude or OpenAI? Get in touch or hire me on Upwork.