Ana içeriğe geç

SDK Üretme

🌐 Yapay Zekâ ve İnsanlar Tarafından Çeviri

Bu çeviri, insanlar tarafından yönlendirilen bir yapay zekâ ile oluşturuldu. 🤝

Orijinal anlamın yanlış anlaşılması ya da kulağa doğal gelmeme gibi hatalar içerebilir. 🤖

Yapay zekâyı daha iyi yönlendirmemize yardımcı olarak bu çeviriyi iyileştirebilirsiniz.

İngilizce sürüm

FastAPI, OpenAPI spesifikasyonunu temel aldığı için API'leri birçok aracın anlayabildiği standart bir formatta tanımlanabilir.

Bu sayede güncel dokümantasyon, birden fazla dilde istemci kütüphaneleri (SDKs) ve kodunuzla senkron kalan test veya otomasyon iş akışları üretmek kolaylaşır.

Bu rehberde, FastAPI backend'iniz için bir TypeScript SDK üretmeyi öğreneceksiniz.

Açık Kaynak SDK Üreteçleri

Esnek bir seçenek olan OpenAPI Generator, birçok programlama dilini destekler ve OpenAPI spesifikasyonunuzdan SDK üretebilir.

TypeScript client'lar için Hey API, TypeScript ekosistemi için özel olarak tasarlanmış, optimize bir deneyim sunan bir çözümdür.

Daha fazla SDK üretecini OpenAPI.Tools üzerinde keşfedebilirsiniz.

İpucu

FastAPI otomatik olarak OpenAPI 3.1 spesifikasyonları üretir; bu yüzden kullanacağınız aracın bu sürümü desteklemesi gerekir.

FastAPI Sponsorlarından SDK Üreteçleri

Bu bölüm, FastAPI'yi sponsorlayan şirketlerin sunduğu yatırım destekli ve şirket destekli çözümleri öne çıkarır. Bu ürünler, yüksek kaliteli üretilen SDK'ların üzerine ek özellikler ve entegrasyonlar sağlar.

FastAPI'ye sponsor olarak ✨ bu şirketler, framework'ün ve ekosisteminin sağlıklı ve sürdürülebilir kalmasına yardımcı olur.

Sponsor olmaları aynı zamanda FastAPI topluluğuna (size) güçlü bir bağlılığı da gösterir; yalnızca iyi bir hizmet sunmayı değil, aynı zamanda güçlü ve gelişen bir framework olan FastAPI'yi desteklemeyi de önemsediklerini gösterir. 🙇

Örneğin şunları deneyebilirsiniz:

Bu çözümlerin bazıları açık kaynak olabilir veya ücretsiz katman sunabilir; yani finansal bir taahhüt olmadan deneyebilirsiniz. Başka ticari SDK üreteçleri de vardır ve internette bulunabilir. 🤓

TypeScript SDK Oluşturma

Basit bir FastAPI uygulamasıyla başlayalım:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float


class ResponseMessage(BaseModel):
    message: str


@app.post("/items/", response_model=ResponseMessage)
async def create_item(item: Item):
    return {"message": "item received"}


@app.get("/items/", response_model=list[Item])
async def get_items():
    return [
        {"name": "Plumbus", "price": 3},
        {"name": "Portal Gun", "price": 9001},
    ]

Path operation'ların, request payload ve response payload için kullandıkları modelleri Item ve ResponseMessage modelleriyle tanımladıklarına dikkat edin.

API Dokümanları

/docs adresine giderseniz, request'lerde gönderilecek ve response'larda alınacak veriler için schema'ları içerdiğini görürsünüz:

Bu schema'ları görebilirsiniz, çünkü uygulamada modellerle birlikte tanımlandılar.

Bu bilgi uygulamanın OpenAPI schema'sında bulunur ve sonrasında API dokümanlarında gösterilir.

OpenAPI'ye dahil edilen, modellerden gelen bu bilginin aynısı client code üretmek için kullanılabilir.

Hey API

Modelleri olan bir FastAPI uygulamamız olduğunda, Hey API ile bir TypeScript client üretebiliriz. Bunu yapmanın en hızlı yolu npx kullanmaktır.

npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client

Bu komut ./src/client içine bir TypeScript SDK üretecektir.

Web sitelerinde @hey-api/openapi-ts kurulumunu öğrenebilir ve üretilen çıktıyı inceleyebilirsiniz.

