Back to Documentation
Vue.js Integration Guide
Learn how to integrate FXRateSync API with Vue.js using the Composition API and best practices.
Composable for Currency Operations
Create a reusable Vue composable for currency conversion:
// composables/useCurrencyConverter.js import { ref, reactive } from 'vue' export function useCurrencyConverter() { const loading = ref(false) const error = ref(null) const result = ref(null) const convert = async (from, to, amount) => { loading.value = true error.value = null result.value = null try { const response = await fetch( `https://api.fxratesync.io/v1/convert?from=${from}&to=${to}&amount=${amount}`, { headers: { 'X-API-Key': process.env.NEXT_PUBLIC_FX_API_KEY } } ) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const data = await response.json() result.value = data } catch (err) { error.value = err.message } finally { loading.value = false } } const reset = () => { result.value = null error.value = null } return { loading: readonly(loading), error: readonly(error), result: readonly(result), convert, reset } }
Vue Component Example
<!-- CurrencyConverter.vue --> <template> <div class="max-w-md mx-auto p-6 bg-background rounded-xl shadow-lg"> <h2 class="text-2xl font-bold text-center mb-6">Currency Converter</h2> <form @submit.prevent="handleSubmit" class="space-y-4"> <!-- Amount Input --> <div> <label for="amount" class="block text-sm font-medium text-foreground mb-2"> Amount </label> <input id="amount" v-model.number="form.amount" type="number" min="0" step="0.01" class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-500" placeholder="Enter amount" required /> </div> <!-- Currency Selectors --> <div class="grid grid-cols-2 gap-4"> <div> <label for="from" class="block text-sm font-medium text-foreground mb-2"> From </label> <select id="from" v-model="form.from" class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-500" > <option v-for="currency in currencies" :key="currency" :value="currency"> {{ currency }} </option> </select> </div> <div> <label for="to" class="block text-sm font-medium text-foreground mb-2"> To </label> <select id="to" v-model="form.to" class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-500" > <option v-for="currency in currencies" :key="currency" :value="currency"> {{ currency }} </option> </select> </div> </div> <!-- Swap Button --> <div class="text-center"> <button type="button" @click="swapCurrencies" class="px-4 py-2 text-primary hover:bg-primary/10 rounded-lg transition-colors" > ⇄ Swap Currencies </button> </div> <!-- Submit Button --> <button type="submit" :disabled="loading" class="w-full bg-primary hover:bg-primary/90 disabled:opacity-50 text-background font-medium py-3 px-6 rounded-lg transition-colors" > {{ loading ? 'Converting...' : 'Convert Currency' }} </button> </form> <!-- Results --> <div v-if="result" class="mt-6 p-4 bg-success/10 border border-success/30 rounded-lg"> <div class="text-center"> <div class="text-2xl font-bold text-success-foreground mb-1"> {{ formatCurrency(result.converted_amount, result.to) }} </div> <div class="text-sm text-success"> {{ result.amount }} {{ result.from }} = {{ result.converted_amount.toFixed(2) }} {{ result.to }} </div> <div class="text-xs text-success mt-2"> Rate: {{ result.rate.toFixed(4) }} • {{ new Date(result.timestamp).toLocaleString() }} </div> </div> </div> <!-- Error Display --> <div v-if="error" class="mt-6 p-4 bg-destructive/10 border border-destructive/30 rounded-lg"> <div class="text-destructive-foreground text-center"> {{ error }} </div> </div> </div> </template> <script setup> import { reactive } from 'vue' import { useCurrencyConverter } from '@/composables/useCurrencyConverter' const { loading, error, result, convert, reset } = useCurrencyConverter() const currencies = ['USD', 'EUR', 'GBP', 'JPY', 'CAD', 'AUD', 'CHF', 'CNY'] const form = reactive({ amount: 100, from: 'USD', to: 'EUR' }) const handleSubmit = async () => { if (form.amount <= 0) { return } await convert(form.from, form.to, form.amount) } const swapCurrencies = () => { const temp = form.from form.from = form.to form.to = temp reset() } const formatCurrency = (amount, currency) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency, minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(amount) } </script>
Pinia Store for State Management
// stores/currency.js import { defineStore } from 'pinia' import { ref, computed } from 'vue' export const useCurrencyStore = defineStore('currency', () => { // State const rates = ref({}) const lastUpdated = ref(null) const baseCurrency = ref('USD') const loading = ref(false) const error = ref(null) // Getters const isDataFresh = computed(() => { if (!lastUpdated.value) return false const fiveMinutesAgo = Date.now() - 5 * 60 * 1000 return new Date(lastUpdated.value).getTime() > fiveMinutesAgo }) const getRate = computed(() => { return (from, to) => { if (from === to) return 1 // Direct rate available if (rates.value[to]) { return rates.value[to] } // Calculate cross rate const fromRate = rates.value[from] const toRate = rates.value[to] if (fromRate && toRate) { return toRate / fromRate } return null } }) // Actions const fetchRates = async (base = 'USD') => { loading.value = true error.value = null try { const response = await fetch( `https://api.fxratesync.io/v1/rates/${base}`, { headers: { 'X-API-Key': process.env.NEXT_PUBLIC_FX_API_KEY } } ) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const data = await response.json() rates.value = data.rates lastUpdated.value = data.timestamp baseCurrency.value = base } catch (err) { error.value = err.message } finally { loading.value = false } } const convertAmount = (amount, from, to) => { const rate = getRate.value(from, to) return rate ? amount * rate : null } return { // State rates, lastUpdated, baseCurrency, loading, error, // Getters isDataFresh, getRate, // Actions fetchRates, convertAmount } })
Best Practices
- • Use environment variables for API keys (NEXT_PUBLIC_FX_API_KEY)
- • Implement proper error boundaries
- • Cache data in Pinia for better performance
- • Use Vue's reactivity system effectively
- • Consider using vue-query for advanced data fetching
- • Implement proper TypeScript types for better DX
Environment Setup
.env File
NEXT_PUBLIC_FX_API_KEY=your_api_key_here