Named after the song Φ² from the album “At the Fingertip of the Sea” by HoyoMix as used in “You and Me”.
Elysia 1.2 focuses on commitment to expand universal runtime support and developer experience:
Adapter
Section titled “Adapter”One of the most requested features is to support more runtimes.
Elysia 1.2 introduces adapter to allow Elysia to run on different runtimes.
import { node } from '@elysiajs/node'
new Elysia({ adapter: node() }) .get('/', 'Hello Node') .listen(3000)Elysia is designed to be run on Bun and will continue to have Bun as the primary runtime and prioritize features on Bun first.
However, we are giving you more choices to try Elysia on different environments that fit your needs, eg. serverless like AWS Lambda, Supabase Function, etc.
The goal of Elysia’s adapter is to provide a consistent API across different runtimes while maintaining the best performance with the same code or with minimal changes for each runtime.
Performance
Section titled “Performance”Performance is one of Elysia’s strengths. We do not compromise on performance.
Instead of relying on a bridge to convert Web Standard requests to Node Request/Response, Elysia uses the native Node API directly to achieve the best performance while providing Web Standard compatibility if needed.
Utilizing Sucrose static code analysis allows Elysia to be faster than most Web-Standard frameworks like Hono, h3, and even native Node frameworks like Fastify, Express.

Elysia now supports runtime adapters for:
- Bun
- Web Standard (WinterCG) eg. Deno, Browser
- Node (beta)
Although Node adapter is still in beta, it has most of the features you expect from returning Generator Stream to WebSocket. We recommend you try it out.
We will continue to expand support for more runtimes in the future starting with:
- Cloudflare Worker
- AWS Lambda
- uWebSocket.js
Universal runtime API
Section titled “Universal runtime API”To be compatible with different runtimes, Elysia now wraps hand-picked utility functions to provide a consistent API across different runtimes.
For example, in Bun you may use Bun.file to return a file response which is not available in Node.
import { Elysia } from 'elysia'import { Elysia, file } from 'elysia'
new Elysia() .get('/', () => Bun.file('./public/index.html')) .get('/', () => file('./public/index.html'))These utility functions are replicas of Bun’s utility functions designed to be compatible with runtimes that Elysia supports, which will be expanded in the future.
Currently, Elysia has support for:
file- Return a file responseform- Return a formdata responseserver- A port of Bun’sServertype declaration
Macro with resolve
Section titled “Macro with resolve”Starting with Elysia 1.2, you can now use resolve in macro.
import { Elysia } from 'elysia'
new Elysia() .macro({ user: (enabled: true) => ({ resolve: ({ cookie: { session } }) => ({ user: session.value! }) }) }) .get('/', ({ user }) => user, { user: true })With the new macro object syntax, instead of retrieving lifecycle you can now return it instead to reduce boilerplate.
Here’s a comparison between the old and new syntax:
// ✅ Object Macronew Elysia() .macro({ role: (role: 'admin' | 'user') => ({ beforeHandle: ({ cookie: { session } }) => ({ user: session.value! }) }) })
// ⚠️ Function Macronew Elysia() .macro(({ onBeforeHandle }) => { role(role: 'admin' | 'user') { onBeforeHandle(({ cookie: { session } }) => ({ user: session.value! }) } })Both syntaxes are supported but the new object syntax is recommended. We don’t have plans to remove the previous syntax but we will focus on the new object syntax with new features.
Name Parser
Section titled “Name Parser”Elysia 1.2 introduces a parser with a custom name, allowing you to specify which parser should be used for decoding the request body.
import { Elysia } from 'elysia'
new Elysia() .parser('custom', ({ contentType }) => { if(contentType === "application/kivotos") return 'nagisa' }) .post('/', ({ body }) => body, { parse: 'custom' })parser has a similar API to onParse but with a custom name allowing you to reference it in a route.
You may also reference Elysia’s built-in parser or provide multiple parsers to be used in order.
import { Elysia } from 'elysia'
new Elysia() .parser('custom', ({ contentType }) => { if(contentType === "application/kivotos") return 'nagisa' }) .post('/', ({ body }) => body, { parse: ['custom', 'json'] })Parsers will be called in order. If the parser does not return a value, it will move to the next parser until one of the parsers returns a value.
WebSocket
Section titled “WebSocket”We have rewritten WebSocket to be more performant and match the API and behavior of Bun’s latest WebSocket API while maintaining compatibility with each runtime.
new Elysia() .ws('/ws', { ping: (message) => message, pong: (message) => message })WebSocket now has a more consistent API with HTTP routes and has a lifecycle similar to HTTP routes.
TypeBox 0.34
Section titled “TypeBox 0.34”Elysia 1.2 now has support for TypeBox 0.34.
With this update, Elysia now uses TypeBox’s t.Module to handle reference models to support circular recursive types.
import { Elysia, t } from 'elysia'
new Elysia() .model({ a: t.Object({ a: t.Optional(t.Ref('a')) }) }) .post('/recursive', ({ body }) => body, { body: 'a' })Reduced Memory usage
Section titled “Reduced Memory usage”We have refactored Sucrose’s generated code for a swappable code generation process.
This refactoring reduces code duplication, optimizes the router, and removes unnecessary code.
This allows Elysia to reuse code from several parts and reduce a large portion of memory usage.
For our project, by simply upgrading to Elysia 1.2, we have seen up to 2x memory reduction from 1.1.

