Skip to main content

Command Palette

Search for a command to run...

bunWay vs Elysia: Which Bun Framework Should You Choose?

Published
6 min read
A

Software Engineer with a background in building scalable web applications and Web3 gaming infrastructure

TL;DR: Both are excellent Bun frameworks with different philosophies. Elysia offers maximum performance and end-to-end type safety with a new API. bunWay offers Express compatibility with zero migration effort. Choose based on your priorities.


You've decided to use Bun. Smart choice—it's fast, has great DX, and the ecosystem is maturing quickly.

But now you need a web framework. Two names keep coming up: Elysia and bunWay.

Let's compare them honestly.

The Philosophies

Elysia: "Reimagine web frameworks for Bun"

Elysia is built from scratch for Bun. It doesn't try to be compatible with anything—it optimizes purely for Bun's strengths.

Key principles:

  • End-to-end type safety
  • Maximum performance
  • Declarative, functional API
  • Plugin-based architecture

bunWay: "Express API at Bun speed"

bunWay recreates the Express experience on Bun. It prioritizes familiarity and migration ease over reinvention.

Key principles:

  • Express API compatibility
  • Zero-rewrite migration
  • Built-in middleware
  • Familiar patterns

Quick Comparison

FeatureElysiabunWay
API StyleNew (functional, typed)Express-compatible
Learning CurveMediumLow (if you know Express)
Type SafetyEnd-to-end inferenceStandard TypeScript
PerformanceMaximumNear-maximum
Migration from ExpressFull rewriteImport changes only
Built-in ValidationYes (TypeBox)Manual
EcosystemElysia pluginsExpress-style middleware

Code Comparison

Basic Route

Elysia:

import { Elysia } from 'elysia'

const app = new Elysia()
  .get('/users/:id', ({ params: { id } }) => ({
    id,
    name: `User ${id}`
  }))
  .listen(3000)

bunWay:

import { bunway, json } from 'bunway'

const app = bunway()
app.use(json())
app.get('/users/:id', (req, res) => {
  res.json({ id: req.params.id, name: `User ${req.params.id}` })
})
app.listen(3000)

Middleware

Elysia:

import { Elysia } from 'elysia'

const app = new Elysia()
  .derive(({ headers }) => {
    const auth = headers.authorization
    return { userId: validateToken(auth) }
  })
  .get('/profile', ({ userId }) => {
    return getUser(userId)
  })

bunWay:

import { bunway } from 'bunway'

const app = bunway()

const authMiddleware = (req, res, next) => {
  const auth = req.headers.authorization
  req.userId = validateToken(auth)
  next()
}

app.get('/profile', authMiddleware, (req, res) => {
  res.json(getUser(req.userId))
})

Validation

Elysia (built-in TypeBox):

import { Elysia, t } from 'elysia'

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

bunWay (manual or with Zod):

import { bunway, json } from 'bunway'
import { z } from 'zod'

const app = bunway()
app.use(json())

const userSchema = z.object({
  name: z.string(),
  email: z.string().email(),
  age: z.number().min(18)
})

app.post('/users', (req, res) => {
  const result = userSchema.safeParse(req.body)
  if (!result.success) {
    return res.status(400).json({ errors: result.error.issues })
  }
  res.json(createUser(result.data))
})

WebSockets

Elysia:

import { Elysia } from 'elysia'

const app = new Elysia()
  .ws('/chat', {
    message(ws, message) {
      ws.send(`Echo: ${message}`)
    }
  })

bunWay:

import { bunway } from 'bunway'

const app = bunway()

app.ws('/chat', {
  message(ws, message) {
    ws.send(`Echo: ${message}`)
  }
})

Performance

Both frameworks are fast because Bun is fast. The framework overhead is minimal compared to the runtime gains.

Realistic benchmarks (JSON response):

FrameworkRequests/secLatency (p99)
Elysia~95,0001.2ms
bunWay~85,0001.4ms
Raw Bun.serve~110,0000.9ms

Elysia is ~10% faster in benchmarks. In real applications with database calls and business logic, this difference becomes negligible.

Choose based on ergonomics, not benchmarks. Both are "fast enough."

Type Safety

Elysia's End-to-End Types

