Blog'a Dön

Vue.js ve Canlı Döviz Kuru API ile Para Birimi Dönüştürücü Nasıl Yapılır (2026 Rehberi)

V
Vlado Grigirov
April 28, 2026
Currency API Vue.js Tutorial Exchange Rates JavaScript Finexly Composition API

Canlı bir API'den gerçek zamanlı döviz kurları çeken bir Vue para birimi dönüştürücüsüne ihtiyacınız varsa, bu eğitim sizi baştan sona götürür. Composition API, TypeScript ve ücretsiz bir döviz kuru API'si kullanarak üretime hazır bir Vue 3 bileşeni inşa edeceksiniz — uygun debouncing, önbellekleme, hata yönetimi ve Nuxt için SSR-uyumlu desenlerle.

Sonunda yeniden kullanılabilir bir <CurrencyConverter /> bileşenine, herhangi bir Vue 3 projesine eklenebilen bir useCurrencyRates composable'ına ve gerçek kullanıcılara para birimi dönüştürmesi sunarken önemli olan ödünleşimlerin net bir kavrayışına sahip olacaksınız.

Ne inşa edeceksiniz

Şu özelliklere sahip bir Vue 3 para birimi dönüştürücüsü:

  • Finexly API üzerinden 170+ para birimi için canlı döviz kurları
  • Kullanıcı yazdıkça güncellenen reaktif dönüştürme
  • Para birimi takas düğmesi (USD → EUR tek tıkla EUR → USD olur)
  • Ağı sömürmemek için debounce'lu API çağrıları
  • Düşük gecikme ve ücretsiz kota dahilinde kalmak için bellek içi önbellek
  • Hata yönetimi, yükleme durumları ve uygun TypeScript tipleri
  • Nuxt 3'te hidrasyon uyumsuzlukları olmadan çalışacak SSR-dostu yapı

Nihai bileşen yaklaşık 80 satır kod. Composable da 60 satır daha. Bu kadar.

Ön koşullar

Şunlarla rahat olmalısınız:

  • Vue 3 sözdizimi (<script setup> formu)
  • Temel TypeScript
  • fetch ile REST API çağrısı

Ayrıca bir Finexly API anahtarı gerekecek. Panodan bir tane alın — yaklaşık 30 saniye sürer ve ücretsiz plan kredi kartı gerektirmeden ayda 1.000 istek verir. Hizmeti hiç kullanmadıysanız, Finexly API dokümantasyonunda 5 dakikalık bir hızlı başlangıç var.

Adım 1: Vite ile Vue 3 projesi kurma

Sıfırdan başlıyorsanız:

npm create vite@latest finexly-converter -- --template vue-ts
cd finexly-converter
npm install
npm run dev

Vite size Vue 3, TypeScript ve hot module reload'u kutudan çıkar çıkmaz verir. src/App.vue'yi açın ve şablon kodu temizleyin — yakında onu dönüştürücüyle değiştireceğiz.

Mevcut bir Nuxt 3 projesine entegre ediyorsanız, bu adımı atlayabilirsiniz. Aşağıdaki composable Nuxt'ta da aynı şekilde çalışır çünkü vue'dan standart ref ve computed kullanır.

Adım 2: API anahtarını güvenli bir şekilde sakla

Finexly API anahtarınızı asla doğrudan bir bileşene yapıştırmayın. .env.local içine koyun:

VITE_FINEXLY_API_KEY=anahtarınız_buraya

Vite, VITE_ ön ekli her değişkeni istemciye açar. Nuxt için NUXT_PUBLIC_FINEXLY_API_KEY kullanın ve useRuntimeConfig().public.finexlyApiKey üzerinden okuyun.

İstemci paketinde anahtarın açığa çıkmasından endişe duyuyorsanız, isteği bir backend route veya serverless function üzerinden proxy'leyin. Bu deseni Adım 7'de göstereceğiz.

Adım 3: useCurrencyRates composable'ını oluşturma

Composable, dönüştürücünün kalbidir. Fetch'i, önbelleği ve yükleme/hata durumunu üstlenir — böylece bileşen sunum odaklı kalır.

src/composables/useCurrencyRates.ts oluşturun:

import { ref, type Ref } from 'vue'

const API_KEY = import.meta.env.VITE_FINEXLY_API_KEY
const BASE_URL = 'https://api.finexly.com/v1'

// Modül seviyesinde önbellek: bu composable'ı çağıran tüm bileşenler arasında paylaşılır.
// Her giriş 5 dakika yaşar — anlık hissetmeye yetecek kadar uzun, oynak FX hareketlerinde
// doğruluğu korumaya yetecek kadar kısa.
const cache = new Map<string, { rates: Record<string, number>; expires: number }>()
const TTL_MS = 5 * 60 * 1000

