At a glance
Elysia is an ergonomic web framework for building backend servers with Bun.
Designed with simplicity and type‑safety in mind, Elysia offers a familiar API with extensive support for TypeScript and is optimized for Bun.
Here’s a simple hello‑world example:
import { Elysia } from 'elysia'
new Elysia()
.get('/', 'Hello Elysia')
.get('/user/:id', ({ params: { id } }) => id)
.post('/form', ({ body }) => body)
.listen(3000)
Navigate to http://localhost:3000 and you should see 'Hello Elysia' as the result.
Tip
Hover over the code snippet to see the type definition.
In the mock browser, click on the path highlighted in blue to change paths and preview the response.
Elysia can run in the browser, and the results you see are actually executed using Elysia.
Performance
Building on Bun and extensive optimisation like static code analysis allows Elysia to generate optimised code on the fly.
Elysia can outperform most web frameworks available today¹, and even match the performance of Golang and Rust frameworks².
| Framework | Runtime | Average | Plain Text | Dynamic Parameters | JSON Body |
|---|---|---|---|---|---|
| bun | bun | 262,660.433 | 326,375.76 | 237,083.18 | 224,522.36 |
| elysia | bun | 255,574.717 | 313,073.64 | 241,891.57 | 211,758.94 |
| hyper-express | node | 234,395.837 | 311,775.43 | 249,675 | 141,737.08 |
| hono | bun | 203,937.883 | 239,229.82 | 201,663.43 | 170,920.4 |
| h3 | node | 96,515.027 | 114,971.87 | 87,935.94 | 86,637.27 |
| oak | deno | 46,569.853 | 55,174.24 | 48,260.36 | 36,274.96 |
| fastify (bun) | bun | 65,897.043 | 92,856.71 | 81,604.66 | 23,229.76 |
| fastify (node) | node | 60,322.413 | 71,150.57 | 62,060.26 | 47,756.41 |
| koa | node | 39,594.14 | 46,219.64 | 40,961.72 | 31,601.06 |
| express (bun) | bun | 29,715.537 | 39,455.46 | 34,700.85 | 14,990.3 |
| express (node) | node | 15,913.153 | 17,736.92 | 17,128.7 | 12,873.84 |
TypeScript
Elysia is designed to help you write less TypeScript.
Elysia’s type system is fine‑tuned to infer types from your code automatically, without needing to write explicit TypeScript, while providing type‑safety at both runtime and compile time for the most ergonomic developer experience.
Take a look at this example:
import { Elysia } from 'elysia'
new Elysia()
.get('/user/:id', ({ params: { id } }) => id)
.listen(3000)
The above code creates a path parameter "id".
The value that replaces :id will be passed to params.id both at runtime and in types, without manual type declaration.
import { Elysia, t } from 'elysia'
new Elysia()
.get('/user/:id', ({ params: { id } }) => id)
.listen(3000)
Note
TypeScript is not required to use Elysia, but it’s recommended.
Type Integrity
To take it a step further, Elysia provides Elysia.t, a schema builder to validate types and values at both runtime and compile time, creating a single source of truth for your data types.
Let’s modify the previous code to accept only a number value instead of a string.
import { Elysia, t } from 'elysia'
new Elysia()
.get('/user/:id', ({ params: { id } }) => id)
.listen(3000)
The framework will automatically infer that id is a string; to enforce a numeric type, you can use Elysia.t as shown above.