Elysia's killer feature is automatic type inference:

import { Elysia, t } from 'elysia'

const app = new Elysia()
  .post('/users', ({ body }) => {
    // body is automatically typed as { name: string, age: number }
    return { id: 1, ...body }
  }, {
    body: t.Object({
      name: t.String(),
      age: t.Number()
    })
  })

// Client-side with Eden (Elysia's client)
const client = treaty<typeof app>('localhost:3000')
const { data } = await client.users.post({
  name: 'Alice', // TypeScript knows this is required
  age: 25        // TypeScript knows this is required
})
// data is typed as { id: number, name: string, age: number }

This is genuine end-to-end type safety. Your API contract is enforced at compile time.

bunWay's Standard TypeScript

bunWay uses standard TypeScript:

import { bunway, json, Request, Response } from 'bunway'

interface CreateUserBody {
  name: string
  age: number
}

const app = bunway()
app.use(json())

app.post('/users', (req: Request, res: Response) => {
  const body = req.body as CreateUserBody // Manual type assertion
  res.json({ id: 1, ...body })
})

You get TypeScript benefits, but need to manage types manually. No automatic inference across client/server boundaries.

Migration Effort

From Express to bunWay

- import express from 'express'
- import cors from 'cors'
+ import { bunway, cors, json } from 'bunway'

- const app = express()
+ const app = bunway()

- app.use(express.json())
+ app.use(json())

// Routes stay EXACTLY the same
app.get('/users/:id', (req, res) => {
  res.json({ id: req.params.id })
})

Effort: Minutes to hours. Change imports, keep everything else.

From Express to Elysia

- import express from 'express'
- const app = express()
- app.use(express.json())
- app.get('/users/:id', (req, res) => {
-   res.json({ id: req.params.id })
- })
- app.listen(3000)

+ import { Elysia } from 'elysia'
+ const app = new Elysia()
+   .get('/users/:id', ({ params: { id } }) => ({ id }))
+   .listen(3000)

Effort: Days to weeks. Every route, middleware, and pattern changes.

When to Choose Elysia

Choose Elysia if:

  • You're starting fresh — No existing Express code to migrate
  • Type safety is critical — End-to-end types catch bugs at compile time
  • You want built-in validation — TypeBox integration is seamless
  • Performance is paramount — Every microsecond matters
  • You like functional patterns — Chained, declarative API

Elysia shines for new projects where you can fully embrace its patterns.

When to Choose bunWay

Choose bunWay if:

  • You have Express code — Migrate in minutes, not weeks
  • Your team knows Express — No learning curve for the API
  • You want familiar patternsreq, res, next feel like home
  • You need Express middleware patterns — Custom middleware works unchanged
  • Migration speed matters — Get Bun benefits immediately

bunWay shines when you want Bun's speed without rewriting your application.

The Honest Trade-offs

Elysia Trade-offs

Pros:

  • Maximum type safety
  • Slightly better raw performance
  • Modern, clean API design
  • Built-in everything (validation, swagger, etc.)

Cons:

  • Learning curve for new API patterns
  • Full rewrite from Express
  • Smaller ecosystem (Elysia-specific plugins only)
  • Breaking changes as it evolves (still < v1.0 stability)

bunWay Trade-offs

Pros:

  • Zero migration effort from Express
  • Familiar API (Express knowledge transfers)
  • Built-in common middleware
  • Lower risk migration

Cons:

  • No end-to-end type inference
  • Slightly lower raw performance
  • Carries some Express design decisions
  • Manual validation setup

Can You Use Both?

Yes, actually. Some teams use:

  • bunWay for existing services (quick migration)
  • Elysia for new services (best patterns)

They coexist fine since both run on Bun.

My Recommendation

For new projects: Try Elysia first. Its type safety and modern patterns are worth learning.

For existing Express apps: Use bunWay. The migration is painless and you get Bun's speed immediately.

If you're unsure: Start with bunWay. You can always rewrite specific services to Elysia later if you want the extra type safety.

Both are excellent frameworks. The "wrong" choice is still a good choice—you're using Bun either way.


Resources


What framework are you using? Let me know in the comments.

Tags: #bun #elysia #bunway #javascript #typescript #webdev #frameworks #comparison