Building a scalable full-stack web application requires careful planning and strategic decisions from the outset. Every choice, from framework selection to authentication, impacts cost, scalability, and adaptability. This article details the design choices behind the ENT Stack, a reusable full-stack solution built with Express, Next.js, and tRPC.
The ENT Stack aims to be a single, deployable project that shares code between the backend and frontend while allowing independent deployment. It's designed for cost-effectiveness (initially running on low-cost VPS) but with the scalability to handle future growth. To avoid costly refactoring, key decisions were made upfront across thirteen critical areas: framework selection, project structure, API layer, environment and configuration, database, authentication and authorization, validation and error handling, state management, internationalization (i18n), logging, mailing, testing, and DevOps.
The ENT Stack is available as a monorepo on GitHub and an NPM package for easy project scaffolding.
(It's 2025, and revisiting our tech stack is practically a yearly ritual!)
Here's a summary of the key architectural decisions:
A monorepo structure using PNPM workspaces facilitates code sharing between the backend and frontend, offering speed and simplicity over Yarn or NPM workspaces. The directory structure is organized as follows:
<code>apps - backend - ... - frontend - ... packages - shared - config - enums - i18n - schemas - scripts - services - types</code>
tRPC was selected over REST and GraphQL for its developer-friendly approach and end-to-end type safety. Its seamless integration with Next.js and TanStack Query minimizes boilerplate and enhances type inference.
T3 Env ensures static validation of environment variables. Custom config classes manage static settings, while ESM (ECMAScript Modules) supports code sharing between backend and frontend. Tailwind CSS is used for frontend styling.
An ACID-compliant MySQL database was chosen for reliability and transaction guarantees. Drizzle ORM provides a lightweight, type-safe approach to database interactions.
A custom passwordless authentication system offers flexibility and control. Basic frontend route-level protection is implemented using a protected
flag in the routes definition.
Zod handles input validation, while tRPC's errorFormatter
standardizes error handling. Sonner toasts display errors to users. A custom ErrorService
ensures explicit error handling.
Zustand manages simple, synchronous global state, while TanStack Query handles asynchronous state management.
A custom i18n solution using standalone TypeScript functions with ICU syntax (via intl-messageformat
) provides type safety and automatic tree-shaking. Routes are also fully translatable.
Pino provides structured logging for both backend and frontend, offering speed and consistent output.
Resend simplifies email sending, Handlebars creates email templates, and MailSlurp facilitates email testing.
Playwright handles both frontend E2E and backend API testing (integrated with Supertest).
AWS ECS, Terraform, GitHub Actions, S3, and CloudFront manage infrastructure and CI/CD, detailed in a separate repository.
The ENT Stack is open-source and welcomes contributions. Report issues or ask questions via the GitHub repository.
The above is the detailed content of The ENT Stack: Key Decisions for Building a Full-Stack Web App in 5. For more information, please follow other related articles on the PHP Chinese website!