Kur çevirici, küçük ama öğretici bir API entegrasyonudur. Kullanıcı bir tutar girer, kaynak para birimini seçer, hedef para birimini seçer ve uygulama güncel veya cache'li kurla sonucu hesaplar. Basit görünür, fakat veri tazeliği, hatalı sayı girişi, API yanıt doğrulaması ve üretim sorumluluğu gibi gerçek kararları içerir.
Bu rehberde API Deposu'ndaki currency-api-fawazahmed0 kaydını temel alıyoruz. Amaç finansal tavsiye veren bir ürün kurmak değil; ücretsiz bir exchange-rate kaynağıyla güvenli bir demo veya yardımcı araç nasıl tasarlanır sorusunu yanıtlamak.
Temel akış
Kur çeviricinin dört girdisi vardır: kaynak para birimi, hedef para birimi, tutar ve hedef kur. Örneğin usd, try ve 100 için USD bazlı oran belgesindeki try değeri bulunur; sonuç 100 * oran ile hesaplanır.
API çağrısından önce para birimi kodlarını ve tutarı doğrulayın. Yanıtta hedef alan yoksa veya sonlu bir sayı değilse sonuç üretmeyin. TypeScript tipleri dışarıdan gelen JSON'u çalışma anında doğrulamadığı için bu kontroller fetch yardımcısında yapılmalıdır.
Endpoint şekli
API Deposu kaydının Test Lab tabanı jsDelivr üzerindedir:
https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latestUSD bazlı oran belgesi için yol v1/currencies/usd.json olur:
GET https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/usd.jsonKaynak para birimi URL'deki dosya adını ve yanıttaki oran nesnesinin anahtarını belirler. Upstream proje veya barındırma yönlendirmesi değişebileceği için üretime geçmeden önce repository belgelerini yeniden kontrol edin; gerekiyorsa incelenmiş bir paket sürümünü @latest yerine sabitleyin.
Doğrulamalı ve cache'li fetch yardımcısı
Aşağıdaki kod, oran belgesini bir saat yeniden doğrulama süresiyle ister. HTTP hatasını, timeout'u ve beklenmeyen JSON şeklini kullanıcı arayüzünden önce ele alır.
// src/lib/currency-rates.ts
type RatesMap = Record<string, number>;
export type CurrencyRates = {
date: string | null;
rates: RatesMap;
};
function parseRates(payload: unknown, base: string): CurrencyRates {
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
throw new Error("Kur API yanıtı bir nesne değil.");
}
const document = payload as Record<string, unknown>;
const rawRates = document[base];
if (!rawRates || typeof rawRates !== "object" || Array.isArray(rawRates)) {
throw new Error("Kur API yanıtında oran nesnesi yok.");
}
const rates: RatesMap = {};
for (const [currency, value] of Object.entries(rawRates)) {
if (
/^[a-z]{3}$/.test(currency)
&& typeof value === "number"
&& Number.isFinite(value)
&& value > 0
) {
rates[currency] = value;
}
}
if (Object.keys(rates).length === 0) {
throw new Error("Kur API yanıtında kullanılabilir oran yok.");
}
return {
date:
typeof document.date === "string" && /^\d{4}-\d{2}-\d{2}$/.test(document.date)
? document.date
: null,
rates,
};
}
export async function fetchCurrencyRates(baseCurrency: string): Promise<CurrencyRates> {
const base = baseCurrency.trim().toLowerCase();
if (!/^[a-z]{3}$/.test(base)) {
throw new Error("Kaynak para birimi üç harfli olmalı.");
}
const url =
`https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/${base}.json`;
const response = await fetch(url, {
next: { revalidate: 60 * 60 },
signal: AbortSignal.timeout(5_000),
});
if (!response.ok) {
throw new Error(`Kur API isteği HTTP ${response.status} ile başarısız oldu.`);
}
const payload: unknown = await response.json();
return parseRates(payload, base);
}Cache URL'ye göre tutulduğu için bir kez alınan usd oran haritası, aynı pencere içinde USD'den farklı hedeflere yapılan dönüşümlerde yeniden kullanılabilir. Formun her tuş vuruşunda fetch çağırmak yerine oran belgesini sunucuda bir kez alın; dönüşüm hesaplarını eldeki harita üzerinde yapın. Bir saat yalnızca örnek tercihtir, doğruluk gereksiniminize ve upstream güncelleme davranışına göre değiştirin.
Normalizasyon yalnızca üç harfli anahtara sahip, sonlu ve pozitif oranları kabul eder. date alanı da beklenen tarih biçimine uymuyorsa gösterilmez; tarih alanının varlığı, verinin iş ihtiyacınız için yeterince güncel olduğunu tek başına kanıtlamaz. Kabul edilebilir azami veri yaşını ürün kuralı olarak ayrıca tanımlayın.
Tutarı çevirin ve biçimlendirin
Dönüşümü ağ erişiminden ayrı, saf bir fonksiyon yapmak birim testini kolaylaştırır:
export function convertCurrency(
amount: number,
targetCurrency: string,
rates: Record<string, number>,
): number {
const target = targetCurrency.trim().toLowerCase();
if (!Number.isFinite(amount) || amount <= 0) {
throw new Error("Tutar sıfırdan büyük bir sayı olmalı.");
}
if (!/^[a-z]{3}$/.test(target)) {
throw new Error("Hedef para birimi üç harfli olmalı.");
}
const rate = rates[target];
if (!Number.isFinite(rate)) {
throw new Error(`${target.toUpperCase()} kuru yanıtta bulunamadı.`);
}
return amount * rate;
}
export function formatCurrency(value: number, currency: string): string {
try {
return new Intl.NumberFormat("tr-TR", {
style: "currency",
currency: currency.toUpperCase(),
maximumFractionDigits: 2,
}).format(value);
} catch {
return `${value.toLocaleString("tr-TR")} ${currency.toUpperCase()}`;
}
}Muhasebe veya ödeme akışında kayan noktalı sayılar ve iki basamaklı gösterim tek başına yeterli değildir; para tutarlarını küçük birimlerle ya da uygun bir decimal kütüphanesiyle modelleyin.
Server component kullanım örneği
// src/components/currency-example.tsx
import {
convertCurrency,
fetchCurrencyRates,
formatCurrency,
} from "@/lib/currency-rates";
export async function CurrencyExample() {
let result: { converted: number; date: string | null } | null = null;
try {
const amount = 100;
const { date, rates } = await fetchCurrencyRates("usd");
const converted = convertCurrency(amount, "try", rates);
result = { converted, date };
} catch {
result = null;
}
if (!result) {
return <p role="alert">Kur bilgisi şu anda alınamıyor.</p>;
}
return (
<section aria-label="Örnek kur çevirisi">
<p>100 USD ≈ {formatCurrency(result.converted, "try")}</p>
{result.date ? <small>Veri tarihi: {result.date}</small> : null}
</section>
);
}Gerçek bir formda kullanıcı girdisini parse edip doğruladıktan sonra aynı dönüşüm fonksiyonunu çağırın. Hata ayrıntısını sunucu loglarında izleyebilir, kullanıcıya ise sağlayıcı veya dahili yapı hakkında gereksiz detay sızdırmayan kısa bir durum gösterebilirsiniz.
Form hatası ile sağlayıcı hatasını aynı sınıfa koymayın. Geçersiz tutar veya para birimi kullanıcı tarafından düzeltilebilir; timeout, HTTP hatası ya da eksik oran ise veri katmanına aittir. UI ilk grupta alanın yanında açıklama, ikinci grupta yeniden deneme veya son başarılı verinin tarihi gibi bir durum göstermelidir.
Üretim sınırları
Ücretsiz ve anahtarsız kaynak, demo ve düşük riskli yardımcı araçlar için uygundur; ödeme, muhasebe, yatırım veya sözleşme tutarının tek veri kaynağı olarak otomatik biçimde uygun sayılmaz. Üretimde upstream repository ve lisansı inceleyin, başarısız fetch'leri izleyin ve kabul edilebiliyorsa son başarılı belgeyi tarihini açıkça göstererek stale fallback olarak saklayın.
Sağlayıcı çağrısını fetchCurrencyRates sınırında tutmak, ileride resmî veya SLA sunan bir kaynağa geçerken form doğrulamasını ve UI'ı korur. Kullanıcıya veri tarihi, kaynak ve "yaklaşık sonuç" niteliğini göstermek de ücretsiz veriyle kesin işlem kuru arasındaki farkı açık tutar.
İlgili API Deposu kayıtları
Kaynaklar
Sık Sorulan Sorular
›Bu kur çevirici için API anahtarı gerekir mi?
Bu rehberde kullanılan fawazahmed0 currency API akışı public CDN üzerinden örneklenebilir ve API anahtarı gerektirmez. Üretim öncesi upstream repo, lisans ve hosting beklentileri kontrol edilmelidir.
›Döviz kurları ne kadar sık yenilenmeli?
Basit bir çevirici için her tuş vuruşunda istek atmak yerine dakikalar veya saatler seviyesinde cache kullanmak daha güvenlidir. Kesin süre ürünün doğruluk ihtiyacına göre belirlenmelidir.
›Ücretsiz döviz API'si üretimde kullanılabilir mi?
Prototip ve düşük riskli araçlarda kullanılabilir, fakat üretimde yanıt doğrulama, cache, fallback ve lisans incelemesi olmadan doğrudan kritik finansal kararlar için kullanılmamalıdır.



