fkdev UIv2.4.1

Uygulama · Claude API

Sitene yapay zekâ.

Bu rehber Anthropic’in Claude modellerini kendi sitene güvenle bağlamayı anlatır: anahtar sunucuda kalır, tarayıcı yalnızca kendi API’ni çağırır.

01Mimari: anahtar asla tarayıcıda değil#

Tarayıcıfk-chat arayüzü
fetch /api/chat
Senin sunucunPHP · Node · .env anahtar
HTTPS + x-api-key
Claude APIapi.anthropic.com

Altın kuralAPI anahtarını HTML’e, JavaScript dosyasına ya da GitHub’a koyma. Tarayıcıya giden her şey herkese açıktır; anahtarını bulan biri senin hesabından harcama yapar.

  1. Anthropic Console’dan bir API anahtarı oluştur.
  2. Anahtarı sunucuda ortam değişkeni olarak sakla: ANTHROPIC_API_KEY.
  3. Tarayıcı senin /api/chat adresini çağırır; sunucun Claude’a gider, cevabı geri döndürür.

02Modeller#

ModelModel kimliğiNe zaman?
Claude Opus 5.5claude-opus-5-5Varsayılan: en iyi kalite; karmaşık soru, kod, uzun metin
Claude Sonnet 5.5claude-sonnet-5-5Hızlı ve dengeli; günlük sohbet, destek
Claude Haiku 5.5claude-haiku-5-5En hızlı ve ekonomik; sınıflandırma, kısa yanıt

Model kimliğini olduğu gibi kullan; sonuna tarih ekleme. Güncel liste ve fiyatlar için resmî dokümantasyona bak.

03API anahtarını saklamak#

Terminal.env (depoya EKLEME)
ANTHROPIC_API_KEY=sk-ant-...
Metin.gitignore
.env
node_modules/
vendor/

Vercel’de: Project → Settings → Environment Variables. Paylaşımlı hostingde: cPanel’de ortam değişkeni ya da public_html dışında duran bir .env dosyası.

04Node.js (resmî SDK)#

Terminal
npm install @anthropic-ai/sdk express
JavaScriptbasit-istek.mjs
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

const response = await client.messages.create({
  model: 'claude-opus-5-5',
  max_tokens: 16000,
  system: 'Sen fkdev.sbs sitesinin yardımcı asistanısın. Kısa ve Türkçe cevap ver.',
  messages: [{ role: 'user', content: 'fkdev UI nedir?' }],
});

if (response.stop_reason === 'refusal') {
  console.log('Model bu isteği yanıtlamadı.');
} else {
  for (const block of response.content) {
    if (block.type === 'text') console.log(block.text);
  }
}

new Anthropic() anahtarı ANTHROPIC_API_KEY ortam değişkeninden okur. Cevap bir blok dizisidir; metni almak için type === 'text' olan blokları oku.

05Node.js: canlı akış (streaming) uç noktası#

Uzun cevaplarda kullanıcı beklemesin diye cevabı kelime kelime akıt. Sunucu, Claude’dan gelen metni Server-Sent Events olarak tarayıcıya iletir.

JavaScriptserver.mjs
import express from 'express';
import Anthropic from '@anthropic-ai/sdk';

const app = express();
const client = new Anthropic();
app.use(express.json({ limit: '100kb' }));

app.post('/api/chat', async (req, res) => {
  const history = Array.isArray(req.body?.messages) ? req.body.messages.slice(-20) : [];
  const messages = history
    .filter((m) => (m.role === 'user' || m.role === 'assistant') && typeof m.content === 'string')
    .map((m) => ({ role: m.role, content: m.content.slice(0, 4000) }));
  if (!messages.length || messages[messages.length - 1].role !== 'user') {
    return res.status(400).json({ error: 'Mesaj yok' });
  }

  res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  try {
    const stream = client.messages.stream({
      model: 'claude-opus-5-5',
      max_tokens: 64000,
      system: 'Sen fkdev.sbs sitesinin yardımcı asistanısın. Kısa ve Türkçe cevap ver.',
      messages,
    });

    for await (const event of stream) {
      if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
        res.write('data: ' + JSON.stringify({ text: event.delta.text }) + '\n\n');
      }
    }

    const final = await stream.finalMessage();
    res.write('data: ' + JSON.stringify({ done: true, stop: final.stop_reason }) + '\n\n');
  } catch (err) {
    console.error(err);
    res.write('data: ' + JSON.stringify({ error: 'Şu an cevap veremiyorum, biraz sonra tekrar dene.' }) + '\n\n');
  }
  res.end();
});