interface RatesResponse {
  base: string
  date: string
  rates: Record<string, number>
}

export function useCurrencyRates() {
  const rates: Ref<Record<string, number>> = ref({})
  const loading = ref(false)
  const error = ref<string | null>(null)

  async function fetchRates(base: string) {
    const cached = cache.get(base)
    if (cached && cached.expires > Date.now()) {
      rates.value = cached.rates
      return
    }

    loading.value = true
    error.value = null
    try {
      const url = `${BASE_URL}/latest?base=${base}&apikey=${API_KEY}`
      const res = await fetch(url)
      if (!res.ok) throw new Error(`Finexly returned ${res.status}`)
      const data: RatesResponse = await res.json()

      rates.value = data.rates
      cache.set(base, { rates: data.rates, expires: Date.now() + TTL_MS })
    } catch (e) {
      error.value = e instanceof Error ? e.message : 'Failed to load rates'
    } finally {
      loading.value = false
    }
  }

  function convert(amount: number, from: string, to: string): number {
    if (from === to) return amount
    const fromRate = rates.value[from]
    const toRate = rates.value[to]
    if (!fromRate || !toRate) return 0
    return (amount / fromRate) * toRate
  }

  return { rates, loading, error, fetchRates, convert }
}

Vurgulamaya değer birkaç ayrıntı:

  • cache kasten modül seviyesinde yaşar. Composable'ı çağıran iki bileşen aynı önbelleği paylaşır, bu da rota değiştirmenin aynı temel para birimini yeniden çekmeyeceği anlamına gelir.
  • rates, Record<string, number> olarak tipiplendi, böylece daha sonra doğrudan bir <select>'e besleyebilirsiniz.
  • convert fonksiyonu çapraz kur matematiği yapar; bu yüzden "from" para birimi her değiştiğinde yeniden çekmek zorunda değilsiniz. Daha fazlası Adım 5'te.

Adım 4: Dönüştürücü bileşeni

src/components/CurrencyConverter.vue oluşturun:

<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import { useCurrencyRates } from '@/composables/useCurrencyRates'

const { rates, loading, error, fetchRates, convert } = useCurrencyRates()

const amount = ref(100)
const from = ref('USD')
const to = ref('EUR')

const converted = computed(() =>
  convert(amount.value, from.value, to.value).toFixed(2)
)

const currencies = computed(() => Object.keys(rates.value).sort())

function swap() {
  ;[from.value, to.value] = [to.value, from.value]
}

let timeout: ReturnType<typeof setTimeout> | null = null
watch(from, (newBase) => {
  if (timeout) clearTimeout(timeout)
  timeout = setTimeout(() => fetchRates(newBase), 300)
})

onMounted(() => fetchRates(from.value))
</script>

<template>
  <div class="converter">
    <h2>Currency Converter</h2>

    <div class="row">
      <input v-model.number="amount" type="number" min="0" />
      <select v-model="from">
        <option v-for="c in currencies" :key="c" :value="c">{{ c }}</option>
      </select>
    </div>

    <button @click="swap" aria-label="Swap currencies">⇅</button>

    <div class="row">
      <output>{{ loading ? '...' : converted }}</output>
      <select v-model="to">
        <option v-for="c in currencies" :key="c" :value="c">{{ c }}</option>
      </select>
    </div>

    <p v-if="error" class="error">{{ error }}</p>
  </div>
</template>

<CurrencyConverter />App.vue'ya yerleştirin ve çalışan bir dönüştürücünüz olsun. İlk fetch döner dönmez para birimleri listesi dolar. Tutar alanına yazmak ağa dokunmadan sonucu reaktif olarak günceller. Para birimlerini takas etmek dropdown'ları yeniden sıralar. "from" para birimini değiştirmek debounce'lu bir refetch tetikler.

Adım 5: Çapraz kur dönüştürmesi (istek yakmamak için)

Yalnızca tek bir tabandan dönüştürüyorsanız, bu bölümü atlayabilirsiniz. Ama çoğu dönüştürücü kullanıcının her iki tarafı da seçmesine izin verir — ve naif bir uygulama her "from" değişikliğinde yeniden çeker.

Çapraz kur matematiği bunu çözer. Tabanınız USD ise ve EUR ile JPY'nin USD karşısındaki kurlarını biliyorsanız, EUR → JPY kuru basitçe (1 / EUR_rate) * JPY_rate olur. Bu tam olarak Adım 3'teki convert fonksiyonunun yaptığı şey:

return (amount / fromRate) * toRate

Bu, oturum başına yalnızca bir kez fetch yapmanız gerektiği anlamına gelir. Ücretsiz kota için büyük kazanç. from üzerindeki watcher savunma önlemi haline gelir — bir kullanıcı önbellekteki kur tablosunda olmayan egzotik bir para birimi seçerse, o tabanla yeniden fetch yapmak dönüşümün çalışmaya devam etmesini garanti eder.

