Intégrez l'API Claude d'Anthropic dans vos applications : messages, system prompt, streaming temps réel et tool use avec le SDK officiel TypeScript.
Installation et configuration
npm install @anthropic-ai/sdk
# TypeScript (recommandé)
npm install typescript @types/node
// anthropic.ts — Client configuré
import Anthropic from '@anthropic-ai/sdk';
// La clé API est lue depuis la variable d'environnement ANTHROPIC_API_KEY
// Ne jamais hardcoder la clé dans le code source
const client = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
// Optionnel : timeout et retry
timeout: 30_000, // 30 secondes
maxRetries: 2, // Retry automatique sur erreurs réseau
});
export default client;
.env (jamais dans le code). Pour les apps Angular, l'API doit être appelée côté serveur (Node.js/Express) — ne jamais exposer la clé côté client.
Messages et structure de réponse
import client from './anthropic.js';
// Appel simple
const message = await client.messages.create({
model: 'claude-opus-4-6',
max_tokens: 1024,
messages: [
{ role: 'user', content: 'Explique les closures JavaScript en 3 points avec des exemples.' }
]
});
// Accès au texte de la réponse
const text = message.content[0].type === 'text' ? message.content[0].text : '';
console.log(text);
// Structure complète de la réponse Message
interface Message {
id: string; // 'msg_01XFDUDYJgAACzvnptvVoYEL'
type: 'message';
role: 'assistant';
content: ContentBlock[]; // Tableau de blocs de contenu
model: string; // 'claude-opus-4-6'
stop_reason: 'end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use';
stop_sequence: string | null;
usage: {
input_tokens: number; // Tokens dans le prompt
output_tokens: number; // Tokens générés
};
}
// Content blocks typés
type ContentBlock =
| { type: 'text'; text: string }
| { type: 'tool_use'; id: string; name: string; input: Record<string, unknown> };
// Extraction sécurisée du texte
function extractText(message: Anthropic.Message): string {
return message.content
.filter(block => block.type === 'text')
.map(block => (block as Anthropic.TextBlock).text)
.join('\n');
}
System Prompt et conversations multi-tours
// Conversation multi-tours avec gestion de l'historique
class ConversationManager {
private history: Anthropic.MessageParam[] = [];
private systemPrompt: string;
constructor(systemPrompt: string) {
this.systemPrompt = systemPrompt;
}
async chat(userMessage: string): Promise<string> {
// Ajouter le message utilisateur à l'historique
this.history.push({ role: 'user', content: userMessage });
const response = await client.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 2048,
system: this.systemPrompt, // System prompt en dehors de messages[]
messages: this.history, // Historique complet à chaque requête
});
const assistantMessage = extractText(response);
// Ajouter la réponse à l'historique pour le prochain tour
this.history.push({ role: 'assistant', content: assistantMessage });
// Truncation de l'historique si trop long (fenêtre de contexte)
if (this.history.length > 20) {
// Garder les 2 premiers (contexte initial) et les 18 derniers
this.history = [...this.history.slice(0, 2), ...this.history.slice(-18)];
}
return assistantMessage;
}
clearHistory() { this.history = []; }
}
// Utilisation
const bot = new ConversationManager(`
Tu es un expert Angular qui répond en français.
Fournis toujours des exemples TypeScript avec commentaires.
Sois précis, concis, et pratique.
`);
console.log(await bot.chat('Comment créer un service Angular injectable ?'));
console.log(await bot.chat('Et comment l\´injecter dans un composant ?'));
Streaming avec SSE
Le streaming envoie les tokens au fur et à mesure, réduisant la latence perçue de 80% sur les longues réponses.
// Streaming avec SDK helper
const stream = await client.messages.stream({
model: 'claude-opus-4-6',
max_tokens: 2048,
messages: [{ role: 'user', content: 'Explique le SSR Angular en détail.' }],
});
// Écouter les tokens en temps réel
stream.on('text', (text) => {
process.stdout.write(text); // Affichage progressif
});
// Message final avec usage complet
const finalMessage = await stream.finalMessage();
console.log('\n\nUsage:', finalMessage.usage);
// Endpoint Express avec Server-Sent Events (SSE)
import express from 'express';
import client from './anthropic.js';
app.post('/api/chat/stream', async (req, res) => {
const { messages, system } = req.body;
// Configuration SSE
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('Access-Control-Allow-Origin', '*');
try {
const stream = await client.messages.stream({
model: 'claude-sonnet-4-6',
max_tokens: 2048,
system,
messages,
});
for await (const event of stream) {
if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
// Envoyer chaque token au client
res.write(`data: ${JSON.stringify({ text: event.delta.text })}\n\n`);
}
}
const final = await stream.finalMessage();
res.write(`data: ${JSON.stringify({ done: true, usage: final.usage })}\n\n`);
res.end();
} catch (error) {
res.write(`data: ${JSON.stringify({ error: 'Erreur de streaming' })}\n\n`);
res.end();
}
});
Tool Use — cycle complet
Le Tool Use (function calling) permet à Claude d'appeler des fonctions définies par le développeur. Le cycle complet requiert plusieurs échanges : Claude demande l'outil → vous l'exécutez → vous renvoyez le résultat.
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic();
// Définition des outils disponibles
const tools: Anthropic.Tool[] = [
{
name: 'get_weather',
description: 'Obtenir la météo actuelle pour une ville',
input_schema: {
type: 'object',
properties: {
city: { type: 'string', description: 'Nom de la ville' },
unit: { type: 'string', enum: ['celsius', 'fahrenheit'], description: 'Unité de température' }
},
required: ['city']
}
},
{
name: 'search_web',
description: 'Rechercher des informations sur le web',
input_schema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Requête de recherche' }
},
required: ['query']
}
}
];
// Fonctions d'implémentation
async function executeToolCall(name: string, input: Record<string, unknown>): Promise<string> {
switch (name) {
case 'get_weather':
// Appel API météo réel
const weather = await fetchWeatherApi(input.city as string);
return JSON.stringify(weather);
case 'search_web':
const results = await webSearch(input.query as string);
return JSON.stringify(results);
default:
return JSON.stringify({ error: `Outil inconnu: ${name}` });
}
}
// Cycle complet tool use
async function chatWithTools(userMessage: string): Promise<string> {
const messages: Anthropic.MessageParam[] = [
{ role: 'user', content: userMessage }
];
// Boucle : continuer tant que Claude appelle des outils
while (true) {
const response = await client.messages.create({
model: 'claude-opus-4-6',
max_tokens: 4096,
tools,
messages,
});
// Claude a terminé (pas d'appel d'outil)
if (response.stop_reason === 'end_turn') {
return extractText(response);
}
// Claude demande un ou plusieurs outils
if (response.stop_reason === 'tool_use') {
// Ajouter la réponse de Claude à l'historique
messages.push({ role: 'assistant', content: response.content });
// Exécuter TOUS les outils demandés (Claude peut en demander plusieurs)
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const block of response.content) {
if (block.type === 'tool_use') {
const result = await executeToolCall(block.name, block.input);
toolResults.push({
type: 'tool_result',
tool_use_id: block.id,
content: result,
});
}
}
// Ajouter les résultats à l'historique pour le prochain tour
messages.push({ role: 'user', content: toolResults });
}
}
}
// Usage
const answer = await chatWithTools('Quel temps fait-il à Paris aujourd\'hui ?');
console.log(answer);
Vision — analyse d'images
Claude supporte l'analyse d'images (Vision) via des blocs image dans les messages. Supporte JPG, PNG, GIF, WebP et les URLs.
import * as fs from 'fs';
// Vision avec image en base64
const imageData = fs.readFileSync('./screenshot.png').toString('base64');
const response = await client.messages.create({
model: 'claude-opus-4-6',
max_tokens: 1024,
messages: [{
role: 'user',
content: [
{
type: 'image',
source: {
type: 'base64',
media_type: 'image/png',
data: imageData,
}
},
{
type: 'text',
text: 'Analyse cette capture d\'écran d\'interface. Quels problèmes UX vois-tu ?'
}
]
}]
});
// Vision avec URL (image publique)
const responseUrl = await client.messages.create({
model: 'claude-opus-4-6',
max_tokens: 1024,
messages: [{
role: 'user',
content: [
{ type: 'image', source: { type: 'url', url: 'https://example.com/diagram.png' } },
{ type: 'text', text: 'Explique ce diagramme d\'architecture.' }
]
}]
});
Modèles et paramètres
| Modèle | Points forts | Contexte | Usage recommandé |
|---|---|---|---|
claude-opus-4-6 |
Raisonnement avancé, code complexe | 200k tokens | Analyse, architecture, debug difficile |
claude-sonnet-4-6 |
Équilibre perf/vitesse/coût | 200k tokens | Chatbots, génération de contenu |
claude-haiku-4-5-20251001 |
Ultra rapide, faible coût | 200k tokens | Classification, extraction, fort volume |
// Paramètres importants de messages.create()
await client.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 2048, // Tokens maximum à générer (obligatoire)
temperature: 0.7, // 0 = déterministe, 1 = créatif (défaut: 1)
top_p: 0.9, // Nucleus sampling (alternatif à temperature)
top_k: 40, // Limite le vocabulaire (moins courant)
stop_sequences: ['', 'FIN'], // Arrêter la génération sur ces séquences
system: 'Tu es un expert Angular.',
messages: [...],
metadata: {
user_id: 'user_123' // Traçabilité (non envoyé au modèle)
}
});
Service Angular avec Claude API
En production Angular, l'appel à l'API Claude doit passer par un backend Node.js. Voici l'architecture complète :
// === BACKEND : api.service.ts (Node.js/Express) ===
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
app.post('/api/ai/chat', async (req, res) => {
const { messages, system } = req.body;
const response = await client.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 2048,
system,
messages,
});
res.json({ text: extractText(response), usage: response.usage });
});
// === FRONTEND Angular : claude.service.ts ===
import { Injectable, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
interface ChatMessage {
role: 'user' | 'assistant';
content: string;
}
@Injectable({ providedIn: 'root' })
export class ClaudeService {
private http = inject(HttpClient);
readonly isLoading = signal(false);
readonly history = signal<ChatMessage[]>([]);
readonly error = signal<string | null>(null);
async sendMessage(userMessage: string, systemPrompt?: string): Promise<string> {
this.isLoading.set(true);
this.error.set(null);
// Ajouter le message utilisateur à l'historique
this.history.update(h => [...h, { role: 'user', content: userMessage }]);
try {
const response = await this.http.post<{ text: string }>('/api/ai/chat', {
messages: this.history(),
system: systemPrompt ?? 'Tu es un assistant Angular expert.'
}).toPromise();
const assistantText = response!.text;
// Ajouter la réponse à l'historique
this.history.update(h => [...h, { role: 'assistant', content: assistantText }]);
return assistantText;
} catch (err) {
const msg = 'Erreur de communication avec l\'assistant IA';
this.error.set(msg);
throw new Error(msg);
} finally {
this.isLoading.set(false);
}
}
clearHistory() { this.history.set([]); }
}
Gestion d'erreurs et rate limits
import Anthropic from '@anthropic-ai/sdk';
// Le SDK gère les retries automatiquement, mais voici la gestion manuelle
async function callWithRetry(fn: () => Promise<Anthropic.Message>, maxRetries = 3): Promise<Anthropic.Message> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error instanceof Anthropic.RateLimitError) {
// 429 — attendre avant de réessayer
const waitMs = Math.pow(2, attempt) * 1000; // Backoff exponentiel
console.warn(`Rate limit atteint, attente ${waitMs}ms...`);
await new Promise(resolve => setTimeout(resolve, waitMs));
} else if (error instanceof Anthropic.APIError) {
// Erreurs non-retryables
if (error.status === 401) throw new Error('Clé API invalide');
if (error.status === 400) throw new Error(`Requête invalide: ${error.message}`);
if (attempt === maxRetries) throw error;
} else {
throw error;
}
}
}
throw new Error('Nombre maximum de tentatives atteint');
}
// Gestion des types d'erreurs Anthropic
try {
const response = await client.messages.create({ ... });
} catch (error) {
if (error instanceof Anthropic.AuthenticationError) {
console.error('401: Clé API invalide ou expirée');
} else if (error instanceof Anthropic.RateLimitError) {
console.error('429: Quota dépassé — attendre ou upgrader');
} else if (error instanceof Anthropic.APIConnectionError) {
console.error('Erreur réseau — vérifier la connexion');
} else if (error instanceof Anthropic.APIError) {
console.error(`Erreur API ${error.status}: ${error.message}`);
}
}
Conclusion
L'API Claude via le SDK @anthropic-ai/sdk couvre tous les cas d'usage d'une application IA moderne. Points clés à retenir :
client.messages.create()— appel simple et conversations multi-toursmessages.stream()— streaming SSE pour l'UX temps réel- Tool Use : cycle demander → exécuter → renvoyer pour connecter Claude à des APIs externes
- Vision : blocs
image(base64 ou URL) pour l'analyse d'images - Ne jamais exposer la clé API côté client Angular — toujours via un backend Node.js
- Gérer les
RateLimitErroravec backoff exponentiel