Python'da bir fintech arka ucu, çok para birimli bir ödeme akışı veya dahili bir fiyatlandırma servisi geliştiriyorsanız, er ya da geç kendi döviz kuru uç noktanıza ihtiyaç duyarsınız. FastAPI ile bir döviz kuru API'si oluşturmak, canlı bir kur sağlayıcısını saran, ücretsiz katmanda kalmanız için önbellekleme ekleyen ve diğer servislerinizin çağırabileceği temiz bir /convert uç noktası sunan hızlı, asenkron ve tamamen tip güvenli bir mikroservis verir. FastAPI burada doğal olarak uygundur: gerçek asenkron G/Ç için ASGI üzerine kuruludur, istek ve yanıtları Pydantic ile doğrular ve ücretsiz olarak etkileşimli OpenAPI dokümantasyonu üretir. Bu eğitim tüm yolu kat eder — sağlayıcıya yaptığınız ilk istekten, paylaşılan bir HTTP istemcisi, önbellekli okumalar, Decimal ile para açısından güvenli aritmetik ve tip güvenli hata yönetimi içeren üretime hazır bir dönüştürücüye kadar.
Sonunda, Finexly API dokümantasyonu ve 170'ten fazla para birimi kapsamıyla desteklenen küçük, kendi kendine yeten bir FastAPI servisiniz olacak. Python döviz API eğitimimizi zaten okuduysanız, bu, parçaların gerçek bir ASGI arka ucunda nasıl bir araya geldiğini gösteren servis düzeyindeki tamamlayıcıdır. Django eğitimi ORM ve şablon katmanına dayanırken, FastAPI her şeyi yalın ve async odaklı tutar.
FastAPI neden bir para birimi mikroservisi için harika bir seçim
Para birimi dönüşümü basit görünür — bir tutarı bir kurla çarpmak — ama bunu bir servis olarak iyi yapmak, FastAPI'nin tasarlandığı konulara dokunur:
- Gerçek asenkron G/Ç. Kur çekmek ağa bağlıdır.
async defuç noktaları ve asenkron bir HTTP istemcisiyle tek bir worker, istekler yolda iken çağrı başına bir iş parçacığını bloke etmek yerine birçok eşzamanlı dönüşümü karşılayabilir. - Tip güvenli sözleşmeler. Pydantic modelleri gelen sorgu parametrelerini doğrular ve giden JSON'u şekillendirir, böylece bozuk bir
amountveya bilinmeyen bir para birimi kodu, mantığınıza ulaşmadan önce net bir 422 ile reddedilir. - Ücretsiz etkileşimli dokümantasyon. Yazdığınız her uç nokta
/docsadresindeki Swagger UI'da görünür; bu da servisi ekibinizin geri kalanı için kendini belgeleyen hale getirir. - Lifespan yönetimi. FastAPI, başlangıçta tek bir HTTP bağlantı havuzu açmak ve kapanışta boşaltmak için temiz bir yer verir — performans için kritik, aşağıda göreceğiniz gibi.
Oluşturduğumuz mimari ince, önbellek bilinçli bir vekildir: servisiniz kur API'sini çağırır, yanıtı kısa bir pencere boyunca önbelleğe alır ve dönüşümleri kendi ön ucunuza veya diğer mikroservislere sunar. Bu desen API anahtarlarınızı sunucu tarafında tutar, sizi sağlayıcının hız sınırlarından yalıtır ve iş kurallarını (yuvarlama, komisyon ekleri, izin listeleri) tek bir yerde eklemenize olanak tanır.
Ön koşullar ve proje kurulumu
Python 3.11 veya daha yenisi gerekir. Bir sanal ortam oluşturun ve bağımlılıkları kurun:
python -m venv .venv
source .venv/bin/activate # on Windows: .venv\Scripts\activate
pip install "fastapi[standard]" httpx pydantic-settingsfastapi[standard] Uvicorn'u (ASGI sunucusu) ve CLI'yi getirir. httpx asenkron HTTP istemcimizdir ve pydantic-settings yapılandırmayı ortam değişkenlerinden yönetir.
Finexly panelinizden ücretsiz bir API anahtarı alın ve asla sürüm kontrolüne düşmemesi için dışa aktarın:
export FINEXLY_API_KEY="your_api_key_here"Proje yapısını oluşturun:
fx-service/
├── app/
│ ├── __init__.py
│ ├── config.py
│ ├── client.py
│ ├── models.py
│ └── main.py
└── .envAdım 1: pydantic-settings ile yapılandırma
Yapılandırmayı tek bir tip güvenli yerde tutun. pydantic-settings ortam değişkenlerini ve bir .env dosyasını okur ve gerekli bir değer eksikse gürültülü biçimde başarısız olur.
# app/config.py
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_prefix="FINEXLY_")
api_key: str
base_url: str = "https://api.finexly.com/v1"
cache_ttl_seconds: int = 300
request_timeout_seconds: float = 10.0
settings = Settings() # raises at startup if FINEXLY_API_KEY is unsetYapılandırma nesnesi içe aktarma anında oluşturulduğundan, eksik bir anahtar servisin ilk istekte başarısız olmasını değil, hiç başlamamasını sağlar — bir dağıtım hattında tam da istediğiniz şey.
Adım 2: Lifespan aracılığıyla paylaşılan, asenkron bir HTTP istemcisi
Bu, tüm servisteki en önemli performans kararıdır. İstek başına yeni bir httpx.AsyncClient oluşturmayın. Her istemci bir bağlantı havuzuna sahiptir; çağrı başına bir tane oluşturmak keep-alive bağlantılarını çöpe atar ve her seferinde yeni bir TLS el sıkışmasını zorlar. Bunun yerine, uygulama başlarken bir istemci açın ve onu sürecin tüm ömrü boyunca yeniden kullanın.
Bunu yapmanın modern yolu FastAPI'nin lifespan bağlam yöneticisidir. (Eski @app.on_event("startup") ve @app.on_event("shutdown") dekoratörleri, lifespan lehine FastAPI 0.93+ ile kullanımdan kaldırıldı.) async with kullanmak, başlangıç kesintiye uğrasa bile kapanışta bağlantı havuzunun boşaltılmasını ve soketlerin kapatılmasını garanti eder.
# app/client.py
import httpx
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from .config import settings
@asynccontextmanager
async def lifespan(app: FastAPI):
# Runs once on startup: open a single pooled client
async with httpx.AsyncClient(
base_url=settings.base_url,
headers={"Authorization": f"Bearer {settings.api_key}"},
timeout=settings.request_timeout_seconds,
) as client:
app.state.http = client
yield
# On shutdown, the async-with block closes the pool automatically
def get_client(request: Request) -> httpx.AsyncClient:
# FastAPI dependency: hand endpoints the shared client
return request.app.state.httpArtık her uç nokta aynı havuzlanmış istemciyi bir bağımlılık aracılığıyla ödünç alır ve kimsenin bağlantı açmayı veya kapatmayı düşünmesi gerekmez.
Adım 3: Tip güvenli istek ve yanıt modelleri
Pydantic modelleri size doğrulama ve kendini belgeleyen yanıtlar verir. Parasal tutarlar için Decimal kullanımına dikkat edin: ikili kayan noktalar ondalık kesirleri tam olarak temsil edemez ve parada yuvarlama hataları bir merak değil, gerçek bir hatadır.
# app/models.py
from decimal import Decimal
from pydantic import BaseModel, Field
class ConversionResult(BaseModel):
base: str = Field(..., examples=["USD"])
quote: str = Field(..., examples=["EUR"])
amount: Decimal = Field(..., examples=["100.00"])
rate: Decimal = Field(..., examples=["0.92"])
converted: Decimal = Field(..., examples=["92.00"])
as_of: str = Field(..., description="Timestamp of the upstream rate")
class RatesResponse(BaseModel):
base: str
rates: dict[str, Decimal]
as_of: strFastAPI bu modelleri OpenAPI şemasında işler ve değerleri otomatik olarak dönüştürür/doğrular, böylece anlamsız bir tutar içeren bir istek, işleyiciniz çalışmadan önce reddedilir.
Adım 4: Küçük bir TTL önbelleğiyle kurları çekmek
Kurlar milisaniyeden milisaniyeye değişmez ve sağlayıcı API'sini her istekte dövmek hem yavaştır hem de kotanızı hızla tüketmenin bir yoludur. Yaşam süreli (TTL) küçük bir bellek içi önbellek bunu yumuşatır. Tek süreçli bir servis için sözlük tabanlı bu önbellek yeterlidir; birden çok worker için çağrı noktalarını değiştirmeden sonra onu Redis ile değiştirin.
# app/main.py
import time
from decimal import Decimal, ROUND_HALF_UP
import httpx
from fastapi import Depends, FastAPI, HTTPException, Query
from .client import lifespan, get_client
from .config import settings
from .models import ConversionResult
app = FastAPI(title="FX Service", lifespan=lifespan)
# Simple in-memory cache: {base_currency: (expiry_timestamp, rates_dict)}
_cache: dict[str, tuple[float, dict]] = {}
async def fetch_rates(base: str, client: httpx.AsyncClient) -> dict:
base = base.upper()
now = time.monotonic()
cached = _cache.get(base)
if cached and cached[0] > now:
return cached[1] # cache hit — no network call
try:
resp = await client.get("/latest", params={"base": base})
resp.raise_for_status()
except httpx.HTTPStatusError as exc:
code = exc.response.status_code
if code == 429:
raise HTTPException(503, "Upstream rate limit reached; try again shortly")
if code in (401, 403):
raise HTTPException(502, "Upstream authentication failed")
raise HTTPException(502, "Upstream rates provider error")
except httpx.RequestError:
raise HTTPException(504, "Could not reach rates provider")
rates = resp.json()["rates"]
_cache[base] = (now + settings.cache_ttl_seconds, rates)
return ratesSağlayıcı hatalarının sizin çağıranlarınız için anlamlı HTTP durum kodlarına nasıl çevrildiğine dikkat edin. Sağlayıcıdan gelen bir 429, yeniden deneme ipucuyla bir 503 olur; bir kimlik doğrulama hatası bir 502 olur, böylece kimlik bilgisi ayrıntılarını asla aşağı akışa sızdırmazsınız. Para birimi API'lerinde önbellekleme ve hata yönetimi üzerine tamamlayıcı kılavuzumuz bu desenleri daha derinlemesine ele alır.
Adım 5: /convert ve /rates uç noktaları
Artık uç noktaların kendisi minicik, çünkü tüm zor iş fetch_rates içinde yaşıyor. Sorgu parametrelerini FastAPI'nin Query'siyle doğrular, ondalık açısından güvenli aritmetik yapar ve tip güvenli modeller döndürürüz.
@app.get("/convert", response_model=ConversionResult)
async def convert(
base: str = Query(..., min_length=3, max_length=3),
quote: str = Query(..., min_length=3, max_length=3),
amount: Decimal = Query(..., gt=0),
client: httpx.AsyncClient = Depends(get_client),
):
base, quote = base.upper(), quote.upper()
rates = await fetch_rates(base, client)
if quote not in rates:
raise HTTPException(400, f"Unsupported currency: {quote}")
rate = Decimal(str(rates[quote]))
converted = (amount * rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
return ConversionResult(
base=base, quote=quote, amount=amount,
rate=rate, converted=converted, as_of="latest",
)
@app.get("/health")
async def health():
return {"status": "ok"}Yerelde çalıştırın:
fastapi dev app/main.pyArdından etkileşimli Swagger UI için http://127.0.0.1:8000/docs adresini açın veya uç noktayı doğrudan çağırın:
curl "http://127.0.0.1:8000/convert?base=USD"e=EUR&amount=100"{
"base": "USD",
"quote": "EUR",
"amount": "100",
"rate": "0.92",
"converted": "92.00",
"as_of": "latest"
}Artık çalışan bir para birimi mikroservisiniz var. Kendi dönüşüm aritmetiğinizi çalıştırmamayı tercih ederseniz, Finexly ayrıca barındırılan bir para birimi dönüştürücü ve çağırabileceğiniz doğrudan bir dönüşüm uç noktası da sunar.
Adım 6: Ondalık hassasiyet ve ondalıksız para birimleri hakkında bir not
İki para tuzağı neredeyse her para birimi servisini yakalar:
- Tutarlar için asla ikili kayan nokta kullanmayın. Kayan nokta aritmetiğinde
0.1 + 0.2,0.3değildir. Kurları ve tutarlarıDecimal(str(value))ile ayrıştırın ve son sonucu yukarıdaki gibi kuantalayın. - Her para biriminin iki ondalık basamağı yoktur. ISO 4217 para birimi başına alt birimler tanımlar: JPY ve KRW sıfır ondalık basamağa sahipken, BHD ve KWD üç basamağa sahiptir.
.quantize(Decimal("0.01"))'i sabit kodlamak yen ve dinarı sessizce yanlış yuvarlar. Üretim servisi, her para birimi için doğru üssü aramalı ve buna göre kuantalamalıdır.
Bunu doğru yapmak, bir demo ile gerçek işlemlerin önüne koyabileceğiniz bir şey arasındaki farktır.
Adım 7: Servisi test etmek
HTTP istemcisi bir bağımlılık olarak enjekte edildiğinden testler kolaydır — bağımlılığı bir mock ile geçersiz kılın ve asla ağa dokunmayın:
# test_convert.py
from fastapi.testclient import TestClient
from app.main import app, fetch_rates
async def fake_rates(base, client):
return {"EUR": "0.92", "GBP": "0.79"}
def test_convert(monkeypatch):
monkeypatch.setattr("app.main.fetch_rates", fake_rates)
with TestClient(app) as c:
r = c.get("/convert?base=USD"e=EUR&amount=100")
assert r.status_code == 200
assert r.json()["converted"] == "92.00"TestClient lifespan olaylarını sizin için çalıştırır, böylece paylaşılan istemci tam olarak üretimdeki gibi kurulur ve sökülür.
Üretime doğru: ölçekleme ve hız sınırları
Yayınlamadan önce planlanacak birkaç şey:
- Birden çok worker. Birkaç worker'lı Uvicorn/Gunicorn altında bellek içi önbelleğiniz süreç başınadır. Paylaşılan bir önbellek için Redis'e geçin, böylece bir worker tarafından çekilen bir kur hepsine hizmet eder.
fetch_ratesdikişi bunu tek fonksiyonluk bir değişiklik yapar. - Sağlayıcı kotası. Önbellekleme ilk savunma hattınızdır. 5 dakikalık bir TTL ile, kaç dönüşüm sunarsanız sunun, bir taban para birimi saatte en fazla 12 sağlayıcı çağrısına mal olur. Planınızın sınırlarını fiyatlandırma sayfasında inceleyin ve TTL'yi katmanınıza uygun ayarlayın.
- Geçmiş kurlar. Faturalar, raporlar veya denetim izleri için belirli bir tarihin kurlarını sorgulayan bir
/historicaluç noktası isteyeceksiniz. Aynı istemci ve önbellek desenleri geçerlidir; yalnızca önbelleği(base, date)ile indeksleyin. - Gözlemlenebilirlik. Önbellek isabet/ıska oranlarını ve sağlayıcı gecikmesini kaydedin, böylece TTL'yi gerçek verilerle ayarlayabilirsiniz.
Hâlâ bir sağlayıcı seçiyorsanız, para birimi API'lerini karşılaştırma sayfamız kapsamı, sınırları ve fiyatları yan yana sunar.
Sık Sorulan Sorular
FastAPI'de requests yerine neden httpx kullanmalı?
requests senkrondur ve olay döngüsünü bloke eder, bu da asenkron bir çerçevenin amacını boşa çıkarır. httpx, bağlantı havuzu, zaman aşımları ve yeniden denemeli taşımalar içeren tamamen asenkron bir istemci (httpx.AsyncClient) sunar, böylece diğer istekleri bloke etmeden doğrudan FastAPI'nin async def uç noktalarına takılır.httpx istemcisini istek başına mı yoksa bir kez mi oluşturmalıyım?
Bir kez. Lifespan bağlam yöneticisinde tek birhttpx.AsyncClient oluşturun ve tüm isteklerde yeniden kullanın. İstek başına bir istemci bağlantı havuzunu çöpe atar ve her çağrıda yeni bir TLS el sıkışmasını zorlar; bu yük altında ölçülebilir biçimde daha yavaştır ve soketleri tüketebilir.Para birimi API'sinin hız sınırına ulaşmayı nasıl önlerim?
Sağlayıcı yanıtlarını kısa bir TTL ile (örneğin 5 dakika) önbelleğe alın. Kurlar yavaş hareket ettiğinden, taban para birimi başına önbellekleme binlerce kullanıcı dönüşümünü saatte bir avuç sağlayıcı çağrısına indirir. Çok worker'lı dağıtımlar için önbelleğin süreçler arasında paylaşılması için Redis kullanın.Farklı ondalık basamağa sahip para birimlerini nasıl ele alırım?
Python'unDecimal türünü kullanın, asla float değil, ve sonucu ISO 4217'ye göre her para biriminin doğru alt birim sayısına kuantalayın. JPY ve KRW sıfır ondalık kullanır, çoğu iki kullanır ve BHD/KWD üç kullanır. İki ondalık sabit kodlamak yerine para birimi başına üssü arayın.Bu deseni Flask veya Django gibi senkron bir çerçeve için kullanabilir miyim?
Önbellekleme ve Decimal desenleri doğrudan aktarılır, ancak paylaşılan istemci ve asenkron uç nokta kısımları FastAPI'ye özgüdür. Django için, çerçevenin önbellek arka ucunu ve ORM'sini kullanan özel Django para birimi API eğitimimize bakın.Kurmaya başlayın
Artık FastAPI'de tam, asenkron, önbellek bilinçli bir para birimi mikroservisiniz var — Pydantic ile tip güvenli, Decimal ile güvenli ve sağlayıcı hatalarına dayanıklı. Bir oturuşta okunacak kadar küçük ve gerçek bir üretim servisine büyüyecek kadar yapılandırılmış.
Projenize gerçek zamanlı döviz kurlarını entegre etmeye hazır mısınız? Ücretsiz Finexly API anahtarınızı alın — kredi kartı gerekmez. 170'ten fazla para birimini kapsayan cömert bir ücretsiz katmanla başlayın ve trafiğiniz büyüdükçe ölçeklendirin. Önce keşfetmeyi mi tercih edersiniz? Ücretsiz para birimi API'si genel bakışına göz atın veya API dokümantasyonuna dalın.
Explore More
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 →