Why Elysia Over Express and Hono

January 25, 2025 · Magnus Rødseth

architectureelysiatypescriptbackendopinion

Why Elysia Over Express and Hono

When choosing a backend framework for Eden Stack, I evaluated the major TypeScript options: Express, Fastify, Hono, and Elysia. Each has its strengths, but Elysia won decisively. Here's why.

The State of Express in 2025

Let's address the elephant in the room: Express isn't dead. After a decade in development, Express v5 finally shipped in October 2024, and v6 is actively being developed. The OpenJS Foundation has revitalized governance with a proper Technical Committee, and the project is moving again.

But "not dead" isn't the same as "the right choice."

Express is 15 years old. It was designed for a JavaScript world that no longer exists — before TypeScript became the standard, before async/await, before edge runtimes, before we expected type safety across the stack. You can make Express work with TypeScript, but it's bolted on, not baked in.

The middleware model that made Express revolutionary in 2010 now feels dated. Request and response types are loose. Error handling is awkward. And while the community is massive, much of that accumulated knowledge is about working around limitations rather than building on strengths.

Express is still fine for quick prototypes or maintaining legacy applications. But for a new production stack in 2025? There are better options.

Hono: The Multi-Runtime Champion

Hono deserves serious consideration. It's fast, lightweight, and runs everywhere — Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda. If multi-runtime deployment is your primary concern, Hono is excellent.

Built on Web Standards (WinterCG compliant), Hono feels modern. The API is clean, middleware is composable, and performance is outstanding. It even has RPC capabilities for type-safe client-server communication.

So why not Hono?

Elysia: End-to-End Type Safety Without Code Generation

Here's where Elysia pulls ahead: Eden Treaty.

Eden Treaty provides end-to-end type safety between your backend and frontend without code generation. You define your API in Elysia, and the types flow automatically to your client. Change a response shape on the server, and TypeScript immediately catches the mismatch on the client.

// Backend (Elysia)
const app = new Elysia()
  .get('/users/:id', ({ params }) => {
    return { id: params.id, name: 'Alice', email: 'alice@example.com' }
  })
 
export type App = typeof app
 
// Frontend (anywhere)
import { treaty } from '@elysiajs/eden'
import type { App } from './api'
 
const api = treaty<App>('localhost:3000')
const { data } = await api.users({ id: '123' }).get()
// data is typed as { id: string, name: string, email: string }

This is similar to tRPC, but for REST-like APIs. No code generation step. No schema files to keep in sync. Just TypeScript inference doing what it does best.

Hono has RPC capabilities too, but Eden Treaty's type inference is deeper. It handles path parameters, query strings, request bodies, and response types with a level of precision that makes full-stack TypeScript development genuinely pleasant.

The Developer Experience Gap

Elysia was designed around developer experience from day one. The documentation calls it an "ergonomic web framework," and that's not marketing fluff — it's a design philosophy.

Validation is built-in:

const app = new Elysia()
  .post('/users', ({ body }) => createUser(body), {
    body: t.Object({
      name: t.String(),
      email: t.String({ format: 'email' })
    })
  })

The schema validates at runtime and provides TypeScript types at compile time. One definition, two purposes. Hono can do this with Zod middleware, but it's an addition rather than a core feature.

Lifecycle hooks are intuitive:

const app = new Elysia()
  .onBeforeHandle(({ headers }) => {
    if (!headers.authorization) {
      return new Response('Unauthorized', { status: 401 })
    }
  })
  .get('/protected', () => 'secret data')

Plugin composition is elegant:

const authPlugin = new Elysia()
  .derive(({ headers }) => ({
    user: validateToken(headers.authorization)
  }))
 
const app = new Elysia()
  .use(authPlugin)
  .get('/me', ({ user }) => user) // user is typed!

Performance: Both Are Fast

Let's be honest: for most applications, both Elysia and Hono are fast enough that performance isn't the deciding factor. Benchmarks show them trading leads depending on the specific test case, but both dramatically outperform Express.

Elysia was originally built specifically for Bun, taking advantage of Bun-native optimizations. It now also supports Node.js and Cloudflare Workers, though Bun remains its sweet spot.

Hono was originally built for Cloudflare Workers and has broader multi-runtime parity.

If you're deploying to Cloudflare Workers and need every microsecond, Hono might edge ahead. If you're running Bun, Elysia has the home-field advantage. For most real-world applications, both are "fast enough" and the choice should be made on other criteria.

When to Choose What

Choose Express when:

  • You're maintaining a legacy codebase
  • Your team knows Express deeply and switching isn't worth the cost
  • You need a specific Express middleware that has no equivalent

Choose Hono when:

  • Multi-runtime deployment is critical (especially Cloudflare Workers)
  • You want the lightest possible footprint
  • You're building serverless functions that need to cold-start fast

Choose Elysia when:

  • End-to-end type safety is a priority
  • You're using Bun (or plan to)
  • Developer experience matters more than ecosystem size
  • You want validation, OpenAPI docs, and type inference built-in

Why Eden Stack Uses Elysia

For a full-stack TypeScript template, the choice was clear. Eden Stack is about type safety from database to UI, and Eden Treaty makes that vision real for the API layer.

Combined with TanStack Start on the frontend, you get:

  1. Type-safe database queries (Drizzle)
  2. Type-safe API definitions (Elysia)
  3. Type-safe API calls (Eden Treaty)
  4. Type-safe routing and data loading (TanStack Router)

No code generation. No runtime type checking overhead. Just TypeScript doing what TypeScript does best.

The Elysia + Eden Treaty combination isn't just a technical choice — it's a statement about how modern full-stack development should work. Types should flow. Changes should propagate. The compiler should catch mistakes before users do.

That's why Eden Stack uses Elysia.


This post reflects my opinions after evaluating and building with multiple backend frameworks. Your requirements may differ, and that's okay — there's no single "best" framework for every situation.

Ready to build with Eden Stack?

One-time payment. Full source code. No lock-in.

View pricing