Skip to main content

Encore.ts vs Hono vs Elysia (2026)

·StarterPick Team
Share:

TL;DR

Hono is the pragmatic choice — it runs everywhere (Workers, Bun, Deno, Node), has the largest ecosystem, and plenty of SaaS starters. Elysia is the fastest option on Bun with an excellent ergonomics story. Encore.ts is the most opinionated — it generates infrastructure as code from TypeScript decorators and manages your cloud deployment. Pick Hono for flexibility, Elysia for Bun performance, Encore for managed infrastructure without Terraform.

Key Takeaways

  • Hono runs on 5+ runtimes (Workers, Bun, Deno, Node, Lambda) — widest deployment flexibility
  • Elysia is Bun-first, uses Eden Treaty for end-to-end type safety rivaling tRPC
  • Encore.ts generates AWS/GCP infrastructure from TypeScript annotations — no Terraform needed
  • All three are faster than Express in benchmarks; Elysia + Bun is the fastest overall
  • Starter ecosystems: Hono > Elysia > Encore for SaaS-specific templates
  • Best pairing: Hono + D1 (edge), Elysia + Bun (performance), Encore (full-stack managed infra)

The Three Frameworks

HonoElysiaEncore.ts
RuntimeAny JS runtimeBun-firstNode.js (cloud backend)
PhilosophyMicro, composableErgonomic, fastInfrastructure-as-code
E2E TypesVia OpenAPI/RPCEden TreatyEncore client gen
Stars20k+11k+8k+
SaaS starters20+5-102-3
Best forEdge/multi-runtimeBun API serversCloud-native apps
DeploymentWorker/VPS/LambdaBun serversEncore Cloud or AWS/GCP

Hono — The Multi-Runtime Champion

Founded: 2022 | Creator: Yusuke Wada | Stars: 20k+

Hono started as a Cloudflare Workers framework and evolved into the web framework that runs anywhere. The API surface is Express-like but modernized with TypeScript-first design, built-in middleware, and a tiny bundle size.

Why SaaS Teams Choose Hono

import { Hono } from "hono";
import { zValidator } from "@hono/zod-validator";
import { z } from "zod";

const app = new Hono();

// Type-safe request validation
const createProjectSchema = z.object({
  name: z.string().min(1).max(100),
  slug: z.string().regex(/^[a-z0-9-]+$/),
});

app.post(
  "/projects",
  zValidator("json", createProjectSchema),
  async (c) => {
    const { name, slug } = c.req.valid("json"); // typed!
    const project = await db.insert(projects).values({ name, slug, userId: c.var.userId });
    return c.json(project, 201);
  }
);

// RPC — call from client with full types
// Using hono/client
import type { AppType } from "./server";
import { hc } from "hono/client";

const client = hc<AppType>("http://localhost:3000");
const res = await client.projects.$post({ json: { name: "My SaaS", slug: "my-saas" } });

Hono SaaS Starters

# Cloudflare Workers (most popular)
npm create cloudflare@latest my-api -- --type=hono

# Bun
bun create hono my-api --template bun

# Node.js
npm create hono my-api -- --template nodejs

# Vercel
npm create hono my-api -- --template vercel

Community SaaS starters:

  • hono-saas-starter — Workers + D1 + Clerk + Stripe
  • hono-drizzle-starter — Bun + Hono + Drizzle + Postgres
  • Multiple "hono + shadcn" full-stack starters on GitHub

Hono Strengths

  • ✅ Deploy anywhere — migrate from Workers to Bun to Lambda without rewriting
  • ✅ Large middleware ecosystem (@hono/zod-validator, @hono/clerk-auth, @hono/cors)
  • ✅ Most SaaS starters of the three
  • ✅ Excellent documentation