app.listen(process.env.PORT || 3000);

06Tarayıcı: sohbet arayüzü#

Arayüz sohbet kalıbını kullanır. Akış okunurken metin asistan balonuna parça parça eklenir.

HTML
<div class="fk-card" style="max-width:640px;margin:auto">
  <div class="fk-chat" id="chat" aria-live="polite"></div>
  <form class="fk-input-group" id="chat-form" style="margin-top:18px">
    <input class="fk-input" name="q" placeholder="Bir şey sor…" aria-label="Mesaj" required style="padding-right:56px">
    <button class="fk-btn fk-btn-icon" type="submit" aria-label="Gönder">→</button>
  </form>
</div>
JavaScriptchat.js
const log = document.getElementById('chat');
const form = document.getElementById('chat-form');
const history = [];

function bubble(role, text = '') {
  const el = document.createElement('div');
  el.className = 'fk-msg ' + (role === 'user' ? 'fk-msg-user' : 'fk-msg-ai');
  el.textContent = text;
  log.appendChild(el);
  return el;
}

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const q = form.q.value.trim();
  if (!q) return;
  form.q.value = '';
  bubble('user', q);
  history.push({ role: 'user', content: q });

  const out = bubble('assistant');
  out.innerHTML = '<span class="fk-typing"><i></i><i></i><i></i></span>';
  let answer = '';

  try {
    const res = await fetch('/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ messages: history }),
    });
    const reader = res.body.getReader();
    const decoder = new TextDecoder();
    let buffer = '';
    while (true) {
      const { value, done } = await reader.read();
      if (done) break;
      buffer += decoder.decode(value, { stream: true });
      const parts = buffer.split('\n\n');
      buffer = parts.pop();
      for (const part of parts) {
        if (!part.startsWith('data: ')) continue;
        const msg = JSON.parse(part.slice(6));
        if (msg.text) { answer += msg.text; out.textContent = answer; }
        if (msg.error) FK.toast(msg.error, 'danger');
      }
    }
    history.push({ role: 'assistant', content: answer });
  } catch {
    out.textContent = 'Bağlantı koptu.';
  }
});

textContent kullanModelden gelen metni innerHTML ile basma; textContent kullan. Markdown göstermek istersen önce güvenli bir temizleyiciden (ör. DOMPurify) geçir.

07PHP (resmî SDK)#

Terminal
composer require "anthropic-ai/sdk"
PHPchat.php
<?php
require __DIR__ . '/vendor/autoload.php';

use Anthropic\Client;

$client = new Client(apiKey: getenv('ANTHROPIC_API_KEY'));

$message = $client->messages->create(
  model: 'claude-opus-5-5',
  maxTokens: 16000,
  system: 'Sen fkdev.sbs sitesinin yardımcı asistanısın. Kısa ve Türkçe cevap ver.',
  messages: [
    ['role' => 'user', 'content' => 'fkdev UI nedir?'],
  ],
);

foreach ($message->content as $block) {
  if ($block->type === 'text') {
    echo $block->text;
  }
}

PHP SDK’da parametre adları camelCase’dir: maxTokens.

PHPapi/chat-stream.php
<?php
use Anthropic\Messages\RawContentBlockDeltaEvent;
use Anthropic\Messages\TextDelta;

header('Content-Type: text/event-stream; charset=utf-8');
header('Cache-Control: no-cache');

$stream = $client->messages->createStream(
  model: 'claude-opus-5-5',
  maxTokens: 64000,
  messages: [['role' => 'user', 'content' => $_POST['q'] ?? '']],
);

foreach ($stream as $event) {
  if ($event instanceof RawContentBlockDeltaEvent && $event->delta instanceof TextDelta) {
    echo 'data: ' . json_encode(['text' => $event->delta->text]) . "\n\n";
    @ob_flush();
    flush();
  }
}
echo 'data: ' . json_encode(['done' => true]) . "\n\n";

