Elysia 1.2 – You and Me
Author: @saltyaom
Published: 23 Dec 2024

Elysia 1.2 is a new release that expands universal runtime support and improves the developer experience. The main highlights are:
- Adapter – run Elysia on any supported runtime (Bun, Web Standard, Node beta).
- Object macro with
resolve– a cleaner macro syntax that can return lifecycle data. - Custom
parser– register a parser by name and select it per route. - WebSocket API – a consistent API with HTTP routes.
- TypeBox 0.34 – full support for circular recursive types.
- Reduced memory usage – a more efficient implementation (details below).
Adapter
One of the most requested features is support for more runtimes. Elysia 1.2 introduces the adapter option to allow Elysia to run on different environments.
import { node } from '@elysiajs/node'
new Elysia({
adapter: node()
})
.get('/', 'Hello Node')
.listen(3000)
Elysia is primarily built for Bun, but you can also run it on:
- Bun
- Web Standard (WinterCG) – e.g., Deno, browsers
- Node (beta)
Performance
Elysia avoids using a bridge to convert Web Standard requests to Node Request/Response. Instead it uses the native Node API directly, delivering performance that outperforms many Web‑Standard frameworks (Hono, h3, Fastify, Express).
Node benchmark: https://github.com/saltyaom/bun-http-framework-benchmark
Universal runtime API
To provide a consistent API across runtimes, Elysia exposes a set of utility functions. For example, file is a wrapper around Bun.file that works in both Bun and Node:
import { Elysia, file } from 'elysia'
new Elysia()
.get('/', () => file('./public/index.html'))
Current utilities:
file– return a file responseform– return aFormDataresponseserver– Bun'sServertype declaration (for future extensions)
Object Macro with resolve
Elysia 1.2 lets you use resolve inside macros. The new object‑style macro is more concise and allows returning lifecycle data directly.
import { Elysia } from 'elysia'
new Elysia()
.macro({
user: (enabled: true) => ({
resolve: ({ cookie: { session } }) => ({ user: session.value! })
})
})
.get('/', ({ user }) => user)
Old vs. New Syntax
// ✅ Object Macro
new Elysia()
.macro({
role: (role: 'admin' | 'user') => ({
beforeHandle: ({ cookie: { session } }) => ({ user: session.value! })
})
})
// ⚠️ Function Macro
new Elysia().macro(({ onBeforeHandle }) => ({
role(role: 'admin' | 'user') {
onBeforeHandle(({ cookie: { session } }) => ({ user: session.value! }))
}
}))
Both syntaxes are supported, but the object syntax is recommended.
NOTE:resolveworks only with the new object syntax (TypeScript limitation).
Custom Parser
Elysia 1.2 adds a named parser that can be referenced in routes.
import { Elysia } from 'elysia'
new Elysia()
.parser('custom', ({ contentType }) => {
if (contentType === 'application/kivotos') return 'nagisa'
})
.post('/', ({ body }) => body, { parse: 'custom' })
You can also chain multiple parsers; they are invoked in order until one returns a value.
new Elysia()
.parser('custom', ({ contentType }) => {
if (contentType === 'application/kivotos') return 'nagisa'
})
.post('/', ({ body }) => body, { parse: ['custom', 'json'] })
WebSocket
WebSocket has been rewritten to match the latest Bun API while staying compatible with other runtimes.
new Elysia()
.ws('/ws', {
ping: (message) => message,
pong: (message) => message
})
The API is now consistent with HTTP routes and shares a similar lifecycle.
TypeBox 0.34
Elysia 1.2 now supports TypeBox 0.34, using t.Module for reference models to handle circular recursive types.
import { Elysia, t } from 'elysia'
new Elysia()
.model({
a: t.Object({
b: t.Reference('c')
}),
c: t.Object({
d: t.Number()
})
})
Reduced Memory Usage
(Details omitted from the original content, but the release notes mention improved memory efficiency.)
For more information, see the full markdown source: https://elysiajs.com/blog/elysia-12.md