Why we didn't use the "safe" stack
If you're building an email platform in 2024-2025, the conventional choice is Node.js + Express (or Fastify) + React + Prisma. It's battle-tested, well-documented, and every developer knows it.
We went a different direction: Bun + Astro + Drizzle + PostgreSQL. Not because we wanted to be contrarian, but because each choice solved a specific problem better than the conventional option.
Bun as the runtime
Bun replaced Node.js as our runtime, package manager, and test runner. The decision came down to three things:
Startup time
Email infrastructure runs background workers that process queues — bounce handling, webhook delivery, analytics aggregation. These workers need to start fast, especially when scaling up during traffic spikes. Bun's cold start is roughly 4x faster than Node.js for our worker processes.
Native TypeScript
No build step for TypeScript. No ts-node, no tsx, no tsc --watch. You write .ts files and run them directly. This eliminated an entire category of tooling complexity from our development workflow.
Built-in test runner
Bun's test runner is fast and compatible with the Jest API pattern. We run our entire test suite with a single command — no separate test framework to install, configure, or maintain.
The tradeoff: Bun's ecosystem is younger than Node's. We hit edge cases with some npm packages that assumed Node-specific APIs. But for our use case — a server-side application with well-defined dependencies — the gaps were manageable.
Astro for the fullstack app
Astro is primarily known as a static site generator, but its server-side rendering (SSR) mode makes it a capable fullstack framework. We use it for both the public website and the authenticated dashboard.
Why Astro over Next.js
- Islands architecture — React components only hydrate where interactivity is needed. Static content ships as zero-JS HTML, keeping pages fast.
- No client-side routing overhead — page transitions are server-rendered. This keeps the dashboard responsive even on slow connections.
- Clean separation of concerns — presentation, interactivity, and data fetching each have a clear boundary. No blurred lines.
API routing
Astro's file-based routing maps naturally to API design. We have separate route groups for authentication, our public developer API, and internal operations — each with its own middleware and auth requirements. The convention-over-configuration approach keeps things organized as the API surface grows.
PostgreSQL + Drizzle ORM
PostgreSQL was the obvious database choice for an email platform. We need ACID transactions (email sending is not idempotent), complex queries (analytics, filtering, search), and proven reliability at scale.
Why Drizzle over Prisma
- No query engine binary — Prisma ships a Rust-based query engine that adds cold start latency and deployment complexity. Drizzle generates SQL directly.
- Type-safe without code generation overhead — Drizzle infers types from your schema definition. No separate generate step required.
- SQL-like API — if you know SQL, you know Drizzle. The query builder maps 1:1 to SQL concepts.
- Migration control — Drizzle Kit generates SQL migration files that you can review and modify before applying.
Schema design principles
Our database schema follows the email lifecycle: messages move through states from creation to delivery (or failure), with events tracked at each transition. Key design decisions:
- Multi-tenant by design — every query is scoped to an organization. There's no way to accidentally leak data across tenants.
- Event sourcing for email state — instead of updating a status column, we record events. This gives us a complete audit trail and makes debugging delivery issues straightforward.
- Suppression as a first-class concept — bounced and complained addresses are tracked at the organization level, preventing repeated sends to bad addresses.
Two API layers, one codebase
RelayPost serves two distinct audiences: our own dashboard frontend and external developers integrating via the public API. Each has different needs.
The dashboard needs complex, nested data — an email detail view pulls together the message, its delivery events, the sending domain, and aggregate stats. A single flexible query is more efficient than chaining multiple REST calls.
External developers need a simple, predictable REST API with standard conventions — proper HTTP methods, status codes, and JSON responses. No learning curve beyond reading the docs.
We run both from the same codebase, sharing business logic and database access. The difference is the transport layer and authentication method. This keeps behavior consistent while optimizing the interface for each consumer.
Background job processing
Email platforms are inherently async. You accept an email via API, queue it for sending, process the result, handle bounces, deliver webhooks, aggregate analytics. All of this happens in background workers.
Our job queue handles:
- Email sending (priority-based, rate-limited per domain)
- Bounce processing (classify, suppress, retry)
- Webhook delivery (with retries and exponential backoff)
- Analytics aggregation (periodic rollups for dashboard performance)
The worker layer scales independently from the API server. During traffic spikes, we can add worker capacity without touching the web tier. During quiet periods, workers scale down to save resources.
Lessons learned
- Bun is production-ready for server-side applications. The ecosystem gaps are real but shrinking fast. For new projects, it's worth the bet.
- Astro's SSR mode is underrated for fullstack apps. The islands architecture keeps the client bundle small without sacrificing interactivity.
- Drizzle's SQL-first approach pays off when you need complex queries. The learning curve is lower than Prisma if you already know SQL.
- Separating your internal and external API surfaces is worth the upfront effort. Each API serves its audience without compromise.
- Background job processing is the backbone of email infrastructure. Invest in it early — reliable queue processing prevents most operational issues.
If you're building something similar, or just curious about the stack, sign up for RelayPost and see it in action. The best way to evaluate infrastructure is to use it.
Ready to try RelayPost?
AI-native email infrastructure built for developers. Start sending in minutes.
Get Started Free