- tRPC est un outil qui partage l’inférence TypeScript dans les applications TypeScript full stack, afin de réduire les divergences de types d’API entre le serveur et le client
- Lorsqu’on modifie l’API côté serveur, on peut voir immédiatement l’impact dans le code client grâce aux erreurs TypeScript et à l’autocomplétion, ce qui réduit le coût du refactoring
- Il fonctionne sans schéma distinct ni étape de génération de code, ce qui permet de construire une API type-safe sans alourdir le processus de build ni la charge à l’exécution
- Il fournit des adaptateurs pour plusieurs environnements JavaScript, comme React, Next.js, Express, Fastify, AWS Lambda, Solid et Svelte
- Le flux de base consiste à définir des procédures, créer un serveur HTTP, puis connecter le client en lui passant le type
AppRouter, avec l’objectif d’offrir une expérience où le code du serveur API s’utilise comme un SDK
tRPC change la manière de développer des API
- tRPC améliore la productivité dans les applications full stack en permettant au serveur et au client d’exploiter les mêmes informations de type TypeScript
- Son objectif principal est de faciliter la création d’API type-safe de bout en bout, tout en réduisant le travail répétitif lié aux couches d’API traditionnelles
- Quand un changement côté serveur a un impact sur l’usage côté client, TypeScript signale les erreurs, ce qui permet de repérer rapidement les divergences de types à la frontière client-serveur
Principales caractéristiques perçues par les développeurs
-
Sécurité de type automatique
- Les changements côté serveur qui affectent le code client apparaissent sous forme d’erreurs TypeScript
-
Expérience développeur rapide
- tRPC n’a pas d’étape de build ni de compilation
- Il s’utilise sans génération de code, sans surcharge à l’exécution et sans procédure de build supplémentaire
-
Indépendance vis-à-vis du framework
- Il peut être utilisé dans l’ensemble des frameworks et runtimes JavaScript
- Il s’ajoute facilement à des projets existants
-
Autocomplétion
- Il offre une expérience où le code du serveur API se manipule comme un SDK
- On bénéficie d’aides basées sur les types lors de l’utilisation des endpoints
-
Empreinte client réduite
- tRPC n’a pas de dépendances et sa taille côté client est faible
-
Adaptateurs inclus
- Il fournit des adaptateurs pour React, Next.js, Express, Fastify, AWS Lambda, Solid et Svelte
Flux d’utilisation de base
- Une API tRPC commence par la définition de procédures (procedures)
- Les procédures sont des fonctions qui composent le backend, combinables entre elles, et peuvent prendre la forme de queries, mutations et subscriptions
- Plusieurs procédures sont regroupées dans un routeur
-
Définition des procédures
- Dans l’exemple, on crée une instance tRPC avec
initTRPC.create(), puis on configurerouteretpublicProcedure - La procédure
greetingprend en entrée une chaînenameet renvoie une chaîne au formatHello ${input.name} - La validation des entrées utilise Zod afin que les entrées client correspondent exactement à la forme attendue par la procédure
- À la fin du fichier,
export type AppRouter = typeof appRouter;exporte le type du routeur pour qu’il puisse être utilisé dans le code frontend
- Dans l’exemple, on crée une instance tRPC avec
-
Création du serveur HTTP
- On lance le serveur tRPC en passant
appRouteràcreateHTTPServer - Dans l’exemple, l’API écoute sur le port
3000via l’appel àlisten(3000) - tRPC fournit des adaptateurs pour Next.js, Express, les environnements basés sur la Fetch API, Fastify, AWS Lambda et les serveurs HTTP Node natifs
- Parmi les exemples d’environnements basés sur la Fetch API figurent Astro, Remix, SvelteKit et Cloudflare Workers
- On lance le serveur tRPC en passant
-
Connexion du client et exécution des requêtes
- Une fois le serveur lancé, on peut créer un client avec
createTRPCClient<AppRouter>et interroger les données - Le client de l’exemple utilise
httpBatchLinkpour se connecter àhttp://localhost:3000 - On peut appeler la procédure
greetingdu serveur avectrpc.greeting.query({ name: 'John' }) - En passant le type
AppRouterlors de la création du client, on obtient l’autocomplétion TypeScript et IntelliSense alignés sur l’API backend, sans génération de code
- Une fois le serveur lancé, on peut créer un client avec
Template de démarrage et intention du projet
- Un template prêt à tester est proposé ici : Use this template
- Le créateur de tRPC l’a conçu pour réduire le besoin de couches d’API traditionnelles, permettre d’itérer rapidement tout en gardant la certitude que l’application ne se casse pas
- tRPC est utilisé par des équipes techniques de premier plan et par plusieurs entreprises du Fortune 500
Réactions des développeurs
- Selon plusieurs témoignages de développeurs, tRPC est perçu comme un outil qui améliore la qualité du code, la vitesse de livraison, la satisfaction des développeurs, le refactoring à la frontière client-serveur, la validation des entrées et l’expérience des middlewares typés
- Dans les monorepos TypeScript, il est considéré comme une alternative plus simple que le REST classique ou GraphQL, tout en offrant un typage plus fort
- Un cas d’usage présenté montre que, même si le serveur renvoie une payload de l’API Stripe, les composants React peuvent tout de même recevoir le type des données de réponse
1 commentaires
Avis de Hacker News
Nous sommes en train de retirer tRPC de notre base de code actuelle, et ça a été un cauchemar à cause du couplage fort
Il y avait aussi un côté qui incitait les développeurs juniors à ne pas réfléchir aux interfaces ni aux schémas d’accès aux données, et un mapping direct s’est créé de Prisma jusqu’aux composants
C’est excellent pour prototyper rapidement, mais dès qu’on essaie ensuite de séparer la base de code, on se retrouve très vite dans une impasse
Comme ils s’étendent aussi à des langages autres que TypeScript, cela aide aussi lorsqu’on migre le backend vers un autre langage ou qu’on crée des clients mobiles natifs en Swift ou Kotlin
tRPC est excellent, mais au final ce n’est qu’une couche de transport entre le backend et le frontend
Laisser les structures internes de tRPC pénétrer profondément dans la logique métier est aussi mauvais que de ne pas avoir de couche contrôleur ou routeur qui définisse clairement les entrées, les schémas et la séparation
Donc même si nous quittions tRPC plus tard, je pense que ce serait relativement simple ; par exemple, déplacer tout un sous-système pour qu’il tourne sur une file de messages ne devrait pas être difficile
Il y a une courbe d’apprentissage pour ce genre de choses, et on commence souvent par des types inutiles qui n’expliquent rien, comme
type FunctionIWroteTodayArgs = …Après quelques itérations, on comprend peu à peu que l’objectif n’est pas de dupliquer le code, mais de décrire le domaine et de créer des types, interfaces et API réutilisables et utiles
Donc, à mon avis, plutôt que d’arracher tRPC, il vaut mieux améliorer cet aspect avec l’équipe
Je ne vois pas bien pourquoi déclarer un client HTTP côté serveur et le consommer côté client serait pire
Nous créons des services et utilisons un modèle consommé par toutes les interfaces, comme le web ou la CLI, et le fait que ces éléments ne cassent pas a été une amélioration plus importante que toutes les approches que j’avais vues auparavant
La plainte selon laquelle les développeurs juniors ne réfléchissent pas aux interfaces a de bonnes chances de se produire avec n’importe quelle API, pas seulement avec tRPC
Chez Notion, nous utilisons depuis longtemps un style d’API similaire à tRPC, et cette API existait environ 4 ans avant tRPC
Avec les types mappés de TypeScript, on peut facilement construire cela soi-même. Il suffit de créer un type d’objet dont les clés sont les noms d’API et les valeurs des types
{ request, response }Côté serveur, chaque handler d’API est défini comme une fonction qui prend
APIs["addUser"]["request"]et renvoie unePromise, et côté client on l’expose comme une fonction asynchrone ayant les mêmes arguments et le même type de retourNous utilisons cette stratégie pour des API internes HTTPS, des API temps réel basées sur WebSocket, l’IPC entre Electron et une Webview, ainsi que l’IPC de webview OS entre iOS/Android natif et une Webview
Pour les API natives, comme le serveur est en Swift ou Kotlin, nous réécrivons à la main les types de requête/réponse en TypeScript. Un jour, nous passerons sans doute à un format binaire avec notre propre IDL, mais pour une API unique et cross-language qui grossit progressivement, le coût en expérience développeur de quelque chose comme Protobuf ne nous a pas encore semblé en valoir la peine
Ce n’est pas parfait, mais cela fournit une assez bonne interface d’API avec des types pour les requêtes et les réponses
J’ai aussi créé un wrapper d’API mock d’une dizaine de lignes qu’on peut appeler comme
mockApi((request) => response); il vérifie par types que la fonction mock implémente correctement l’API et renvoie une fonction qui ressemble exactement à la vraie fonction d’API[0]: https://github.com/ferdikoomen/openapi-typescript-codegen
Je pense que ce n’est pas souvent discuté parce qu’il n’existe pas encore de bibliothèque bien maintenue et bien promue qui adopte cette approche
C’est justement une partie importante de la valeur de tRPC, de JSON Schema avec génération de types, ou de Zod
On va jusqu’à demander de fournir explicitement l’argument générique lors de l’appel, ce qui est assez médiocre quand on essaie de garder un arbre de dépendances propre
https://github.com/mikew/transmission-material-ui/blob/maste...
https://github.com/mikew/transmission-material-ui/blob/maste...
J’adore tRPC. Je trouve impressionnant la manière dont il pousse l’expérience développeur jusqu’au bout dans une stack exclusivement TypeScript, et il a amené la communauté GraphQL à prendre conscience des limites et des compromis d’un langage de requête
En même temps, tRPC a traversé son cycle de hype très rapidement, et je n’ai pas l’impression qu’on assiste à une migration massive de REST et GraphQL vers RPC
Cela dit, on voit beaucoup d’intérêt pour RPC ces temps-ci, et dans le framework BFF que nous avons créé (https://wundergraph.com/), nous avons aussi repris certaines idées de tRPC et de l’ancien NextJS pour combiner routage basé sur les fichiers et RPC
En plus de tRPC, nous générons automatiquement un JSON Schema pour chaque opération, ainsi qu’une spécification OpenAPI pour l’ensemble des opérations
Les gens apprécient cette approche, car il est facile de partager un ensemble d’endpoints RPC sous forme de spécification OpenAPI ou de collection Postman. Il y a aussi très peu de débats sur les verbes HTTP ; en pratique, il n’y a que des requêtes, des mutations et des abonnements
Je suis curieux de savoir comment vous utilisez aujourd’hui les API de style GraphQL, REST et RPC, et combien de personnes ou d’équipes interviennent sur vos API
Côté API REST, il y a ts-rest (https://ts-rest.com), zodios (https://www.zodios.org) et Hono (https://hono.dev)
Si votre équipe utilise plusieurs langages, il y a aussi Fern : https://www.buildwithfern.com
Nous générons des types GraphQL à chaque changement du schéma fédéré, et les types de réponse des requêtes et mutations à chaque sauvegarde de fichier
On peut facilement les abstraire avec une bibliothèque de génération de consommateurs d’endpoints, mais à part ça, je ne vois pas vraiment ce qu’on gagne par rapport à une API web classique
De toute façon, il faut quand même faire ce que chaque appel backend doit faire
J’aime tRPC. C’est de loin la meilleure expérience de développement full-stack que j’aie vue jusqu’ici, et avec Zod en particulier, les API deviennent vraiment excellentes
Je considère Zod et tRPC comme des projets importants pour l’avenir de TypeScript, et je pense que, dans les prochaines années, l’écosystème TypeScript verra largement fleurir des expériences développeur inspirées de tRPC
Parmi les projets dont l’ADN tRPC est déjà évident, il y a UploadThing de Ping (https://github.com/pingdotgg/uploadthing), qui vise d’autres cas d’usage, ainsi que notre Lusat (https://github.com/lusatai/lusat)
Le fait que le type des erreurs de validation varie selon le schéma testé est également pénible. Le traitement des erreurs devient imprévisible, avec toujours trop de cas particuliers
Il y a beaucoup de marge d’amélioration
L’écriture de tests unitaires est aussi devenue beaucoup plus facile
Le gros problème semble être le CRUD type-safe et les migrations de données ; Mongoose est souvent mentionné, mais en matière de sûreté de typage, cela donne l’impression d’un net recul par rapport à Zod/TypeScript
Là, j’ai envie de forcer un peu le trait, mais pour certains styles de programmation, on peut presque dire qu’ajouter Zod à une base de code apporte autant de bénéfices qu’ajouter TypeScript
Le pattern qui consiste à partir du schéma pour créer des services typés convient bien aux personnes qui penchent vers la programmation fonctionnelle
Je me demande comment tRPC gère les différences de version et les migrations
Les champs ont un cycle de vie. Lorsqu’ils sont introduits pour la première fois, aucun client ni serveur ne les connaît
Les clients et les serveurs ne sont pas tous redémarrés en une seule fois, et ils n’ont pas non plus la même version des types
Les données sont écrites avec une version du type et lues avec une autre
Ce n’est peut-être pas un problème si l’on peut garantir que tous les binaires, programmes en cours d’exécution et données seront mis à niveau, et s’il n’y a pas de données persistantes
Mais dès que plusieurs organisations sont impliquées, il devient difficile de garantir que toutes les apps et tous les serveurs sont synchronisés sur la dernière version de la bibliothèque, reconstruits et redéployés
La vérification statique des types suppose l’absence de différences de version. Tout le code d’un binaire constitue un monde fermé, compilé avec la même version de la bibliothèque qui définit les types
Zod peut autoriser les champs inconnus, puis on peut passer à des champs optionnels, et enfin les rendre obligatoires une fois la situation stabilisée
Bien sûr, plus un public a envie que tRPC fasse les choses à sa place, moins il est susceptible de réfléchir à ce genre de problème
Si l’on ne vise qu’un seul langage, il est évident qu’on n’a pas besoin de schéma ni de génération de code
J’ai utilisé tRPC dans deux applications web d’environ 50 000 lignes chacune, et je l’aime beaucoup. L’expérience développeur est excellente
Cela dit, la période de hype de tRPC est passée depuis un bon moment, et celle des RSC semble l’avoir rapidement rattrapée
Aujourd’hui, même avec une solution aussi stable et bonne que tRPC, tout le monde ne parle que de savoir s’il faut utiliser les RSC ou non
Je ne suis pas opposé aux RSC, mais il y a trop de discussions à leur sujet. Pour construire des applications aujourd’hui, tRPC reste une approche très pragmatique
Ajout : RSC signifie React Server Components et apporte avec lui sa propre philosophie de lecture/écriture des données
Ce serait bien de définir les sigles que tout le monde ne connaît pas, ou d’ajouter un lien
J’ai utilisé tRPC et Next.js dans quelques projets personnels, et l’expérience a été bonne.
Surtout avec des templates préconfigurés comme Create T3 App (https://create.t3.gg/), il est difficile de battre la vitesse d’itération.
Je ne connaissais pas vraiment tRPC jusqu’à ce qu’un développeur avec qui je travaillais en fasse l’éloge, mais une fois que je l’ai découvert, cela m’a paru être une évidence.
Nous avons construit ensemble une app T3 (tRPC, Next.js, Tailwind, TypeScript, Prisma) ; si vous voulez la voir, elle est ici : https://github.com/stytchauth/stytch-t3-example
Quand on travaille avec TypeScript, une API type-safe est vraiment d’une grande aide.