2 puan yazan GN⁺ 2023-08-13 | 1 yorum | WhatsApp'ta paylaş
  • tRPC, full-stack TypeScript uygulamalarında TypeScript çıkarımını paylaşarak sunucu ile istemci arasındaki API tip uyumsuzluklarını azaltan bir araçtır
  • Sunucu API’si değiştiğinde istemci kodunda etkileri anında TypeScript hataları ve otomatik tamamlama ile görebilirsiniz; bu da refactor yükünü azaltır
  • Ayrı bir şema ya da kod üretimi adımı olmadan çalıştığı için build sürecini ve runtime yükünü artırmadan type-safe API oluşturmayı mümkün kılar
  • React, Next.js, Express, Fastify, AWS Lambda, Solid, Svelte gibi birçok JavaScript ortamına bağlanabilen adaptörler sunar
  • Temel akış; prosedür tanımı, HTTP sunucusu oluşturma ve AppRouter tipini geçirerek istemciyi bağlamadan oluşur; amaç, API sunucu kodunu bir SDK gibi kullanma deneyimidir

tRPC’nin değiştirdiği API geliştirme yaklaşımı

  • tRPC, full-stack uygulamalarda sunucu ve istemcinin aynı TypeScript tip bilgisini kullanmasını sağlayarak üretkenliği artırır
  • Temel amaç, uçtan uca type-safe API’leri kolayca oluşturmak ve geleneksel API katmanlarının getirdiği tekrar eden işleri azaltmaktır
  • Sunucu tarafındaki değişiklikler istemci kullanımını etkilediğinde TypeScript hata verdiği için, istemci-sunucu sınırındaki tip uyumsuzluklarını hızla fark edebilirsiniz

Geliştiricilerin doğrudan hissettiği başlıca özellikler

  • Otomatik tip güvenliği

    • Sunucu tarafındaki değişiklikler istemci kodunu etkilediğinde bu durum TypeScript hataları olarak görünür
  • Hızlı geliştirici deneyimi

    • tRPC’de build veya compile aşaması yoktur
    • Kod üretimi, runtime yükü veya ek build adımları olmadan kullanılabilir
  • Framework bağımsızlığı

    • JavaScript framework’leri ve runtime’larının genelinde kullanılabilir
    • Mevcut projelere de kolayca eklenebilir
  • Otomatik tamamlama

    • API sunucu kodunu bir SDK gibi ele alma deneyimi sunar
    • Endpoint kullanırken tipe dayalı ipuçları alınabilir
  • Küçük istemci ayak izi

    • tRPC’nin bağımlılığı yoktur ve istemci tarafı boyutu küçüktür
  • Dahil adaptörler

    • React, Next.js, Express, Fastify, AWS Lambda, Solid, Svelte vb. için adaptörler sağlar

Temel kullanım akışı

  • tRPC API’si önce bir procedure (prosedür) tanımlayarak başlar
  • Prosedürler backend’i oluşturan fonksiyonlardır; birleştirilebilir ve query, mutation, subscription olarak tanımlanabilir
  • Birden fazla prosedür router içinde gruplanır
  • Prosedür tanımı

    • Örnekte initTRPC.create() ile bir tRPC instance’ı oluşturulup router ve publicProcedure yapılandırılır
    • greeting prosedürü girdi olarak name string’ini alır ve Hello ${input.name} biçiminde bir string döndürür
    • Girdi doğrulaması için Zod kullanılır; böylece istemci girdisinin prosedürün beklediği yapıyla tam olarak eşleşmesi sağlanır
    • Dosyanın sonunda export type AppRouter = typeof appRouter; ile router tipi dışa aktarılır ve frontend kodunda kullanılabilir hale gelir
  • HTTP sunucusu oluşturma

    • tRPC sunucusunu çalıştırmak için createHTTPServer içine appRouter verilir
    • Örnekte API’nin 3000 portunda dinlemesi için listen(3000) çağrılır
    • tRPC; Next.js, Express, Fetch API tabanlı ortamlar, Fastify, AWS Lambda ve vanilla Node HTTP sunucuları için adaptörler sunar
    • Fetch API tabanlı ortamlara Astro, Remix, SvelteKit ve Cloudflare Workers örnek verilir
  • İstemciyi bağlama ve query çalıştırma

    • Sunucu çalıştıktan sonra createTRPCClient<AppRouter> ile istemci oluşturulup veri sorgulanabilir
    • Örnek istemci, httpBatchLink kullanarak http://localhost:3000 adresine bağlanır
    • trpc.greeting.query({ name: 'John' }) gibi bir çağrıyla sunucudaki greeting prosedürü kullanılabilir
    • İstemci oluşturulurken AppRouter tipi verilirse, backend API ile uyumlu TypeScript otomatik tamamlama ve IntelliSense kod üretimi olmadan elde edilir