08PHP: composer’sız (saf cURL)#

Paylaşımlı hostingde composer yoksa doğrudan HTTP isteği atabilirsin.

PHPinc/claude.php
<?php
function claude(array $messages, string $system = ''): array {
  $payload = [
    'model'      => 'claude-opus-5-5',
    'max_tokens' => 16000,
    'messages'   => $messages,
  ];
  if ($system !== '') $payload['system'] = $system;

  $ch = curl_init('https://api.anthropic.com/v1/messages');
  curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 120,
    CURLOPT_HTTPHEADER     => [
      'content-type: application/json',
      'x-api-key: ' . getenv('ANTHROPIC_API_KEY'),
      'anthropic-version: 2026-10-21',
    ],
    CURLOPT_POSTFIELDS     => json_encode($payload, JSON_UNESCAPED_UNICODE),
  ]);
  $body = curl_exec($ch);
  $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  $data = json_decode((string) $body, true) ?: [];
  if ($code !== 200) throw new RuntimeException($data['error']['message'] ?? 'Claude API hatası: ' . $code);
  return $data;
}

$res = claude([['role' => 'user', 'content' => 'Merhaba!']], 'Kısa ve Türkçe cevap ver.');
$text = '';
foreach ($res['content'] as $block) {
  if ($block['type'] === 'text') $text .= $block['text'];
}
echo htmlspecialchars($text);

09cURL ile hızlı test#

Terminal
curl https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2026-10-21" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 16000,
    "messages": [{"role": "user", "content": "Merhaba!"}]
  }'
Başlık / alanAçıklama
x-api-keyAPI anahtarın
anthropic-versionAPI sürümü
modelModel kimliği
max_tokensCevabın en fazla uzunluğu (token)
systemAsistanın rolü ve kuralları
messagesSohbet geçmişi: user ve assistant sırayla
streamtrue ise cevap olay akışı olarak gelir

10Güvenlik, kötüye kullanım ve hatalar#

  • Hız sınırı koy: IP ya da oturum başına dakikada belirli sayıda istek; yoksa biri senin faturanı şişirir.
  • Girdiyi sınırla: mesaj uzunluğunu ve geçmiş sayısını kes (ör. son 20 mesaj, mesaj başına 4.000 karakter).
  • Sistem talimatı: asistanın ne yapıp ne yapmayacağını system içinde yaz. Kullanıcı mesajları sistem talimatını “ezmeye” çalışabilir; gizli bilgiyi zaten prompt’a koyma.
  • Reddetme: stop_reason değeri "refusal" ise model isteği yanıtlamamıştır; kullanıcıya nazik bir mesaj göster.
  • Hatalar: 429 (çok istek) ve 5xx hatalarında bir süre bekleyip yeniden dene; 400 hatalarında isteği düzelt, tekrar deneme.
  • Kayıt: token kullanımını (usage.input_tokens, usage.output_tokens) ai_messages tablosuna yaz; maliyeti buradan izlersin.
  • Gizlilik: kullanıcıya sohbetin yapay zekâ tarafından işlendiğini söyle; kişisel veri istemeyen bir arayüz tasarla.

11Arayüz demosu#

Merhaba! Ben bir demo asistanım; gerçek API’ye bağlı değilim.
HTML
<div class="fk-card" data-chat-demo style="width:100%;max-width:560px">
  <div class="fk-chat" aria-live="polite"><div class="fk-msg fk-msg-ai">Merhaba! Ben bir demo asistanım; gerçek API’ye bağlı değilim.</div></div>
  <form class="fk-input-group" style="margin-top:18px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3c.6 4.8 4.2 8.4 9 9-4.8.6-8.4 4.2-9 9-.6-4.8-4.2-8.4-9-9 4.8-.6 8.4-4.2 9-9Z"/></svg><input type="text" class="fk-input" placeholder="Bir şey sor…" aria-label="Mesaj" style="padding-right:56px"><button class="fk-btn fk-btn-icon" type="submit" aria-label="Gönder"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 3 3 10.5l7 2.5 2.5 7L21 3Z"/><path d="m10 13 4-4"/></svg></button></form>
</div>

Dokümantasyonda ara

Esc
↑ ↓ gezinEnter aç