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.
| Aspect | Elysia | Encore.ts |
|---|---|---|
| Philosophy | Ergonomic Bun-native framework | Infrastructure-aware application framework |
| Runtime | Bun (primary), Node.js compatible | Node.js with Rust runtime |
| Type Safety | End-to-end with TypeBox/Zod/Valibot | Native TypeScript types |
| Validation | Schema-first (TypeBox, Zod, Valibot, ArkType) | Native TypeScript types + validation rules |
| Infrastructure from Code | No | Yes (databases, Pub/Sub, cron, storage) |
| Local Development | Bun standard tooling | Automatic infrastructure provisioning |
| Observability | Manual setup | Built-in tracing, metrics, logs |
| Microservices | Manual | Type-safe service-to-service calls |
| AI Agent Compatibility | Manual configuration | Built-in infrastructure awareness via MCP |
| Best For | Fast APIs on Bun, edge-like performance | Distributed systems, production backends on AWS/GCP |
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.
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.
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.
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.
This is where the frameworks differ the most.
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.
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.
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 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.
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.
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.

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.
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.
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.

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.
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
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.
Elysia makes sense when:
Encore.ts makes sense when:
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.
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.
Install the CLI and create an app.
Have questions about choosing a framework? Join our Discord community where developers discuss architecture decisions daily.