04/14/26

Elysia vs Encore.ts in 2026

Comparing Bun-native and infrastructure-aware TypeScript frameworks

8 Min Read

Elysia is a Bun-native framework built around ergonomics and type safety, with end-to-end type inference between client and server. Encore.ts focuses on distributed systems with automatic infrastructure provisioning, type-safe service communication, and built-in observability. Both frameworks are TypeScript-first, but they solve different problems.

Quick Comparison

AspectElysiaEncore.ts
PhilosophyErgonomic Bun-native frameworkInfrastructure-aware application framework
RuntimeBun (primary), Node.js compatibleNode.js with Rust runtime
Type SafetyEnd-to-end with TypeBox/Zod/ValibotNative TypeScript types
ValidationSchema-first (TypeBox, Zod, Valibot, ArkType)Native TypeScript types + validation rules
Infrastructure from CodeNoYes (databases, Pub/Sub, cron, storage)
Local DevelopmentBun standard toolingAutomatic infrastructure provisioning
ObservabilityManual setupBuilt-in tracing, metrics, logs
MicroservicesManualType-safe service-to-service calls
AI Agent CompatibilityManual configurationBuilt-in infrastructure awareness via MCP
Best ForFast APIs on Bun, edge-like performanceDistributed systems, production backends on AWS/GCP

The Basics: Defining an API

Elysia

import { Elysia } from 'elysia';

const app = new Elysia()
  .get('/hello/:name', ({ params: { name } }) => ({
    message: `Hello, ${name}!`
  }))
  .listen(3000);

Elysia's API is method-chained and concise. The framework infers types from the route parameters and returns the response type automatically.

Encore.ts

import { api } from "encore.dev/api";

interface HelloResponse {
  message: string;
}

export const hello = api(
  { method: "GET", path: "/hello/:name", expose: true },
  async ({ name }: { name: string }): Promise<HelloResponse> => {
    return { message: `Hello, ${name}!` };
  }
);

Encore uses a declarative approach with explicit types for request and response. The framework handles server setup, routing, and request validation.

Verdict: Elysia's chaining is more concise for simple endpoints. Encore provides more explicit typing that works better across service boundaries and for generated documentation.

Type Safety and Validation

Elysia

Elysia supports multiple schema libraries and infers types from them:

import { Elysia, t } from 'elysia';

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

The schema validates at runtime and provides type inference at compile time. Elysia also supports Zod, Valibot, and ArkType as validators through its plugin system. The eden client provides end-to-end type safety between frontend and backend similar to tRPC.

Encore.ts

import { api } from "encore.dev/api";
import { MinLen, IsEmail } from "encore.dev/validate";

interface CreateUserRequest {
  email: string & IsEmail;
  name: string & MinLen<1>;
}

interface User {
  id: number;
  email: string;
  name: string;
}

export const createUser = api(
  { method: "POST", path: "/users", expose: true },
  async (req: CreateUserRequest): Promise<User> => {
    return { id: 1, email: req.email, name: req.name };
  }
);

Encore validates requests based on TypeScript types with optional validation rules. Service-to-service calls are type-safe through generated clients:

import { users } from "~encore/clients";

const user = await users.createUser({ email: "[email protected]", name: "John" });

Verdict: Elysia's schema approach with TypeBox gives precise runtime validation with inference. Encore uses native TypeScript types without a schema library, and generates type-safe clients across services automatically.

Infrastructure and Databases

This is where the frameworks differ the most.

Elysia

Elysia has no built-in infrastructure primitives. You bring your own database, ORM, and configuration:

import { Elysia } from 'elysia';
import { drizzle } from 'drizzle-orm/bun-sqlite';

const db = drizzle('mydb.sqlite');

const app = new Elysia()
  .get('/users/:id', async ({ params: { id } }) => {
    return await db.select().from(users).where(eq(users.id, id));
  });

You manage connection strings, environment variables, and database lifecycle yourself. For production, you set up the database, networking, and backups separately.

Encore.ts

import { api } from "encore.dev/api";
import { SQLDatabase } from "encore.dev/storage/sqldb";

// Provisions RDS on AWS or Cloud SQL on GCP with sensible defaults (uses Docker Postgres locally).
const db = new SQLDatabase("users", { migrations: "./migrations" });

export const getUser = api(
  { method: "GET", path: "/users/:id", expose: true },
  async ({ id }: { id: string }) => {
    return await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
  }
);

Running encore run provisions a local PostgreSQL database automatically. Deploying provisions RDS or Cloud SQL in your cloud account. Encore also includes Pub/Sub, cron jobs, object storage, and caching:

import { Topic } from "encore.dev/pubsub";
import { CronJob } from "encore.dev/cron";
import { Bucket } from "encore.dev/storage/objects";

// Provisions SNS+SQS on AWS or GCP Pub/Sub on GCP with sensible defaults (in-memory locally).
const userEvents = new Topic<{ userId: string }>("user-events", {
  deliveryGuarantee: "at-least-once",
});

const cleanup = new CronJob("cleanup", {
  title: "Daily cleanup",
  every: "24h",
  endpoint: cleanupEndpoint,
});

const uploads = new Bucket("uploads", { versioned: false });

Verdict: Elysia leaves infrastructure to you. Encore provisions databases, queues, storage, and cron jobs automatically from your application code. For backends that need more than an HTTP layer, Encore handles the infrastructure that would otherwise require separate tooling.