Adım 6: Yükleme iskeletleri ve hata durumları

Dönen ikonlar iyi, ancak kurlar yüklenirken layout kayması rahatsız edicidir. İlk fetch sırasında placeholder seçenekleri render edin:

<select v-model="from" :disabled="loading && currencies.length === 0">
  <option v-if="currencies.length === 0">Loading...</option>
  <option v-for="c in currencies" :key="c" :value="c">{{ c }}</option>
</select>

Hata yolunda çıkmaz mesaj yerine bir tekrar dene düğmesi gösterin:

<div v-if="error" class="error-box">
  <p>{{ error }}</p>
  <button @click="fetchRates(from)">Retry</button>
</div>

Kibar olmak isterseniz 429 (rate limit) ve 5xx'i ayrı ele alın. Finexly'nin aylık 1.000 ücretsiz isteği ve 5 dakikalık önbellekle, sınıra ulaşmak için gerçek bir tepe trafiği gerekir — ama temiz bir retry yolu UI'yi sağlam gösterir.

Adım 7: API anahtarını sunucu proxy'siyle gizleme

import.meta.env.VITE_* içindeki her şey istemci paketine girer. Salt okunur para birimi widget'larının çoğu için kabul edilebilir — en kötü durumda biri anahtarınızla kurları kazır. Derin savunma istiyorsanız, isteği sunucu tarafında proxy'leyin.

Nuxt 3 sunucu route'u ile:

// server/api/rates.get.ts
export default defineEventHandler(async (event) => {
  const { base } = getQuery(event)
  const config = useRuntimeConfig()
  return await $fetch(
    `https://api.finexly.com/v1/latest?base=${base}&apikey=${config.finexlyApiKey}`
  )
})

Sonra composable'ı Finexly origin yerine /api/rates?base=${base}'a yönlendirin. Anahtar sunucudan asla çıkmaz. Aynı desen herhangi bir Vue meta-framework'ünde çalışır — Nuxt, Quasar veya basit bir Express backend.

Adım 8: Üretim kontrol listesi

Yayınlamadan önce:

  • Agresif önbellekleme yapın. FX kurları her saniye değişmez. İstemcide 5 dakika TTL ve sunucuda 1 dakika TTL neredeyse her gösterim kullanım durumu için fazlasıyla yeterli. Üretimde test edilmiş desenler için önbellekleme ve hata yönetimi rehberimize bakın.
  • Doğru yuvarlayın. Para birimi gösterimi para biriminin alt birimine yuvarlanır (USD için 2 hane, JPY için 0, KWD için 3). .toFixed(2) yerine Intl.NumberFormat(locale, { style: 'currency', currency: to.value }) kullanın.
  • Intl ile biçimlendirin. new Intl.NumberFormat('en-US', { style: 'currency', currency: 'EUR' }).format(123.45) size "€123.45" verir ve kullanıcının yerel ayarlarına saygı gösterir.
  • SSR-güvenli fetch'ler. Nuxt'ta onMounted içindeki ham fetch yerine useFetch'i tercih edin; böylece kurlar sunucu render'ı sırasında erişilebilir olur ve hidrasyon uyumsuzluğunu tetiklemez.
  • Kotanızı izleyin. Aylık istek bütçenizin %80'ini tükettiğinizde uyaran küçük bir logger ekleyin. Daha yüksek hacim için fiyatlandırma planları ücretsiz katmanın bittiği yerden başlar.

Vue 2 (Options API) varyantı

Vue 2'deyseniz, aynı mantık neredeyse satır satır çevrilir:

export default {
  data() {
    return { rates: {}, loading: false, error: null, amount: 100, from: 'USD', to: 'EUR' }
  },
  computed: {
    converted() {
      const fr = this.rates[this.from]
      const tr = this.rates[this.to]
      return fr && tr ? ((this.amount / fr) * tr).toFixed(2) : '0.00'
    },
    currencies() {
      return Object.keys(this.rates).sort()
    },
  },
  mounted() { this.fetchRates(this.from) },
  watch: {
    from(newBase) { this.fetchRates(newBase) },
  },
  methods: {
    async fetchRates(base) {
      this.loading = true
      try {
        const res = await fetch(
          `https://api.finexly.com/v1/latest?base=${base}&apikey=${process.env.VUE_APP_FINEXLY_KEY}`
        )
        const data = await res.json()
        this.rates = data.rates
      } catch (e) {
        this.error = e.message
      } finally {
        this.loading = false
      }
    },
  },
}

