Create a Full-Stack app with tRPC, Express and React

Building Fullstack Web Apps with TER: A Focus on Developer Experience
Developing a web application can be complex, especially with the countless tools and libraries available today. With TER, you’re not just getting a boilerplate; you’re tapping into a stack that prioritizes simplicity, efficiency, and speed, specifically for developers who want a seamless experience. This project is an excellent example of how modern tooling can be used to create an optimized developer workflow without sacrificing quality or performance.
Motivation Behind TER
The primary goal behind TER is to enhance the developer experience. We focused on creating a setup that’s simple to understand, efficient to use, and fast to work with. By selecting top-tier libraries, TER minimizes configuration overhead and maximizes productivity.
A Different Approach from the T3 Stack
Many boilerplates, like the popular T3 stack, utilize frameworks such as Next.js to manage both the frontend and backend. TER takes a different approach. We chose not to include Next.js, allowing the frontend to be compiled into static files that can easily be stored in cloud object storage solutions like AWS S3. This approach brings several advantages:
- Simpler Hosting Options: By generating static files for the frontend, the app can be deployed to any static file host, making cloud storage an easy and cost-effective option.
- Focus on Web Apps: TER is built specifically for web applications rather than traditional, SEO-optimized websites. By opting out of Next.js, TER is ideal for internal applications, dashboards, or tools where SEO isn’t a priority but functionality and developer productivity are.
Why This Stack?
The technologies in TER were selected to support a fullstack application that developers can quickly understand and extend. Here’s a breakdown of why each component was chosen and how it contributes to a seamless development experience:
- tRPC: Enables type-safe, end-to-end API calls between the client and server, allowing developers to build APIs without worrying about schema mismatches or redundant validations.
- Express: As a minimalist Node.js framework, Express provides all the essential features without unnecessary complexity, giving developers full control over their backend.
- React: Powers a dynamic and responsive frontend, making it easy to build interactive UIs. React’s flexibility aligns well with TER’s goal of providing a highly customizable developer experience.
- Drizzle ORM: Ensures type safety at the database level and makes data interactions smooth and error-free. Drizzle’s TypeScript-first design is a great fit for TER’s fullstack, type-safe approach.
Key Features of TER
TER is more than just a collection of tools; it offers built-in functionality to handle common application needs, from user authentication to data management.
- JWT Authentication with HttpOnly Cookies: Offers secure, stateless authentication with cookies that cannot be accessed via JavaScript, enhancing protection against XSS attacks.
- External API Integrations: Examples like Beers, Users, Movies, and Albums APIs demonstrate how to retrieve data from external sources, a common requirement in many web applications.
- Health Check Endpoint: The `/health` endpoint allows for quick monitoring of server status, which can be critical in production.
- Dynamic Table Row Display: Gives users control over the number of rows displayed in tables, improving the experience for data-heavy applications.
Why Drizzle?
While I previously loved using Prisma for its amazing user experience, I found it started to fall short as my projects scaled. The SQL queries generated by Prisma were not optimized, and there was no concept of SQL-level joins, which presented challenges. Additionally, Prisma proved problematic for serverless deployments, as I would frequently hit the connection pool limit unless I paid for their Prisma Accelerate plan. This added a significant 15MB Prisma Rust query engine to my Lambda functions, which was a lot given the size constraints.
From Prisma to Drizzle: My Journey to a More Scalable ORM
References like the CodeDamn article, the YouTube video, and the Reddit discussion highlighted these issues and pushed me to explore alternative solutions. Ultimately, I decided to switch to Drizzle, which has provided a more optimized SQL experience and better fit for my serverless architecture.
NPM Workspace in a monorepo: Let’s make it simple again
In my previous projects, I faced several challenges with sharing code and managing fullstack apps. For example:
- Sharing code: GitHub submodules and npm private packages were used to share files between the client and server, but both approaches had drawbacks. Submodules cluttered my workflow with too many repos in VS Code, while npm packages moved the source of truth away from GitHub.
- Versioning issues: Managing versions was a constant struggle. With submodules, it was tedious to align versions across repos, and with npm packages, tracking changes became complex since updates weren’t directly tied to the main codebase.
- Pull request overload: A single fullstack feature often required multiple pull requests (one per repo), significantly slowing down development and review cycles.

