Skip to content

Web Development · Backend

ElysiaJS in 2026: Type-Safe Bun APIs, No Codegen

ElysiaJS gives Bun APIs fast routing, TypeBox validation, and type inference that reaches the client with no codegen step. How it works and when to pick it.

Anurag Verma

Anurag Verma

5 min read

ElysiaJS in 2026: End-to-End Type Safety for Bun APIs Without the Ceremony

Sponsored

Share

Express is 15 years old. Fastify is better, but its TypeScript support still feels bolted on. If you’re building APIs on Bun and want type safety without stitching together three libraries and a code generator, ElysiaJS is the option worth knowing.

It’s designed around Bun’s runtime, uses TypeBox for validation instead of Zod (which keeps startup times low), and ships a client library called Eden Treaty that gives you full type inference from the server to whatever is calling it. No schema generation step. No GraphQL. Just TypeScript types that follow the contract from one end to the other.

What ElysiaJS Actually Does

At the core, it’s an HTTP router with schema validation built in. You define routes the same way you define types:

import { Elysia, t } from "elysia";

const app = new Elysia()
  .get("/health", () => ({ status: "ok" }))
  .post("/users", ({ body }) => {
    return { id: crypto.randomUUID(), email: body.email };
  }, {
    body: t.Object({
      email: t.String({ format: "email" }),
      name: t.String({ minLength: 1 }),
    }),
    response: t.Object({
      id: t.String(),
      email: t.String(),
    }),
  })
  .listen(3000);

export type App = typeof app;

The t object is TypeBox, which compiles JSON Schema validators at startup rather than running Zod’s parser on every request. In benchmarks, this matters: ElysiaJS consistently places near the top of Node/Bun HTTP framework comparisons. Real applications won’t hit the theoretical ceiling, but the per-request overhead is genuinely lower than Express or NestJS.

Eden Treaty: The Client Side

The App type exported from the server is the contract. On the client:

import { treaty } from "@elysiajs/eden";
import type { App } from "../server";

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

// Fully typed: api.users.post knows what body it accepts
// and what response to expect
const { data, error } = await api.users.post({
  email: "user@example.com",
  name: "Alice",
});

if (error) {
  console.error(error.message);
} else {
  console.log(data.id); // string — TypeScript knows this
}

This is the part that makes ElysiaJS interesting. You’re not running tRPC (which requires a separate router setup), not running GraphQL (which requires a schema and resolvers), and not generating API clients from an OpenAPI spec. The server’s type definition is the client’s type definition.

The main limitation: Eden Treaty works well for TypeScript-to-TypeScript calls. If your clients are mobile apps or third-party integrators, you’ll still want to emit an OpenAPI spec. ElysiaJS has a Swagger plugin for that.

Plugin System

import { Elysia } from "elysia";
import { cors } from "@elysiajs/cors";
import { swagger } from "@elysiajs/swagger";
import { bearer } from "@elysiajs/bearer";

const app = new Elysia()
  .use(cors({
    origin: ["https://app.example.com"],
    methods: ["GET", "POST", "PUT", "DELETE"],
  }))
  .use(swagger({
    documentation: {
      info: { title: "API", version: "1.0.0" },
    },
  }))
  .use(bearer())
  .guard({
    beforeHandle: ({ bearer, set }) => {
      if (!bearer) {
        set.status = 401;
        return "Unauthorized";
      }
    },
  }, (app) =>
    app
      .get("/me", ({ bearer }) => {
        return { token: bearer };
      })
  )
  .listen(3000);

The guard pattern groups routes under shared middleware. You apply beforeHandle hooks to a block of routes rather than adding middleware per-route or globally. This maps well to how real applications are structured: public routes, authenticated routes, admin routes.

Organizing a Real Application

For anything beyond a toy API, split routes across files:

// routes/users.ts
import { Elysia, t } from "elysia";
import { db } from "../db";