Local Development

Elysia

bun create elysia myapp
cd myapp
bun run dev

Fast to start. For databases, you set up your own PostgreSQL or use SQLite through Bun's built-in support. No Docker required for SQLite, but production-like Postgres requires manual setup.

Encore.ts

encore app create myapp
cd myapp
encore run

Encore provisions real Postgres, Pub/Sub, and distributed tracing locally. The local development dashboard at localhost:9400 provides API testing, architecture diagrams, and trace inspection.

Verdict: Elysia starts fast with minimal dependencies. Encore provisions the full infrastructure stack locally so development mirrors production. The right choice depends on whether your project needs databases and services.

Observability

Elysia

Elysia relies on external tooling or plugins for observability:

import { Elysia } from 'elysia';

const app = new Elysia()
  .onRequest(({ request }) => {
    console.log(`${request.method} ${request.url}`);
  });

For tracing and metrics, you integrate OpenTelemetry or platform-specific tools.

Encore.ts

import { api } from "encore.dev/api";
import log from "encore.dev/log";

export const getUser = api(
  { method: "GET", path: "/users/:id", expose: true },
  async ({ id }: { id: string }) => {
    log.info("fetching user", { userId: id });
    return { id, name: "John" };
  }
);

Every API call, database query, and Pub/Sub message is traced automatically. No setup or instrumentation code needed.

Encore distributed tracing

Verdict: Encore provides built-in observability without configuration. Elysia requires manual setup. For production systems where you need to debug issues across services, Encore's tracing is a significant advantage.

Microservices

Elysia

Elysia provides a treaty client for type-safe communication between Elysia instances, but service discovery and deployment are manual:

import { treaty } from '@elysiajs/eden';

const usersService = treaty<UsersApp>('http://localhost:3001');
const { data } = await usersService.users['1'].get();

You manage service URLs, deployment, and orchestration yourself.

Encore.ts

import { users } from "~encore/clients";

export const createOrder = api(
  { method: "POST", path: "/orders", expose: true },
  async (req: CreateOrderRequest) => {
    const user = await users.getUser({ id: req.userId });
    return { orderId: 1, user };
  }
);

Service calls are type-safe function calls with automatic discovery and distributed tracing across service boundaries. Encore generates a service catalog showing your architecture.

Encore service catalog

Verdict: Elysia's Eden provides type safety between Elysia instances. Encore provides type-safe calls with automatic discovery, tracing, and a service catalog. For multi-service backends, Encore reduces the coordination overhead significantly.

AI Code Generation

Elysia

Elysia's method-chaining API means an agent has to decide on validation libraries (TypeBox, Zod, Valibot, ArkType), database access patterns, and deployment targets with every prompt. The chaining syntax is distinctive enough that agents trained on Express-style code sometimes produce hybrid patterns.

// An agent might generate:
// - TypeBox schema or Zod validator (different syntax for each)
// - drizzle-orm or prisma or raw bun:sqlite
// - Different plugin configurations each time

Encore.ts

With Encore, the project conventions are already established. An agent reads the existing patterns and follows them. Encore's MCP server provides agents access to database schemas, distributed traces, and service architecture for context-aware code generation.

export const create = api(
  { expose: true, auth: true, method: "POST", path: "/orders" },
  async (req: CreateOrderRequest): Promise<Order> => {
    const order = await db.queryRow<Order>`
      INSERT INTO orders (customer_id, total)
      VALUES (${req.customerId}, ${req.total})
      RETURNING id, customer_id as "customerId", total
    `;
    return order!;
  }
);

Verdict: Elysia's validation library flexibility means agents make different choices each time. Encore gives agents fixed conventions and live system context through MCP, producing more consistent code.

When to Choose Elysia

Elysia makes sense when:

  • You're building on Bun and want a framework optimized for it
  • Raw performance is a priority and you want the fastest possible request handling
  • You prefer method chaining and a concise API surface
  • You want Eden/treaty for frontend-to-backend type safety
  • Your project is a single service with simple infrastructure needs

When to Choose Encore.ts

Encore.ts makes sense when:

  • You're building distributed systems with databases and multiple services
  • You want infrastructure automation from local development to production on AWS/GCP
  • You need built-in observability without configuring OpenTelemetry
  • You're building microservices and want type-safe service communication
  • You want to deploy to your own AWS or GCP account with sensible defaults
  • You want infrastructure-aware code that AI agents can generate and deploy with guardrails

For single-service APIs optimized for Bun, Elysia delivers excellent performance and developer experience. For production backends with databases, multiple services, and observability requirements, Encore.ts provides the infrastructure automation that Elysia leaves to you.

Getting Started

Try both with a small project:

See also: Best TypeScript Backend Frameworks for a broader perspective on the TypeScript backend landscape, or our Hono vs Encore.ts comparison for another lightweight-vs-infrastructure comparison.

Get started

Install the CLI and create an app.

$ brew install encoredev/tap/encore$ iwr https://encore.dev/install.ps1 | iex$ curl -L https://encore.dev/install.sh | bash

Have questions about choosing a framework? Join our Discord community where developers discuss architecture decisions daily.

Ready to build your next backend?

Encore is the Open Source framework for building robust type-safe distributed systems with declarative infrastructure.