API do Claude com TypeScript e Node.js: guia prático
Este tutorial mostra como usar a API do Claude em TypeScript e Node.js com o SDK oficial da Anthropic. Os exemplos usam o Claude Opus 5 (claude-opus-5) e funcionam com a API da open33 trocando só a URL base e a chave.
Instalação
npm install @anthropic-ai/sdk
Configuração
O SDK lê ANTHROPIC_BASE_URL e ANTHROPIC_API_KEY do ambiente:
export ANTHROPIC_BASE_URL="https://api.open33.tech"
export ANTHROPIC_API_KEY="SUA_CHAVE"
Se preferir deixar a URL explícita no código, passe baseURL ao criar o cliente e mantenha a chave em variável de ambiente:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
baseURL: "https://api.open33.tech",
apiKey: process.env.ANTHROPIC_API_KEY,
});
A primeira chamada
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const resposta = await client.messages.create({
model: "claude-opus-5",
max_tokens: 16000,
messages: [{ role: "user", content: "Crie um schema Zod para um cadastro com nome, e-mail e CPF." }],
});
for (const bloco of resposta.content) {
if (bloco.type === "text") console.log(bloco.text);
}
console.log(resposta.usage);
resposta.content é uma lista de blocos de tipos diferentes. O TypeScript obriga a checar bloco.type antes de ler bloco.text, e isso é bom: o Claude Opus 5 pode devolver outros blocos antes do texto.
Conversa com histórico
A API não guarda estado. Envie o histórico inteiro a cada chamada, usando os tipos do próprio SDK:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const historico: Anthropic.MessageParam[] = [];
async function perguntar(texto: string): Promise<string> {
historico.push({ role: "user", content: texto });
const resposta = await client.messages.create({
model: "claude-opus-5",
max_tokens: 16000,
system: "Você é um assistente de programação. Responda em português.",
messages: historico,
});
historico.push({ role: "assistant", content: resposta.content });
return resposta.content
.filter((b): b is Anthropic.TextBlock => b.type === "text")
.map((b) => b.text)
.join("");
}
console.log(await perguntar("Estou criando uma API em Express."));
console.log(await perguntar("Como adiciono validação de entrada nela?"));
Streaming em uma rota de API
Para mostrar a resposta enquanto ela é gerada, use client.messages.stream. Um exemplo de rota que envia o texto aos pedaços para o navegador (Node 18+, com qualquer framework que aceite um Response de streaming):
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
export async function POST(req: Request) {
const { pergunta } = await req.json();
const stream = client.messages.stream({
model: "claude-opus-5",
max_tokens: 64000,
messages: [{ role: "user", content: pergunta }],
});
const encoder = new TextEncoder();
const corpo = new ReadableStream({
async start(controller) {
for await (const evento of stream) {
if (evento.type === "content_block_delta" && evento.delta.type === "text_delta") {
controller.enqueue(encoder.encode(evento.delta.text));
}
}
controller.close();
},
});
return new Response(corpo, { headers: { "content-type": "text/plain; charset=utf-8" } });
}
A chave fica no servidor. Nunca chame a API direto do navegador com a sua chave: qualquer visitante conseguiria copiá-la. Mais detalhes no guia de streaming.
Tratando erros
O SDK repete sozinho, por padrão duas vezes, as falhas de conexão, os 429 e os 5xx. Para o resto, use as classes de erro do SDK:
import Anthropic from "@anthropic-ai/sdk";
try {
await client.messages.create({
model: "claude-opus-5",
max_tokens: 16000,
messages: [{ role: "user", content: "Olá!" }],
});
} catch (err) {
if (err instanceof Anthropic.AuthenticationError) {
console.error("Chave inválida: confira ANTHROPIC_API_KEY.");
} else if (err instanceof Anthropic.NotFoundError) {
console.error("Endpoint ou modelo não encontrado: confira a URL base e o modelo.");
} else if (err instanceof Anthropic.RateLimitError) {
console.error("Limite atingido (429): aguarde a próxima janela do plano.");
} else if (err instanceof Anthropic.APIConnectionError) {
console.error("Falha de rede: confira sua conexão.");
} else if (err instanceof Anthropic.APIError) {
console.error(`Erro da API (${err.status}): ${err.message}`);
} else {
throw err;
}
}
Próximos passos
- Receber a resposta em JSON, com o tipo vindo do Zod: respostas em JSON.
- Deixar o Claude chamar funções do seu código: tool use.
- Trazer um código que usa o SDK da OpenAI: migrar da OpenAI para o Claude.
Perguntas frequentes
Funciona com JavaScript puro?
Sim. O pacote @anthropic-ai/sdk funciona em JavaScript e TypeScript. Os exemplos em TypeScript funcionam em JavaScript sem as anotações de tipo.
Posso usar no navegador?
Não com a sua chave. Faça as chamadas no servidor (Node, uma rota de API ou uma função serverless) e envie só a resposta para o navegador.
Qual a diferença entre messages.create e messages.stream?
create espera a resposta inteira e devolve tudo de uma vez. stream entrega a resposta aos pedaços, enquanto ela é gerada, e é o indicado para respostas longas e para interfaces de chat.
Qual o timeout padrão do SDK?
Dez minutos por requisição. No SDK de TypeScript, o valor é informado em milissegundos na opção timeout.