Switching to NPM Workspaces solved these problems by centralizing everything in a monorepo. It enabled seamless code sharing, simplified version control, and reduced the overhead of managing pull requests — all while keeping the source of truth in GitHub.
Developer Experience: Efficiency Meets Flexibility
TER is designed to be developer-friendly from the ground up. By combining TypeScript with tRPC, Drizzle, and Express, we’ve created a stack that avoids redundant code and minimizes the likelihood of runtime errors.

Why I Fell in Love with tRPC: A Developer’s Dream
I’ve spent years wrestling with the complexities of REST and GraphQL APIs. While they’ve been the industry standards, I’ve often found them to be cumbersome and challenging to maintain. Enter tRPC, a game-changer that has completely transformed my approach to API development.
What initially drew me to tRPC was its type-safety. By leveraging TypeScript, tRPC ensures that both the frontend and backend share a single source of truth for data types. This eliminates a significant source of errors and significantly improves developer productivity. Additionally, its intuitive syntax and powerful features, such as data fetching, mutations, and subscriptions, make it a breeze to implement complex API interactions.
In conclusion, tRPC has become an indispensable tool in my development toolkit. Its type-safety, ease of use, and powerful features have made it a joy to work with. If you’re tired of the complexities of traditional API development, I highly recommend giving tRPC a try. You won’t be disappointed.
End-to-end typesafe with Trpc

- End-to-End Type Safety: With TypeScript and tRPC, you don’t need to worry about mismatched types between the frontend and backend. This reduces debugging time and boosts development speed.
- Fast Iteration with Vite: Vite speeds up frontend development with hot module replacement, making the frontend development experience smooth and responsive.
- Customizable and Extendable: TER’s structure is highly customizable, allowing developers to add new routes, middleware, or components with minimal configuration. This extensibility makes TER a robust starting point for any fullstack web application.
The Ideal Use Case for TER
TER’s approach is tailored to fullstack web apps where SEO isn’t a priority. Its setup is ideal for applications like:
- Internal Tools: Create dashboards, reporting tools, or administrative panels.
- Data-Heavy Apps: Apps that rely on retrieving, displaying, and managing large datasets can benefit from TER’s flexibility and speed.
- Prototyping: TER is perfect for MVPs or proof-of-concept apps that need to be developed quickly.
Installation: Quick and Straightforward
Full documentation is here.
One of TER’s main goals is to simplify the development process, so getting started with TER is as easy as possible. Follow these steps to set up the project locally:
- Database Creation: Ensure that a Postgres database is running and create a database called `ter`.
- Environment Configuration: Update `env.ts` with your database credentials.
- Install Dependencies: Run `npm i` in the root directory.
- Run Database Migrations: Execute `npm run push` to set up the database schema.
- Seed the Database: Populate the database with sample data using `npm run seed`.
- Run the App: Start the development server with `npm run dev`. This will launch both the backend and frontend.
In Summary
TER is more than just a fullstack boilerplate; it’s a toolkit that emphasizes developer experience, efficiency, and speed. By taking a different approach from traditional stacks like T3, TER provides developers with the flexibility to create powerful web applications without the constraints of SEO requirements or tightly coupled frameworks. With its type-safe, modern tech stack, TER is ready to handle any challenge in web app development.
The project is here: https://github.com/alan345/TER
Don’t forget to add a star to this repo if you find it useful!
In Plain English 🚀
Thank you for being a part of the In Plain English community! Before you go:
- Be sure to clap and follow the writer ️👏️️
- Follow us: X | LinkedIn | YouTube | Discord | Newsletter | Podcast
- Create a free AI-powered blog on Differ.
- More content at PlainEnglish.io