SDK'yı Kullanma

Artık client code'u import edip kullanabilirsiniz. Şuna benzer görünebilir; method'lar için otomatik tamamlama aldığınıza dikkat edin:

Ayrıca gönderilecek payload için de otomatik tamamlama alırsınız:

İpucu

name ve price için otomatik tamamlamaya dikkat edin; bunlar FastAPI uygulamasında, Item modelinde tanımlanmıştı.

Gönderdiğiniz veriler için satır içi hatalar (inline errors) da alırsınız:

Response objesi de otomatik tamamlama sunacaktır:

Tag'lerle FastAPI Uygulaması

Birçok durumda FastAPI uygulamanız daha büyük olacaktır ve farklı path operation gruplarını ayırmak için muhtemelen tag'leri kullanacaksınız.

Örneğin items için bir bölüm, users için başka bir bölüm olabilir ve bunları tag'lerle ayırabilirsiniz:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float


class ResponseMessage(BaseModel):
    message: str


class User(BaseModel):
    username: str
    email: str


@app.post("/items/", response_model=ResponseMessage, tags=["items"])
async def create_item(item: Item):
    return {"message": "Item received"}


@app.get("/items/", response_model=list[Item], tags=["items"])
async def get_items():
    return [
        {"name": "Plumbus", "price": 3},
        {"name": "Portal Gun", "price": 9001},
    ]


@app.post("/users/", response_model=ResponseMessage, tags=["users"])
async def create_user(user: User):
    return {"message": "User received"}

Tag'lerle TypeScript Client Üretme

Tag'leri kullanan bir FastAPI uygulaması için client ürettiğinizde, genelde client code da tag'lere göre ayrılır.

Bu sayede client code tarafında her şey doğru şekilde sıralanır ve gruplandırılır:

Bu örnekte şunlar var:

  • ItemsService
  • UsersService

Client Method İsimleri

Şu an üretilen createItemItemsPost gibi method isimleri çok temiz görünmüyor:

ItemsService.createItemItemsPost({name: "Plumbus", price: 5})

...çünkü client üreteci, her path operation için OpenAPI'nin dahili operation ID değerini kullanır.

OpenAPI, her operation ID'nin tüm path operation'lar arasında benzersiz olmasını ister. Bu yüzden FastAPI; operation ID'yi benzersiz tutabilmek için function adı, path ve HTTP method/operation bilgilerini birleştirerek üretir.

Ancak bunu bir sonraki adımda nasıl iyileştirebileceğinizi göstereceğim. 🤓

Özel Operation ID'ler ve Daha İyi Method İsimleri

Bu operation ID'lerin üretilme şeklini değiştirerek, client'larda daha basit method isimleri elde edebilirsiniz.

Bu durumda, her operation ID'nin benzersiz olduğundan başka bir şekilde emin olmanız gerekir.

Örneğin, her path operation'ın bir tag'i olmasını sağlayabilir ve operation ID'yi tag ve path operation adına (function adı) göre üretebilirsiniz.

Benzersiz ID Üreten Özel Fonksiyon

FastAPI, her path operation için bir unique ID kullanır. Bu ID, operation ID için ve ayrıca request/response'lar için gerekebilecek özel model isimleri için de kullanılır.

Bu fonksiyonu özelleştirebilirsiniz. Bir APIRoute alır ve string döndürür.

Örneğin burada ilk tag'i (muhtemelen tek tag'iniz olur) ve path operation adını (function adı) kullanıyor.

Sonrasında bu özel fonksiyonu generate_unique_id_function parametresiyle FastAPI'ye geçebilirsiniz:

from fastapi import FastAPI
from fastapi.routing import APIRoute
from pydantic import BaseModel


def custom_generate_unique_id(route: APIRoute):
    return f"{route.tags[0]}-{route.name}"


app = FastAPI(generate_unique_id_function=custom_generate_unique_id)


class Item(BaseModel):
    name: str
    price: float


class ResponseMessage(BaseModel):
    message: str


class User(BaseModel):
    username: str
    email: str


@app.post("/items/", response_model=ResponseMessage, tags=["items"])
async def create_item(item: Item):
    return {"message": "Item received"}


@app.get("/items/", response_model=list[Item], tags=["items"])
async def get_items():
    return [
        {"name": "Plumbus", "price": 3},
        {"name": "Portal Gun", "price": 9001},
    ]