Composition API, bileşenler arasında mantık paylaşmak için daha temizdir, ancak Options API tek bir dönüştürücü widget'ı için iyi çalışır. Ekibiniz daha fonksiyonel bir yaklaşım tercih ediyorsa, JavaScript entegrasyon rehberi framework'ten bağımsız desenleri kapsar.

Yaygın tuzaklar (ve nasıl kaçınılır)

Nuxt'ta hidrasyon uyumsuzluğu. onMounted içinde fetch çağırmak istemcide çalışır ama SSR tutarlılığını bozar. Bunun yerine useFetch veya useAsyncData kullanın.

Uzun oturumdan sonra eskimiş kurlar. Yukarıdaki 5 dakika TTL, gün boyu açık kalan bir sekmenin son saatin kurlarını gösterdiği anlamına gelir. visibilitychange ile yenileyin:

document.addEventListener('visibilitychange', () => {
  if (!document.hidden) fetchRates(from.value)
})

Kayan nokta sürüklenmesi. JavaScript sayıları double'dır. 0.1 + 0.2 !== 0.3. Para tutarları için tam sayı kuruşlara çarpın (amount * 100), hesaplayın, sonra bölün. Veya checkout ile ilgili her şey için dinero.js gibi bir kütüphane kullanın.

Geliştirmede CORS hataları. Bazı para birimi API'leri tarayıcıdan doğrudan çağrılara izin vermez. Finexly istemci tarafı kullanım için tarayıcı origin'lerine izin verir; Adım 7'deki proxy diğerlerini çözer.

Vue projeleri için neden Finexly

Bir frontend uygulaması için döviz kuru API seçerken bazı şeyler önemlidir: yanıt süresi, doğruluk, ücretsiz kademenin cömertliği ve temiz JSON. Finexly dördünü de hedefler — sub-50ms p95 gecikmesi, her 60 saniyede güncellenen mid-market kurları, 170+ para birimi ve hiç işlem yapmadan doğrudan bir Vue ref'ine düşen JSON şekli.

Alternatiflerle nasıl karşılaştırıldığını görmek isterseniz, para birimi API karşılaştırmamız ödünleşimleri detaylı inceler. Veya yan yana karşılaştırma sayfasını kullanın.

Sıkça sorulan sorular

Bu eğitimi Vue 2 ile kullanabilir miyim?

Evet. Composable deseni yalnızca Vue 3'tür, ancak temel mantık — kurları çek, data'da sakla, dönüşümü hesapla — Options API'de aynı çalışır. Yukarıdaki Vue 2 örneği doğrudan bir alternatiftir.

Finexly API Vue projeleri için ücretsiz mi?

Ücretsiz plan size ayda 1.000 istek verir; bir yan proje, bir portfolyo parçası veya küçük bir SaaS için fazlasıyla yeterli. 5 dakikalık önbellekle yaklaşık 200 günlük aktif kullanıcıyı taşır. Daha yüksek hacimler için fiyatlandırma planları'na bakın.

API anahtarımın istemci paketinde açığa çıkmasını nasıl önlerim?

Adım 7'de gösterildiği gibi isteği bir sunucu route'u üzerinden proxy'leyin. VITE_* ve NUXT_PUBLIC_* ön ekleri her ikisi de değişkenleri istemciye görünür kılar. Hassas her şey bir sunucu fonksiyonunun arkasında yaşamalıdır.

Kurlar ne kadar doğru?

Finexly birden fazla Tier-1 likidite sağlayıcısından mid-market kurları toplar ve her 60 saniyede yeniler. Gösterim ve çoğu fiyatlandırma uygulaması için yeterince doğru. Trade yürütme için akış feed'i istersiniz — REST vs WebSocket rehberimize bakın.

Tarihsel tutarları dönüştürebilir miyim?

Evet — Finexly'nin /historical endpoint'i bir tarih parametresi alır ve herhangi bir iş gününün kurlarını döndürür. Desen yukarıdaki /latest endpoint'iyle aynı; sadece URL'yi değiştirin. Tarihsel döviz kurları API rehberi bunu detaylı kapsar.

Toparlama

Artık gerçek dünyadaki kaygıları — önbellekleme, debouncing, hata durumları, SSR ve API anahtarı güvenliği — ele alan bir Vue 3 para birimi dönüştürücüsüne sahipsiniz. Composable deseni, aynı useCurrencyRates'i fetch mantığını yeniden yazmadan bir navbar widget'ına, bir checkout sayfasına veya bir dashboard grafiğine bırakabileceğiniz anlamına gelir.

Kendi projenizde denemeye hazır mısınız? Ücretsiz Finexly API anahtarınızı alın — kredi kartı gerekmez. Ayda 1.000 ücretsiz istekle başlayın ve trafiğiniz ücretsiz katmanı aştığında yükseltin.

Vlado Grigirov

Senior Currency Markets Analyst & Financial Strategist

Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...

View full profile →