We've been looking for many years to improve our product creation process, and one of our major problems was the connection between backend and frontend.
From backend and frontend teams communication issues, to typing issues and difficult error handling, we tried many techniques, without finding a satisfactory solution.
Separating backend and frontend
Data transmission is a complex field in itself. Although the REST model offers a framework and a set of standardized practices, practical implementation can often be more difficult than it seems.
This can makes questions arise such as:
- "How do I manage the query cache?"
- "How to validate input and output data?"
- "How to handle errors granularly?"
- "How to manage pagination?"
REST doesn't necessarily offer ready-made solutions to these concerns, because it can't anticipate the specificities inherent in every project. As a result, we find ourselves improvising or implementing solutions in a hurry.
Let's illustrate this with a typical scenario using REST: imagine a user interface built with React or Vue.js that communicates with a Node.js backend, perhaps on a lightweight framework like Express or a more robust framework like NestJS.
Say our backend transmits a chronologically sorted list of information to our frontend. However, the product manager would like this sorting to be reversed for an antechronological presentation.
In this context, with a frontend developer, a backend developer and a PM, based solely on the REST model, it is often necessary to hold meetings to harmonize changes on both sides. What function should be modified on the backend? What name should be given to the parameter to return the list in chronological order?
After resolving these uncertainties in a meeting, the backend developer makes the necessary changes, updates the REST documentation and sends a revised version to the frontend developer. The latter, in turn, adapts the interface to correctly display the data in the desired order.
This coordination can take several hours of development over several days. And even with this care, not all problems are always anticipated, potentially leading to further complications.
REST, while effective in many cases, is not a silver bullet. It often generates additional costs in terms of development time, team coordination and rigorous documentation. Even with tools like Swagger to standardize this documentation, challenges remain.
With all this knowledge and experience in mind, we started to explore tRPC.
A code base to rule them all?
Fast forward to 2023, no more Vue and welcome (back) React. Although many of our projects continue to use NestJS, we started to use NextJS for our React-based projects.
While looking for new solutions to better integrate our backend with our frontend, we discovered the T3 stack, a complete tech stack for creating web applications in TypeScript. This stack uses tRPC instead of NextJS's native REST API route system.
After initial in-house tests and a production test with Le Piquet de Stream, we quickly realized that tRPC was capable of handling almost all our API needs, and on top of that was finally offering us an answer to our long-standing problems!
A type-safe API for both backend and frontend
The idea behind tRPC is simple: compose your API on the server side and let the library generate TypeScript typing that can be used directly on the front end with either a React-Query integration or a custom client. When a change is made to server-side input or output, TypeScript in your editor will directly tell you where to make changes on the front end. This speeds up the process of adding and modifying features, while reducing the risk of releasing with bugs in the code.
Input validation by default
If tRPC can handle input typing, it's thanks to the Zod validation library. By defining the input schema, the developer defines both typing and server validation rules, simplifying front-end integration and guaranteeing input safety. The icing on the cake is that error handling is automatically taken care of by tRPC!
Automatic caching and batch requests
The tRPC client, through its integration with React-Query, also offers several default optimizations, usually considered a source of complexity and problems when a developer wishes to integrate them manually.
It automatically handles query caching, so if you've already retrieved a list of items for a component, a second call of the list won't create a query to the server and will return the data directly.
A second optimization is to send requests in batches. As one component of latency for a request to the server is the creation of the connection to the server. A process that can take several hundred milliseconds per request! The client will automatically group requests made in a short time frame (e.g. when a page is loaded) and receive the result all at once.
Development cycle is cut down from one month to just a week
Since our transition to tRPC using the T3 stack, we've accelerated our time-to-market without ever sacrificing the quality of our code.
Where it used to take us a month to create a product with separate backend and frontend, wasting hours correcting communication problems between the two, we are now able to create products in a week. We've also noticed a general improvement in the quality of our code.
Gains that actually translate in real-world scenarios
With these solutions, we were able to create products in record time, such as our content management application for the fediverse, Moe. The simplicity of creating an API with tRPC freed up precious time to work on critical points such as implementing login to any Mastodon instance.
We were also able to benefit from lower operating costs than a conventional application, using Vercel's serverless offerings and PlanetScale's database services.
We also occasionally make projects live on Twitch, such as the "tierlist rating" project (available in open source) to experiment with new tools and methods. This project was part of an animation during a charity live for the "World Otter Day 2022" event and had a very tight time constraint.
Thanks to tRPC (and the T3 stack), we were able to create a complete application in less than a week, with a functional backend and frontend deployed on Vercel and the use of UploadThing to manage image uploads as a test of the service for later use.
A community-driven web app that allows a streamer to vote and sort in a tierlist game characters that are chosen by their community
Replays of all the programming sessions are available on our Twitch channel :
Goodbye REST, long live tRPC?
It's undeniable that tRPC contributes to accelerating time-to-market, on the one hand by reducing development constraints between backend and frontend teams, and on the other via the library's technical choices. Full-stack profiles also benefit from a significant advantage in their work, enabling them to correct the front-end themselves.
However, the REST model still has an advantage if your API is to be publicly accessible or used with a third-party client. Even if tRPC can be used as a REST API, the syntax can be difficult to manage without its client.
That's also why we're working on a stack that allows you to use both a T3 application with tRPC and a backend service with NestJS to get the best of both worlds.
Our new TurboRepo based stack with NextJS Frontend and NestJS Backend
Take your product to the next level
Do you need help evaluating your product's technical needs? Our team of experts is here to help you and push your product to the market!
- Article's cover contains work from "ForestWander Nature Photography"