@app.post("/users/", response_model=ResponseMessage, tags=["users"])
async def create_user(user: User):
    return {"message": "User received"}

Özel Operation ID'lerle TypeScript Client Üretme

Artık client'ı tekrar üretirseniz, geliştirilmiş method isimlerini göreceksiniz:

Gördüğünüz gibi method isimleri artık önce tag'i, sonra function adını içeriyor; URL path'i ve HTTP operation bilgisini artık taşımıyor.

Client Üretecine Vermeden Önce OpenAPI Spesifikasyonunu Ön İşlemek

Üretilen kodda hâlâ bazı tekrarlanan bilgiler var.

Bu method'un items ile ilişkili olduğunu zaten biliyoruz; çünkü bu kelime ItemsService içinde var (tag'den geliyor). Ama method adında da tag adı önek olarak duruyor. 😕

OpenAPI genelinde muhtemelen bunu korumak isteriz; çünkü operation ID'lerin benzersiz olmasını sağlar.

Ancak üretilen client için, client'ları üretmeden hemen önce OpenAPI operation ID'lerini değiştirip, method isimlerini daha hoş ve temiz hale getirebiliriz.

OpenAPI JSON'u openapi.json diye bir dosyaya indirip, şu tarz bir script ile öndeki tag'i kaldırabiliriz:

import json
from pathlib import Path

file_path = Path("./openapi.json")
openapi_content = json.loads(file_path.read_text())

for path_data in openapi_content["paths"].values():
    for operation in path_data.values():
        tag = operation["tags"][0]
        operation_id = operation["operationId"]
        to_remove = f"{tag}-"
        new_operation_id = operation_id[len(to_remove) :]
        operation["operationId"] = new_operation_id

file_path.write_text(json.dumps(openapi_content))
import * as fs from 'fs'

async function modifyOpenAPIFile(filePath) {
  try {
    const data = await fs.promises.readFile(filePath)
    const openapiContent = JSON.parse(data)

    const paths = openapiContent.paths
    for (const pathKey of Object.keys(paths)) {
      const pathData = paths[pathKey]
      for (const method of Object.keys(pathData)) {
        const operation = pathData[method]
        if (operation.tags && operation.tags.length > 0) {
          const tag = operation.tags[0]
          const operationId = operation.operationId
          const toRemove = `${tag}-`
          if (operationId.startsWith(toRemove)) {
            const newOperationId = operationId.substring(toRemove.length)
            operation.operationId = newOperationId
          }
        }
      }
    }

    await fs.promises.writeFile(
      filePath,
      JSON.stringify(openapiContent, null, 2),
    )
    console.log('File successfully modified')
  } catch (err) {
    console.error('Error:', err)
  }
}

const filePath = './openapi.json'
modifyOpenAPIFile(filePath)

Bununla operation ID'ler items-get_items gibi değerlerden sadece get_items olacak şekilde yeniden adlandırılır; böylece client üreteci daha basit method isimleri üretebilir.

Ön İşlenmiş OpenAPI ile TypeScript Client Üretme

Sonuç artık bir openapi.json dosyasında olduğuna göre, input konumunu güncellemeniz gerekir:

npx @hey-api/openapi-ts -i ./openapi.json -o src/client

Yeni client'ı ürettikten sonra, tüm otomatik tamamlama, satır içi hatalar, vb. ile birlikte temiz method isimleri elde edersiniz:

Faydalar

Otomatik üretilen client'ları kullanınca şu alanlarda otomatik tamamlama elde edersiniz:

  • Method'lar.
  • Body'deki request payload'ları, query parametreleri, vb.
  • Response payload'ları.

Ayrıca her şey için satır içi hatalar (inline errors) da olur.

Backend kodunu her güncellediğinizde ve frontend'i yeniden ürettiğinizde, yeni path operation'lar method olarak eklenir, eskileri kaldırılır ve diğer değişiklikler de üretilen koda yansır. 🤓

Bu, bir şey değiştiğinde client code'a otomatik olarak yansıyacağı anlamına gelir. Ayrıca client'ı build ettiğinizde, kullanılan verilerde bir uyuşmazlık (mismatch) varsa hata alırsınız.

Böylece üretimde son kullanıcılara hata yansımasını beklemek ve sonra sorunun nerede olduğunu debug etmeye çalışmak yerine, geliştirme sürecinin çok erken aşamalarında birçok hatayı tespit edersiniz. ✨