This memory optimization is scaled with the number of routes and the complexity of the route. So you may see reduced memory usage exponentially.
Notable update
Section titled “Notable update”Below are some of the notable improvements that are made in Elysia 1.2.
Eden validation error
Section titled “Eden validation error”Eden also now automatically infers 422 status code if a validation model is provided.
import { treaty } from '@elysiajs/eden'import type { App } from './app'
const api = treaty<App>('localhost:3000')
const { data, error } = await api.user.put({ name: 'saltyaom'})
if(error) switch(error.status) { case 422: console.log(error.summary) break
default: console.error(error) }Router
Section titled “Router”We have updated route registration deduplication to be more optimized.
Previously Elysia would check all of the possible routes to prevent route duplication. Now Elysia uses a checksum hash map to check if the route is already registered and merges the routing and code generation process loops for static route registration to improve performance.
Changes
Section titled “Changes”- Event Listener now infers path parameter automatically based on scope
- Add ‘scoped’ to bulk
asfor casting type to ‘scoped’ similar to ‘plugin’ - Update
cookieto 1.0.1 - Update TypeBox to 0.33
content-lengthnow accepts numbers- use 16-digit hexadecimals for
idintrace - disable
minifyin production build for better debugging/error reporting
Breaking Change
Section titled “Breaking Change”There should be minor required changes to your codebase to adapt to upgrade to Elysia 1.2.
However, these are all the changes that you need to be aware of.
type is now merged with parse to allow control over the order of custom and built-in parsers.
import { Elysia, form, file } from 'elysia'
new Elysia() .post('/', ({ body }) => body, { type: 'json' parse: 'json' })formdata
Section titled “formdata”Starting from 1.2, you now have to explicitly return form if the response is a formdata instead of automatically detecting if a file is present in a 1-level deep object.
import { Elysia, form, file } from 'elysia'
new Elysia() .post('/', ({ file }) => ({ .post('/', ({ file }) => form({ a: file('./public/kyuukurarin.mp4') }))WebSocket
Section titled “WebSocket”WebSocket methods now return their respective values instead of returning WebSocket.
This removes the ability to do method chaining.
This is to make WebSocket match the same API with Bun’s WebSocket API for better compatibility and migration.
import { Elysia } from 'elysia'
new Elysia() .ws('/', { message(ws) { ws .send('hello') .send('world')
ws.send('hello') ws.send('world') } })scoped
Section titled “scoped”Elysia has now removed the constructor scoped as it might be confused with scope's scoped/global.
import { Elysia } from 'elysia'
new Elysia({ scoped: false })
const scoped = new Elysia()
const main = new Elysia() .mount(scoped)Internal breaking changes
Section titled “Internal breaking changes”- Remove router internal property static.http.staticHandlers
- Router history compile now links with history composed
Afterword
Section titled “Afterword”Elysia 1.2 is an ambitious update that we have been working on for a while.
A gambit to expand Elysia’s reach to more developers and more runtimes, but there is something else I want to say as well.
Let’s start this again.
Section titled “Let’s start this again.”Hi, how are you? I hope you are doing well.
I still love doing this, writing blog posts about Elysia. It has been a while since then.
You may have noticed that it has been a while since the last update, and it’s not a long one. I’m sorry for that.
I hope you understand that we also have our own lives to take care of. We are not robots, we are human. Sometimes it’s about life, sometimes about work, sometimes about family, sometimes about finances.
I want to be with you all the time.
Section titled “I want to be with you all the time.”I’m doing what I love. I keep updating Elysia, keep writing blog posts, keep making art, but you know that I have something to take care of too.
I have to bring food to the table. I have to take care of a lot of things financially. I have to take care of myself too.
I hope you are doing well, I hope you are happy, I hope you are healthy, I hope you are safe.
Even if I’m not here, a part of me known as Elysia will be with you.
Thanks for being with me.
Here I feel the touch brought by the real number solution.
Ripples of two souls now have reached our double slits.
Casting stripes of light and dark in the world as days and nights.
You set me free under the sun.
I fly your cradle dreams to the moon and back.
A worm will turn into a butterfly,
before one can answer “who am I”.
After the ice turns into water,
the sea I hang upside down will be your sky.
And we finally met again, Seele.