export const usersRouter = new Elysia({ prefix: "/users" })
  .get("/", async () => {
    return db.query.users.findMany();
  })
  .get("/:id", async ({ params, set }) => {
    const user = await db.query.users.findFirst({
      where: (users, { eq }) => eq(users.id, params.id),
    });
    if (!user) {
      set.status = 404;
      return { error: "Not found" };
    }
    return user;
  }, {
    params: t.Object({ id: t.String() }),
  });

// app.ts
import { Elysia } from "elysia";
import { usersRouter } from "./routes/users";

const app = new Elysia()
  .use(usersRouter)
  .listen(3000);

export type App = typeof app;

Eden Treaty understands the prefix, so api.users.get() routes correctly on the client side.

vs Hono

Hono is the other major contender in this space. Both run on Bun, Cloudflare Workers, Deno, and Node. The relevant differences:

Hono has broader runtime support and more documentation around edge deployments. Its RPC mode gives you similar type inference to Eden Treaty. ElysiaJS has the validation story better integrated, TypeBox schemas validate and type-infer in one step, and the guard pattern is cleaner for middleware organization.

For Cloudflare Workers deployments, Hono is the safer choice because ElysiaJS’s Workers support is still maturing. For a Bun server on your own infrastructure, both work well.

When to Use It

Good fit:

  • Full-stack TypeScript apps where the client is also TypeScript (Next.js frontend, React Native app)
  • Bun projects where startup time matters (short-lived containers, serverless)
  • Teams that want type safety without pulling in tRPC or setting up code generation
  • Internal tools and admin APIs where you control both ends of the connection

Less ideal:

  • APIs consumed by clients in other languages (document the Swagger output carefully)
  • Projects that need the ecosystem breadth of Express or Fastify
  • Teams not already using Bun (ElysiaJS runs on Node, but its advantages are clearest on Bun)

Getting Started

bun create elysia my-api
cd my-api
bun dev

The scaffolded project includes a basic Elysia app, TypeScript config, and a test file. The framework is stable enough for production use. The ecosystem is smaller than Express, but the core features (routing, validation, Eden Treaty, Swagger), cover what most API projects need without reaching for external packages.

Frequently asked questions

Is ElysiaJS faster than Express or Fastify?
Per-request overhead is genuinely lower, and it places near the top of Bun and Node HTTP framework benchmarks. Most of that comes from TypeBox compiling its validators at startup instead of running a parser on every request, plus Bun's runtime underneath. Real applications spend most of their time in database calls and business logic, so you should not expect benchmark numbers to translate into the same speedup end to end.
How does Eden Treaty differ from tRPC?
tRPC needs a separate router definition that both ends import. Eden Treaty skips that: you export the App type your Elysia server already produces, and the client infers everything from it. Fewer moving parts, but it is also more tightly coupled, since the client depends on the server's actual type rather than a contract defined between them.
Can I use ElysiaJS if my API has non-TypeScript clients?
Yes, but you lose the main reason to choose it. Eden Treaty's inference only works TypeScript to TypeScript. For mobile apps or external integrators, add the Swagger plugin, publish the OpenAPI spec, and treat that spec as the real contract. At that point Fastify or Hono are worth comparing again.
Should I choose ElysiaJS or Hono?
Hono for Cloudflare Workers and edge deployments, where it has broader runtime support and better documentation, and where Elysia's Workers story is still maturing. ElysiaJS for a Bun server on infrastructure you control, where its validation integration is tighter (TypeBox validates and infers in one step) and the guard pattern organises middleware more cleanly.
Is ElysiaJS ready for production?
The core is stable enough to run in production: routing, validation, Eden Treaty, and Swagger cover what most API projects need without external packages. The real constraint is ecosystem breadth. If you rely on a long tail of Express middleware, you will be writing some of it yourself.

Sponsored

Sponsored

Discussion

Join the conversation.

Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.

Sponsored