Başlangıç şablonu ve oluşturulma amacı

  • Doğrudan deneyebilmeniz için Use this template şablonu sunuluyor
  • tRPC’nin geliştiricisi, geleneksel API katmanlarına olan ihtiyacı azaltmak ve hızlı iterasyon yaparken uygulamanın bozulmayacağına dair güven vermek için tRPC’yi geliştirdi
  • tRPC, önde gelen teknik ekipler ve birçok Fortune 500 şirketi tarafından kullanılıyor

Geliştirici tepkileri

  • Çeşitli geliştirici alıntılarında tRPC; kod kalitesini, teslim hızını, geliştirici memnuniyetini, istemci-sunucu sınırındaki refactor süreçlerini, girdi doğrulamasını ve tiplenmiş middleware deneyimini iyileştiren bir araç olarak değerlendiriliyor
  • TypeScript monorepo’larında plain REST veya GraphQL’e göre daha basit ama daha güçlü tipler sunan bir seçenek olarak görülüyor
  • Sunucuda Stripe API payload’ı döndürülse bile React bileşenlerinde yanıt veri tiplerinin alınabildiği bir kullanım örneği de paylaşılıyor

1 yorum

 
GN⁺ 2023-08-13
Hacker News yorumları
  • Şu an mevcut kod tabanından tRPC'yi kaldırma sürecindeyiz; sıkı bağlılık yüzünden kâbus gibiydi.
    Junior geliştiricileri arayüzleri veya veri erişim kalıplarını düşünmemeye iten bir yönü de vardı; Prisma'dan bileşenlere kadar aynen uzanan bir eşleme oluştu.
    Hızlı prototipleme için harika, ama daha sonra kod tabanını ayırmaya çalışınca çok çabuk çıkmaz sokağa giriyorsunuz.

    • Şemaların gözden kaçan avantajı tam da bu. GraphQL gibi şemalar, veri tipleri ve sözleşmeler üzerine düşünmeye zorlar; kodun farklı kısımlarından sorumlu kişilerin uyumlu çalışmasını kolaylaştırır.
      TypeScript dışındaki dillere de genişleyebildiği için, backend'i başka bir dile taşırken veya Swift, Kotlin gibi native mobil istemciler oluştururken de yardımcı olur.
    • Şirkette tRPC'yi benimsedik; biraz ön tasarımla kod içinde “sıkı bağlı” olabilecek şeyleri iyi ayırarak kullanıyoruz.
      tRPC harika, ama sonuçta backend ile frontend arasındaki bir taşıma katmanından ibaret.
      tRPC'nin iç yapısını iş mantığının derinlerine kadar kullandırmak, girdileri, şemaları ve ayrımı net tanımlayan bir controller veya router katmanı olmaması kadar kötü.
      Bu yüzden ileride tRPC'den ayrılsak bile nispeten basit olacağını düşünüyorum; örneğin bütün bir alt sistemi kuyruk üzerinde çalışacak şekilde taşımak da zor olmayacak gibi.
    • Sorun tRPC'den çok, tipleri sadece tip eklemek için kullanan mühendislerin olmasıyla ilgili. Hangi aracı kullanırsanız kullanın aynı sorun ortaya çıkar.
      Bunun bir öğrenme eğrisi var ve genelde type FunctionIWroteTodayArgs = … gibi işe yaramaz, hiçbir şeyi açıklamayan tiplerle başlıyor.
      Birkaç iterasyondan sonra hedefin kodu kopyalamak değil, domain'i açıklayan, yeniden kullanılabilir ve faydalı tipler, arayüzler ve API'ler oluşturmak olduğu yavaş yavaş anlaşılıyor.
      Bu yüzden tRPC'yi söküp atmak yerine, ekiple birlikte bu kısmı iyileştirmenin doğru olduğunu düşünüyorum.
    • Bağlılığın neden kâbus olduğunu biraz daha açıklarsanız iyi olur.
      Sunucu tarafında bir HTTP istemcisi tanımlayıp istemci tarafında tüketmenin neden daha kötü olduğunu pek anlamıyorum.
      Biz servisler oluşturuyoruz ve web arayüzü ya da CLI gibi tüm tüketicilerin kullandığı bir kalıp kullanıyoruz; bunların bozulmaması, eskiden gördüğüm her yaklaşıma göre büyük bir iyileştirmeydi.
    • Küçük bir ekipte tam tersi bir deneyim yaşıyoruz; önceki büyük ekipte de iyi işleyeceğini düşünüyorum.
      Junior geliştiricilerin arayüzleri düşünmediği şikâyeti, yalnızca tRPC'de değil, herhangi bir API kullanıldığında da muhtemelen aynen ortaya çıkar.
  • Notion'da tRPC'ye benzer bir API stilini kullanıyorduk ve bu API, tRPC'den yaklaşık 4 yıl önce vardı.
    TypeScript'in mapped type'larıyla bunu kendiniz kolayca yapabilirsiniz. Anahtarları API adları, değerleri de { request, response } tipinde olan bir nesne tipi oluşturmanız yeterli.
    Sunucu tarafını, her API handler'ını APIs["addUser"]["request"] alıp Promise döndüren bir fonksiyon olarak tanımlarsınız; istemciyi de aynı argüman ve dönüş tiplerine sahip async fonksiyonlar olarak sunarsınız.
    Bu stratejiyi HTTPS internal API'lerde, WebSocket tabanlı gerçek zamanlı API'lerde, Electron ile Webview arasındaki IPC'de, iOS/Android native ile Webview arasındaki OS webview IPC'sinde kullandık.
    Native API'lerde sunucu tarafı Swift veya Kotlin olduğu için request/response tiplerini TypeScript'te elle yeniden yazıyoruz. Bir gün bunu kendi IDL'ine sahip binary bir formata çevireceğiz; ancak yavaş yavaş büyüyen tek bir çapraz dil API'si için Protobuf gibi şeylerin geliştirici deneyimi maliyeti henüz buna değiyor gibi görünmedi.

    • Mevcut proje TypeScript frontend ve Python backend kombinasyonu; OpenAPI şemasını doğruluk kaynağı olarak alıyoruz ve istemci tarafında openapi-typescript-codegen [0] kullanıyoruz.
      Mükemmel değil ama request/response tipleri eklenmiş oldukça iyi bir API arayüzü sağlıyor.
      Ayrıca mockApi((request) => response) gibi çağrılabilen 10 satırlık bir mock API wrapper'ı yaptık; mock fonksiyonun API'yi doğru uygulayıp uygulamadığını tip denetiminden geçiriyor ve gerçek API fonksiyonuyla birebir aynı görünen bir fonksiyon döndürüyor.
      [0]: https://github.com/ferdikoomen/openapi-typescript-codegen
    • Doğru yaklaşım bu. tRPC, sadece tipleri çıkarsamaktan daha fazla gereksiz karmaşıklık ekliyor.
      Bu yaklaşımı benimseyen, iyi bakılan ve tanıtımı yapılan bir kütüphane henüz çıkmadığı için sık tartışılmıyor sanırım.
    • Bu süreçte request data validation işini nasıl yaptığınızı merak ediyorum; bunun için bir püf noktası var mı?
      tRPC, JSON Schema ile tip üretimi, Zod gibi şeyleri kullanırken önemli değer tam da bu kısım.
    • Örnek koda bakınca, bunun XHR çağrılarının üzerine tip eklenmiş bir wrapper'a daha yakın olup olmadığını merak ettim.
      Çağırırken generic argümanı doğrudan vermenizi bile istiyor; dependency tree'yi düzenli tutmaya çalışırken bu epey kötü.
    • Bu yöntem pratikte oldukça iyi çalışıyor.
      https://github.com/mikew/transmission-material-ui/blob/maste...
      https://github.com/mikew/transmission-material-ui/blob/maste...
  • tRPC’yi çok seviyorum. TypeScript’e özel bir stack’te geliştirici deneyimini sınırlarına kadar zorlaması etkileyici; GraphQL topluluğunun da sorgu dilinin sınırlarını ve ödünlerini fark etmesini sağladı.
    Aynı zamanda tRPC gerçekten çok hızlı bir hype döngüsünden geçti ve REST ile GraphQL’den RPC’ye büyük ölçekli bir göç yaşanıyor gibi görünmüyor.
    Yine de bugünlerde RPC’ye ilgi epey yüksek görünüyor; bizim yaptığımız BFF framework’ünde (https://wundergraph.com/) de tRPC’den ve eski NextJS fikirlerinden bazılarını alıp dosya tabanlı routing ile RPC’yi birleştirdik.
    tRPC’ye ek olarak her işlem için otomatik JSON Schema üretiyor, tüm işlem kümesi için de OpenAPI spesifikasyonu oluşturuyoruz.
    RPC endpoint paketlerini OpenAPI spesifikasyonu veya Postman koleksiyonu olarak kolayca paylaşabildikleri için insanlar bu yaklaşımı seviyor. HTTP fiilleri tartışması da neredeyse yok; pratikte yalnızca sorgular, değişiklikler ve abonelikler var.
    Bugünlerde GraphQL, REST ve RPC tarzı API’leri nasıl kullandığınızı, API’lere kaç kişinin ya da kaç ekibin dahil olduğunu merak ediyorum.

    • Garph, GraphQL için tRPC benzeri bir şey: https://garph.dev
      REST API tarafında ts-rest(https://ts-rest.com), zodios(https://www.zodios.org), Hono(https://hono.dev) var.
      Ekipte birden fazla dil kullanılıyorsa Fern de var: https://www.buildwithfern.com
    • “GraphQL topluluğunun sorgu dilinin sınırlarını ve ödünlerini fark etmesini sağladı” kısmını biraz daha açıklarsan iyi olur.
      Biz birleşik şema her değiştiğinde GraphQL tiplerini üretiyoruz; dosya her kaydedildiğinde de sorguların ve değişikliklerin yanıt tiplerini üretiyoruz.
    • Her zaman göze çarpan şey, fiillerin ve yolların epey önemsiz ayrıntılar olması.
      Endpoint tüketicisi üreten kütüphanelerle kolayca soyutlanabilirler; bunun dışında sıradan bir web API’ye kıyasla gerçekte ne kazandığımızı bilmiyorum.
      Sonuçta her backend çağrısında yapılması gereken iş yine aynen yapılmak zorunda.
  • tRPC’yi seviyorum. Şimdiye kadar gördüğüm full-stack geliştirici deneyimleri arasında açık ara en iyisi; özellikle Zod ile birlikte kullanıldığında API gerçekten harika oluyor.
    Zod ve tRPC’nin TypeScript’in geleceğinde önemli projeler olduğunu düşünüyorum; önümüzdeki birkaç yıl boyunca TypeScript ekosistemi genelinde tRPC’den ilham alan geliştirici deneyimlerinin ciddi biçimde yayılacağını sanıyorum.
    tRPC’nin DNA’sını açıkça taşıyan projeler arasında farklı kullanım senaryolarını hedefleyen Ping’in UploadThing’i (https://github.com/pingdotgg/uploadthing) ve bizim Lusat’ımız (https://github.com/lusatai/lusat) var.

    • Zod’u pek sevmiyorum. Tipler ve generic parametreleri zahmetli ve gereğinden uzun; nesne generic’lerine veri tipini veremiyor, Zod’un kendi schema tiplerini özellik olarak vermek zorunda kalıyorsunuz; bu da dağınık ve sezgisel olmayan bir yapı oluşturuyor.
      Ayrıca doğrulama hatalarının tipinin hangi şemayı kontrol ettiğinize göre değişmesi de can sıkıcı. Hata işleme öngörülemez hale geliyor ve istisnai durumlar hep fazla oluyor.
      Geliştirilecek çok yanı var.
    • Yakın zamanda kişisel bir projede tRPC ve Zod kullandım; gerçekten harika bir deneyim olduğuna katılıyorum.
      Birim testleri yazmak da çok daha kolaylaştı.
    • MongoDB tarafında bu alanda araçlar var mı merak ediyorum.
      Büyük sorun tip güvenli CRUD ve veri migrasyonları gibi görünüyor; Mongoose’tan çok bahsediliyor ama tip güvenliği açısından Zod/TypeScript’e göre ciddi bir geri adım gibi hissettiriyor.
    • Zod gerçekten inanılmaz derecede iyi.
      Şu an biraz abartılı konuşmak istiyorum ama belirli bir programlama tarzında codebase’e Zod eklemenin TypeScript eklemek kadar büyük bir kazanç olduğunu bile söyleyebilirim.
    • effect/core ve effect/schema’yı da öneririm.
      Şemadan başlayıp tipli servisler oluşturma deseni, fonksiyonel programlamaya yatkın kişilere iyi uyuyor.
  • tRPC’nin sürüm farklarını ve migrasyonları nasıl ele aldığını merak ediyorum.
    Alanların bir yaşam döngüsü vardır. İlk eklendiklerinde hiçbir istemci veya sunucu o alanı bilmez.
    İstemciler ve sunucular tek seferde hep birlikte yeniden başlatılmaz ve aynı tip sürümüne de sahip olmazlar.
    Veri bir tip sürümüyle yazılıp başka bir tip sürümüyle okunur.
    Tüm ikililerin, çalışan programların ve verinin yükseltileceğini garanti edebiliyorsanız ve kalıcı veri yoksa bu sorun olmayabilir.
    Ama birden fazla organizasyon işin içine girdiği anda tüm uygulamaların ve sunucuların en güncel kütüphane sürümüne senkronize olacağını, yeniden derleneceğini ve yeniden dağıtılacağını garanti etmek zorlaşır.
    Statik tip denetimi sürüm farkı olmadığını varsayar. İkili içindeki tüm kod, tipleri tanımlayan aynı kütüphane sürümüyle derlenmiş kapalı bir dünyadır.

    • Neredeyse “kendin yapman gerekiyor” denebilir.
      Zod bilinmeyen alanlara izin verebilir; ardından bunları opsiyonel alanlara taşıyıp durum istikrara kavuştuğunda zorunlu alan haline getirebilirsiniz.
      Elbette tRPC’nin yaptıklarını en çok isteyen kesim, bu tür sorunları düşünme ihtimali en düşük olan kesimdir.
    • Bu yüzden web uygulamaları için bir araç zaten.
  • Yalnızca tek bir dili hedefliyorsanız şema veya kod üretimine gerek olmaması zaten doğal.

  • tRPC’yi yaklaşık 50 bin satırlık iki web uygulamasında kullandım ve çok sevdim. Geliştirici deneyimi harika.
    Yine de tRPC’nin popülerlik dönemi bir süre önce geçti; RSC hype’ı hızla arayı kapatmış gibi.
    Bugünlerde tRPC gibi istikrarlı ve iyi bir çözüm varken bile herkes yalnızca RSC kullanıp kullanmamayı konuşuyor.
    RSC’ye karşı değilim ama tartışma çok fazla. Günümüzde uygulama geliştirirken tRPC çok pratik bir yol.
    Ek: RSC, React Server Components demek ve beraberinde kendine özgü bir okuma/yazma veri felsefesi getiriyor.

    • RSC’yi hiç duymadım ve aramada da kolay bulunmuyor. Bir bağlantı olsa iyi olurdu.
    • Royal Shakespeare Company’nin RPC ile ne ilgisi olduğunu bilmiyorum.
      İnsanların bilmeyebileceği kısaltmaları tanımlasanız ya da bağlantı verseniz iyi olur.
  • Birkaç kişisel projede tRPC ve Next.js kullandım ve iyi bir deneyimdi
    Özellikle Create T3 App(https://create.t3.gg/) gibi önceden yapılandırılmış şablonlarla birlikte kullanıldığında yineleme hızını yakalamak zor

    • Next 13 ve sunucu bileşenleriyle artık düzgün çalışıp çalışmadığını merak ediyorum
  • Birlikte çalıştığım bir geliştirici tRPC’yi öve öve bitirene kadar bilmiyordum; öğrendikten sonra ise çok bariz şekilde iyi göründü
    Birlikte bir T3 uygulaması (tRPC, Next.js, Tailwind, TypeScript, Prisma) yaptık; görmek isterseniz burada: https://github.com/stytchauth/stytch-t3-example
    TypeScript ile çalışırken tip güvenli API gerçekten çok yardımcı oluyor