Look, I switched from Node.js + Prisma + Next.js because I was tired of 3-second cold starts and Webpack configuration hell. This stack isn't perfect, but it solves the right problems if you can tolerate some ecosystem immaturity. Here's what actually matters when you're trying to ship code instead of tweaking configs all day.
Runtime Foundation with Bun
Bun is fucking fast, but it's not magic. Built on JavaScriptCore instead of V8, it starts up in about 40ms vs Node.js taking 150-200ms. The killer feature? It runs TypeScript files directly without any ts-node
bullshit or separate build steps. Just bun run server.ts
and you're done.
The "all-in-one runtime" marketing is mostly true - it includes a package manager, test runner, and bundler. No more juggling npm + Jest + webpack configs. But here's what they don't tell you: about 15% of npm packages don't work properly. Had to switch away from sharp
because it kept segfaulting, and bcrypt
was broken until version 1.1.3.
Real talk from someone who's migrated 3 production apps: the HTTP server performance boost is legit - saw about 2x improvement in request throughput. Memory usage dropped by ~30% which saves money on AWS Lambda. Independent benchmarks confirm these numbers aren't marketing bullshit. But expect random shit to break, especially with native modules.
Specific compatibility gotchas I hit:
node:crypto
APIs work differently - had to rewrite JWT verification- File system watchers (
chokidar
) crash randomly on macOS sharp
for image processing segfaults - switched to@squoosh/lib
- Some Prisma middleware breaks because of event loop differences
- WebSocket libraries like
ws
work fine, butsocket.io
has weird connection issues
Check Bun's ecosystem compatibility tracker on GitHub - they track which npm packages work and which are fucked. The 2025 runtime comparison guide covers production readiness extensively.
Frontend Layer with React 19
React 19 server components finally work without making you want to throw your laptop out the window. I spent 6 months fighting with the experimental versions - they were a buggy mess that broke every other week. The stable release actually delivers on the promise: server-side data fetching that doesn't completely fuck up your client-side state.
Here's what's actually good: Bun's built-in JSX support means zero webpack configuration. Just write JSX and it works. Server components do reduce bundle sizes - saw a 40% drop in my last project by moving database queries to the server. SEO improved because Google doesn't have to wait for client-side JS to render content.
The Actions API is surprisingly solid for form handling. No more juggling axios + useState + loading states - just useActionState
and you're done. But watch out for hydration mismatches when mixing server and client components. Spent 3 hours debugging why user avatars weren't showing up - turned out the client was rendering before server state hydrated. React 19 troubleshooting guides helped me fix similar issues.
Real hydration gotchas from production:
- Browser extensions (especially ColorZilla) inject attributes that break hydration
- Date/time rendering differs between server and client timezones - use
suppressHydrationWarning
sparingly - Random
useEffect
calls in server components throw "Cannot read properties of undefined" - Third-party scripts loading async can modify DOM before hydration completes
- This Josh Comeau guide saved my ass when nothing else made sense
The React Server Components documentation is actually useful now (unlike the beta docs), and production debugging techniques help when things break. If you're trying to convince your team to adopt this, there's some good analysis of the real concerns developers have with React server components.
Type Safety with TypeScript
TypeScript just works with Bun. No ts-node
, no build steps, no bullshit. Write .ts
files, run bun run
, done. Coming from Node.js where you need 3 different tools just to execute TypeScript, this feels like cheating.
The type safety across the full stack is genuinely game-changing. Database schema changes in Drizzle automatically update your TypeScript types, React components get proper type checking, and API endpoints have end-to-end type safety. Caught a production bug last week where someone changed a database column type but forgot to update the frontend - TypeScript caught it at compile time instead of failing silently in prod.
IDE support is solid - VSCode picks up all the types correctly and auto-completion actually works. Refactoring is less terrifying because you know if you break something, TypeScript will yell at you. Only gotcha: if you're coming from create-react-app
, the tsconfig.json setup is slightly different with Bun. Spent an hour figuring out why imports weren't resolving - needed to add "moduleResolution": "bundler"
to make everything play nice.
The TypeScript handbook covers all the latest features, and the TSConfig reference helps with Bun-specific configuration. For production setups, modern TypeScript project starters provide zero-config templates that actually work.
Database Layer with Drizzle ORM
Drizzle ORM is what Prisma should have been. It's tiny (7.4kb vs Prisma's 145kb), fast, and the generated SQL actually makes sense when you need to debug query performance. No magic, no hidden N+1 queries, just clean SQL that you can read and optimize.
Coming from Prisma, you'll miss the admin UI and the npx prisma studio
command. Drizzle doesn't have that shit, so you're back to writing raw SQL for data exploration or using a separate database GUI. But the performance difference is real - benchmarks show 2-3x faster query execution, and cold starts don't take forever because the ORM isn't loading half the internet.
The schema-first approach is brilliant: define your database schema in TypeScript, run migrations, and get perfect type inference everywhere. Changed a column from nullable to required? TypeScript immediately shows you every place that needs updating. Had zero runtime database errors in production since switching - everything breaks at compile time instead of at 3am when customers are trying to check out.
The comprehensive Drizzle vs Prisma comparison shows why I switched, and TypeScript ORM reviews for 2025 rank Drizzle highly. For implementation, this practical guide covers setup with both SQLite and PostgreSQL.
Only pain point: migrations are more manual than Prisma's db push
. You need to understand SQL and think through schema changes carefully. But honestly, that's a good thing - forces you to understand what you're doing to your database instead of hoping the magic migration generator gets it right.
Migration gotchas that will ruin your day:
drizzle-kit migrate
hangs indefinitely with certain PostgreSQL configurations - restart helps but you lose progress- Missing migration files cause cryptic "schema mismatch" errors hours later
- Order matters - migrations execute randomly on fresh databases if timestamps are close
DO $$
blocks break on some cloud providers (Nile, PlanetScale) - use regular SQL instead- Never delete old migration files - Drizzle loses track of schema history and you're fucked
For debugging these issues, common Drizzle ORM mistakes covers the gotchas I wish I knew earlier, and the Drizzle Kit CLI guide explains migration workflows properly.