Hono Weaknesses

  • ❌ No built-in database abstraction
  • ❌ Type inference requires RPC client setup (not automatic like Elysia's Eden)
  • ❌ File-based routing requires a plugin

Elysia — Bun-Native Performance

Founded: 2023 | Creator: SaltyAom | Stars: 11k+

Elysia was built specifically for Bun. It's the fastest JavaScript API framework in benchmarks (beating Fastify, Hono, Express on throughput). The DX is excellent: Eden Treaty gives you tRPC-level type safety without a separate package.

Elysia's Eden Treaty

// server.ts
import { Elysia, t } from "elysia";

const app = new Elysia()
  .get("/", "Hello Elysia")
  .post(
    "/projects",
    ({ body }) => {
      // body is typed from the schema!
      return db.insert(projects).values(body);
    },
    {
      body: t.Object({
        name: t.String({ minLength: 1 }),
        slug: t.String({ pattern: "^[a-z0-9-]+$" }),
      }),
    }
  )
  .listen(3000);

export type App = typeof app;

// client.ts — zero config, full types
import { treaty } from "@elysiajs/eden";
import type { App } from "./server";

const client = treaty<App>("http://localhost:3000");

// Fully typed — no separate schema file or type generation step
const { data, error } = await client.projects.post({
  name: "My SaaS",
  slug: "my-saas",
});

Eden Treaty is the standout feature. Unlike tRPC (which requires a separate router/procedure definition) or Hono RPC (which requires hc() client), Eden Types flow automatically from your Elysia app definition.

Elysia with Bun's Built-In Features

import { Elysia } from "elysia";
import { Database } from "bun:sqlite"; // Bun built-in SQLite

const db = new Database("myapp.db");
db.run(`CREATE TABLE IF NOT EXISTS users (
  id TEXT PRIMARY KEY,
  email TEXT UNIQUE,
  created_at INTEGER
)`);

const app = new Elysia()
  .get("/users/:id", ({ params }) => {
    return db.query("SELECT * FROM users WHERE id = ?").get(params.id);
  })
  .listen(3000);

Elysia SaaS Starters

The ecosystem is growing but smaller than Hono:

# Official starter
bun create elysia my-app

# Community starters
bunx degit saltyaom/elysia-bun-example my-app
bunx degit elysiajs/elysia-with-prisma my-app

Notable: elysia-saas-starter (community) — Bun + Elysia + Drizzle + Clerk + Stripe.

Elysia Strengths

  • ✅ Fastest TypeScript API framework (Bun runtime)
  • ✅ Eden Treaty: best E2E type safety DX
  • ✅ Plugin system with lifecycle hooks
  • ✅ Swagger/OpenAPI generation built-in

Elysia Weaknesses

  • ❌ Bun-dependent — some Node.js compatibility gaps
  • ❌ Smaller ecosystem and fewer SaaS starters
  • ❌ Less mature than Hono for production workloads

Encore.ts — Infrastructure as TypeScript

Founded: 2021 | Creator: Encore team | Stars: 8k+

Encore.ts takes a fundamentally different approach. Instead of being a web framework, it's a backend development platform where you annotate TypeScript with Encore decorators, and Encore generates your cloud infrastructure (AWS/GCP/Azure or Encore Cloud) automatically.

// No express, no hono, no routing — just Encore annotations
import { api, APIError } from "encore.dev/api";
import { SQLDatabase } from "encore.dev/storage/sqldb";

// Encore generates a PostgreSQL database
const db = new SQLDatabase("saas", {
  migrations: "./migrations",
});

// This becomes an HTTP endpoint
export const getProject = api(
  { expose: true, method: "GET", path: "/projects/:id" },
  async ({ id }: { id: string }) => {
    const project = await db.queryRow`
      SELECT * FROM projects WHERE id = ${id}
    `;
    if (!project) throw APIError.notFound("Project not found");
    return { project };
  }
);

// This becomes a Pub/Sub publisher
import { Topic } from "encore.dev/pubsub";

export const ProjectCreated = new Topic<{ projectId: string; userId: string }>(
  "project-created",
  { deliveryGuarantee: "at-least-once" }
);

// This becomes a subscriber
import { Subscription } from "encore.dev/pubsub";

export const sendWelcomeEmail = new Subscription(ProjectCreated, "send-welcome-email", {
  handler: async (event) => {
    await resend.emails.send({
      to: await getUserEmail(event.userId),
      subject: "Welcome to the project",
      html: `<p>Your project ${event.projectId} is ready.</p>`,
    });
  },
});

Encore's magic: This TypeScript code gets parsed by Encore's compiler, which generates:

  • AWS Lambda functions + API Gateway
  • RDS PostgreSQL database
  • SNS/SQS for Pub/Sub
  • IAM roles and security groups
  • CloudFormation or Terraform (you never see it)

Encore SaaS Starters

# Official Encore CLI
encore app create my-saas --example=user-auth

# Available examples
encore app create --example=slack-bot
encore app create --example=saas-starter
encore app create --example=stripe-checkout

Encore maintains ~10 official example applications and a growing community library.

Encore Strengths

  • ✅ No infrastructure code to write or maintain
  • ✅ Distributed tracing, metrics, and logging built-in
  • ✅ Local development with production parity (runs services locally)
  • ✅ Encore Cloud manages deployments ($0 for development)
  • ✅ Type-safe microservices with generated client

Encore Weaknesses

  • ❌ Lock-in to Encore's deployment model (even if targeting AWS)
  • ❌ Doesn't work with Bun or Cloudflare Workers
  • ❌ Smaller community than Hono/Elysia
  • ❌ Less flexibility for custom infrastructure

Comparison: Same SaaS Feature in Three Frameworks

Let's implement a simple "create organization" endpoint:

Hono

app.post(
  "/organizations",
  zValidator("json", createOrgSchema),
  clerkMiddleware(),
  async (c) => {
    const { userId } = getAuth(c);
    const { name, slug } = c.req.valid("json");

    const org = await db.insert(organizations).values({ id: nanoid(), name, slug, ownerId: userId }).returning();
    return c.json(org[0], 201);
  }
);

Elysia

app.post(
  "/organizations",
  async ({ body, store: { userId } }) => {
    return db.insert(organizations).values({ id: nanoid(), ...body, ownerId: userId }).returning();
  },
  {
    body: t.Object({
      name: t.String({ minLength: 1 }),
      slug: t.String({ pattern: "^[a-z0-9-]+$" }),
    }),
    beforeHandle: [authenticate],
  }
);

Encore.ts

export const createOrganization = api(
  { expose: true, method: "POST", path: "/organizations", auth: true },
  async ({ name, slug }: { name: string; slug: string }) => {
    const userId = getAuthData()!.userID;
    const org = await db.queryRow`
      INSERT INTO organizations (id, name, slug, owner_id)
      VALUES (gen_random_uuid(), ${name}, ${slug}, ${userId})
      RETURNING *
    `;
    return { organization: org };
  }
);

Choosing Your Framework

You should chooseIf
HonoDeploying to Cloudflare Workers / edge; need multi-runtime flexibility; want the most starters
ElysiaUsing Bun; want best performance; love Eden Treaty's DX; building a pure API backend
Encore.tsWant to eliminate infrastructure management; building microservices; hate Terraform

For a typical Next.js SaaS (API routes)

Stick with Next.js Route Handlers — none of these frameworks add value for a monolithic Next.js app with a few API routes.

For a dedicated API service

Hono if you want edge deployment, Elysia if you're on Bun, Encore if you want managed infrastructure.


Making the Final Decision: Real-World Scenarios

The comparison table is a starting point. Here's how the decision plays out in concrete scenarios:

Scenario 1: AI SaaS with a streaming chat endpoint. You need a dedicated backend service that handles LLM streaming, token metering, and user sessions. Hono is the clear winner — it runs on Cloudflare Workers (near-zero latency, global), has middleware for Clerk auth and Zod validation, and the @hono/streaming utility handles server-sent events correctly. Elysia works but Bun's streaming support for long-lived connections is less battle-tested than Workers.

Scenario 2: Internal developer tool with strict type contracts. Your team is Bun-first and the API is consumed only by an internal React dashboard. Elysia + Eden Treaty gives you the tightest end-to-end type safety — changes to the API surface immediately produce TypeScript errors on the client without any code generation step. The performance headroom on Bun means you'll never hit throughput limits at internal scale.

Scenario 3: Microservices for a B2B SaaS. You're splitting a monolith into five services: users, billing, notifications, analytics, and the core product API. Encore.ts is the compelling choice — each service is annotated TypeScript, Encore generates the infrastructure for all five, and inter-service calls have type-safe generated clients. You write zero Terraform. The operational cost of managing five Lambda functions separately drops to near zero.

Migrating From Express

If you're on Express today, the move to Hono is the lowest-friction migration. Hono's middleware model mirrors Express: app.use() for middleware, app.get()/post() for routes, c.json() instead of res.json(). Most Express route handlers translate with minimal changes — the main adjustment is the context object and the different middleware signature. Expect a one-week migration for a medium-sized Express API.

Migrating to Elysia from Express requires a larger mental shift toward Elysia's declarative plugin and lifecycle hook system. It's a real rewrite, not a refactor, but the performance gain is worth it if you're committing to Bun.

Migrating to Encore.ts is an architectural shift, not just a framework swap. Budget 1-2 weeks even for a modest API.

Middleware Ecosystem Depth

Hono's @hono/ organization on npm contains official middleware for Zod validation, Clerk auth, JWT, CORS, rate limiting, and OpenAPI documentation generation. You're composing maintained packages, not writing middleware from scratch.

Elysia's ecosystem is built around plugins (@elysiajs/cors, @elysiajs/jwt, @elysiajs/static), but the community ecosystem is smaller than Hono's. For niche requirements you'll write more code yourself.

Encore.ts doesn't have a traditional middleware ecosystem because infrastructure concerns are handled by the Encore platform rather than npm packages. This is a feature — you don't need rate-limiting middleware when Encore's platform handles it — but it means you're more dependent on Encore's opinionated approach.

What These Frameworks Have in Common

Despite their different philosophies, Hono, Elysia, and Encore share several properties that matter for SaaS in 2026:

All three are TypeScript-first and generate type errors for most API contract violations before runtime. All three are significantly faster than Express — the benchmark differences between them are less important than the shared 5-10x advantage over Express. All three have active communities with growing starter ecosystems. All three support OpenAPI documentation generation (though Encore's is most automatic). And all three represent a clean break from Express-era patterns toward a type-driven, performance-focused backend model.

For the full picture of how these backend frameworks fit into SaaS boilerplate selection, see the best SaaS boilerplates guide and the buy vs build analysis. For type-safe full-stack patterns using Next.js and tRPC, the T3 Stack review shows how the tRPC approach compares to Hono RPC and Elysia Eden for end-to-end type safety. For Hono in a serverless context, the best serverless boilerplates guide covers Hono on Cloudflare Workers alongside SST and Vercel Functions.

Testing Strategies for Each Framework

Testing a backend API is where framework philosophy shows up in practice. The three frameworks require meaningfully different testing approaches.

Hono apps test cleanly with the built-in test utilities:

import { testClient } from 'hono/testing'
import { app } from './app'

describe('POST /projects', () => {
  it('creates a project', async () => {
    const client = testClient(app)
    const res = await client.projects.$post({
      json: { name: 'Test Project', slug: 'test-project' }
    })
    expect(res.status).toBe(201)
    const data = await res.json()
    expect(data.name).toBe('Test Project')
  })
})

The testClient from hono/testing wraps your app and returns a typed client — the same RPC client you'd use from the frontend. Test request/response contracts with full type inference.

Elysia apps use a similar pattern with Eden Treaty on the test client:

import { treaty } from '@elysiajs/eden'
import { app } from './app'

const api = treaty(app)

describe('projects', () => {
  it('creates a project', async () => {
    const { data, error } = await api.projects.post({
      name: 'Test Project',
      slug: 'test-project'
    })
    expect(error).toBeNull()
    expect(data?.name).toBe('Test Project')
  })
})

Elysia's Eden Treaty provides end-to-end type safety in tests without any separate type generation step — the same advantage it provides in application code.

Encore.ts testing runs against the Encore test runner, which starts a local version of your infrastructure:

encore test ./...   # Run all tests with local Encore infrastructure

Each test runs with access to real (local) database instances, pub/sub, and secrets — Encore injects the infrastructure context automatically. This is more realistic than mocked dependencies but requires the Encore runtime to be installed.

Authentication Patterns Across All Three

Authentication is a SaaS requirement, not an optional feature. Here's how auth integrates across the three frameworks:

Hono + Clerk is the most common enterprise pattern for Next.js + Hono backends:

import { clerkMiddleware, getAuth } from '@hono/clerk-auth'

app.use('*', clerkMiddleware())

app.get('/api/me', (c) => {
  const auth = getAuth(c)
  if (!auth?.userId) return c.json({ error: 'Unauthorized' }, 401)
  return c.json({ userId: auth.userId })
})

The @hono/clerk-auth middleware validates Clerk's JWT on every request. The getAuth() call extracts the session inside route handlers. Because Hono runs on Cloudflare Workers, this pattern supports edge auth validation with global latency.

Elysia uses a plugin/lifecycle approach for auth:

const authPlugin = new Elysia({ name: 'auth' })
  .derive(async ({ headers }) => {
    const token = headers['authorization']?.replace('Bearer ', '')
    const payload = await verifyJWT(token)
    return { user: payload }
  })
  .macro(({ onBeforeHandle }) => ({
    requireAuth(value: boolean) {
      if (value) onBeforeHandle(({ user, error }) => {
        if (!user) return error(401, 'Unauthorized')
      })
    }
  }))

app.use(authPlugin).get('/api/me', ({ user }) => user, { requireAuth: true })

Encore.ts auth is declared at the API level using an auth handler:

import { authHandler } from 'encore.dev/auth'

export const myAuthHandler = authHandler(
  { expose: true, method: 'GET', path: '/auth' },
  async (params: HandlerParams): Promise<AuthData> => {
    const token = params.headers['authorization']
    return validateToken(token) // returns { userID, roles }
  }
)

Once the auth handler is defined, any endpoint marked auth: true automatically validates the JWT and makes user data available via getAuthData().


Methodology

  • Reviewed Hono (v4), Elysia (v1.2), and Encore.ts (v1.x) documentation as of March 2026
  • Benchmarked request throughput from published benchmark repositories (TechEmpower, bun/elysia benchmarks)
  • Analyzed community starter ecosystems on GitHub
  • Tested Eden Treaty type safety with a sample Elysia + Next.js project
  • Reviewed Encore.ts infrastructure generation via their local development runner

Find all Hono, Elysia, and Encore starters on StarterPick — filter by framework, runtime, and features.

For broader context on boilerplate and framework selection, see the best SaaS boilerplates guide and the Next.js SaaS tech stack guide.

Evaluating whether to use a boilerplate at all? The best free open-source SaaS boilerplates guide covers open-source starters that pair well with these backend frameworks.

OpenAPI and Documentation Generation

API documentation is a selling point for developer-facing SaaS. All three frameworks support OpenAPI generation, but the ergonomics differ.

Hono uses @hono/zod-openapi — a Zod-first approach where you define routes with explicit input/output schemas and the OpenAPI spec is generated automatically:

import { createRoute, OpenAPIHono } from '@hono/zod-openapi'
import { z } from 'zod'

const app = new OpenAPIHono()

const ProjectSchema = z.object({
  id: z.string(),
  name: z.string(),
})

const createProjectRoute = createRoute({
  method: 'post',
  path: '/projects',
  request: { body: { content: { 'application/json': { schema: ProjectSchema.omit({ id: true }) } } } },
  responses: { 201: { content: { 'application/json': { schema: ProjectSchema } }, description: 'Created' } },
})

app.openapi(createProjectRoute, async (c) => {
  const body = c.req.valid('json')
  // ...
  return c.json(newProject, 201)
})

app.doc('/openapi.json', { openapi: '3.0.0', info: { title: 'My API', version: '1.0.0' } })

Elysia includes Swagger UI generation via the @elysiajs/swagger plugin — no extra schema definitions needed, since Elysia's TypeBox schemas generate both validation and documentation:

import { Elysia } from 'elysia'
import { swagger } from '@elysiajs/swagger'

const app = new Elysia()
  .use(swagger())  // Adds /swagger UI and /swagger/json
  .post('/projects', ({ body }) => createProject(body), {
    body: t.Object({ name: t.String(), slug: t.String() }),
    response: t.Object({ id: t.String(), name: t.String() })
  })

Encore.ts generates OpenAPI documentation automatically from endpoint definitions — no extra packages or decorators needed. The encore gen client command produces a TypeScript client from the OpenAPI spec for use in frontend applications. For microservices architectures where services need to call each other, the generated client with full types eliminates a class of integration bugs.

For teams building public APIs or developer tools as part of their SaaS, Encore's automatic documentation story is compelling. For internal APIs, Elysia's zero-configuration Swagger is the most ergonomic. Hono's @hono/zod-openapi offers the most control at the cost of explicit schema definitions per route.

The SaaS Boilerplate Matrix (Free PDF)

20+ SaaS starters compared: pricing, tech stack, auth, payments, and what you actually ship with. Updated monthly. Used by 150+ founders.

Join 150+ SaaS founders. Unsubscribe in one click.