Comparing Bun-native and infrastructure-aware TypeScript frameworks
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.
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.
hello/hello.tsimport { 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.
users/users.tsimport { 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:
service.tsimport { users } from "~encore/clients";
const user = await users.createUser({ email: "test@example.com", 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.
users/users.tsimport { 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.
orders/orders.tsimport { 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.
orders/orders.tsexport 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.
Start at the top and follow the first question you can answer with a yes:
Do you want infrastructure provisioned from your code, without building a DevOps team internally?
Are you building several services that share databases, Pub/Sub, or cron jobs?
Do you need high request throughput, including with schema validation?
Are you building a single-service API on Bun, and want Eden Treaty for end-to-end types?
In our benchmarks, Encore.ts handled 121,005 requests per second without validation and 107,018 with schema validation, against 82,617 and 35,124 for Elysia.
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.
Encore is open source with 12,000+ GitHub stars. It provisions infrastructure in your own AWS or GCP account, or you can build Docker images and deploy anywhere.
Try both with a small project:
Or install the CLI and create a starter app:
Run in your terminal to get started locally.
Have questions about choosing a framework? Join our Discord community where developers discuss architecture decisions daily.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.