Skip to content
ElysiaJS
← Blog

Release

Elysia 2 RC - またおいで

saltyaom

@saltyaom

Elysia-chan in the center of the page in light rain, with a greenish-blue overlay like a rainforest shower

Welcome back~

After a year of development, Elysia 2 RC is finally here.

Elysia 2 is still named DayDream, but the development took a long time, and the improvements from beta to RC were big enough to justify calling it Elysia 2.1. We decided to name this release as well.

Elysia 2 RC is named after またおいで (Please come again) by South& to honor the improvements we’ve made.

We recommended reading the beta release note first, as this release builds upon it.


First, Elysia 2 is about a better foundation.

We entirely rewrote Elysia to shift its focus from throughput alone to efficiency across all axes, starting with memory usage.

We invested in understanding the internal memory structures of JSC and V8 and how to optimize Elysia’s memory usage.

Static Route Memory usage Dynamic Route Memory usage

This means each route should use less memory than before. Even after a load test, peak memory shouldn’t increase as much and should stay close to its pre-test level.

Return status Memory usage Set Headers Memory usage

An actual codebase almost always uses set.headers and status on the response, so this improvement is relevant to real codebases.

Inline Response Memory usage Inline Guard Memory usage

This means that response schemas and guards use much less memory than before. This is relevant to a larger codebase rather than a “Hello World” test or benchmark.

You wouldn’t notice these improvements in a small “Hello World” demo benchmark. They become relevant in a real codebase, especially as the number of routes grows.

Here’s the measured startup memory usage for 25 and 400 routes.

Elysia 2
No validator
25 routes: 20 MB; 400 routes: 24 MB
TypeBox
25 routes: 31 MB; 400 routes: 37 MB
Zod
25 routes: 28 MB; 400 routes: 44 MB
Elysia 2 + AOT
No validator
25 routes: 19 MB; 400 routes: 23 MB
TypeBox
25 routes: 25 MB; 400 routes: 35 MB
Zod
25 routes: 28 MB; 400 routes: 44 MB
Hono
No validator
25 routes: 17 MB; 400 routes: 23 MB
TypeBox
25 routes: 33 MB; 400 routes: 49 MB
Zod
25 routes: 26 MB; 400 routes: 43 MB
H3
No validator
25 routes: 19 MB; 400 routes: 25 MB
TypeBox
25 routes: 31 MB; 400 routes: 49 MB
Zod
25 routes: 28 MB; 400 routes: 45 MB
Express
No validator
25 routes: 38 MB; 400 routes: 42 MB
TypeBox
25 routes: 44 MB; 400 routes: 61 MB
Zod
25 routes: 42 MB; 400 routes: 58 MB
Fastify
No validator
25 routes: 41 MB; 400 routes: 51 MB
TypeBox
25 routes: 65 MB; 400 routes: 100 MB
Zod
25 routes: 45 MB; 400 routes: 67 MB

Scroll horizontally to see all frameworks.

No validatorTypeBoxZod
Startup memory in MB. Values above each pair read 25/400 routes: the lighter left bar is 25 routes, and the stronger right bar is 400 routes. All bars share a 0–100 MB scale.

Here’s the average memory usage per route when scaling from 25 to 400 routes. The value is calculated by subtracting the 25-route measurement from the 400-route measurement, and then dividing by the 375 added routes.

Elysia 2
No validator
10.7 kB per route
TypeBox
16 kB per route
Zod
42.7 kB per route
Elysia 2 + AOT
No validator
10.7 kB per route
TypeBox
26.7 kB per route
Zod
42.7 kB per route
Hono
No validator
16 kB per route
TypeBox
42.7 kB per route
Zod
45.3 kB per route
H3
No validator
16 kB per route
TypeBox
48 kB per route
Zod
45.3 kB per route
Express
No validator
10.7 kB per route
TypeBox
45.3 kB per route
Zod
42.7 kB per route
Fastify
No validator
26.7 kB per route
TypeBox
93.3 kB per route
Zod
58.7 kB per route

Scroll horizontally to see all frameworks.

No validatorTypeBoxZod
The change in startup memory from 25 to 400 routes, divided by the 375 added routes, shown in decimal kB per route (1 MB = 1,000 kB). Subtracting the 25-route measurement largely removes the fixed startup footprint. Lower is better.

Because Elysia 2 includes a JIT and static code analysis, its bundle size is larger than Hono’s and h3’s, so it has a larger fixed startup footprint.

But as the route count increases, Elysia 2 should be one of the most memory-efficient framework.


It wouldn’t be Elysia if we didn’t improve throughput. Elysia 2 RC is still the fastest framework on Bun and very close to Fastify on Node.js.

See HTTP Benchmark for the latest benchmark.

Even after a load test, Elysia 2’s peak memory usage is still very low and predictable compared to other frameworks.

BeforeAfter
  1. ElysiaBun
    42.2 MB53.5 MB
  2. h3Bun
    38.6 MB55 MB
  3. HonoBun
    38.6 MB55 MB
  4. EffectBun
    52.9 MB81.4 MB
  5. ElysiaNode
    109.4 MB178.1 MB
  6. ExpressBun
    69 MB183.9 MB
  7. EffectNode
    109.9 MB190.5 MB
  8. h3Node
    101.5 MB202.7 MB
  9. AdonisNode
    122.6 MB216.8 MB
  10. KoaNode
    111.6 MB217.4 MB
  11. NestNode
    135.4 MB221.7 MB
  12. HonoNode
    114.5 MB226 MB
  13. FastifyNode
    110.6 MB226.5 MB
  14. ExpressNode
    104.3 MB238.4 MB
The chart shows server RSS for the frameworks in the throughput video. Before is measured after startup validation, and After is measured after all benchmark workloads.

We have improved validation and coercion latency when using an Elysia.t schema.

validation-coercion-latency.webp


Previously, we optimized Elysia’s types to be as fast as possible within TypeScript’s limits, so we had to limit what Elysia could do to keep type-checking time reasonable.

But now that TypeScript 7 has been released with much faster inference, we can give Elysia 2 much deeper type inference while still being conscious of type-checking speed.

We have expanded type inference to cover more cases, especially in error handling.

Elysia infer error from all possible type

Elysia can now statically infer all possible types from macros, lifecycle hooks, and inline hooks. Even error responses can be mapped or overridden and remain statically analyzable.

This makes it easy to identify all possible errors and makes Eden Treaty and OpenAPI type generation more useful, as they can now generate much more accurate types for error responses.

OpenAPI type gen showing all possible error

With OpenAPI type generation, you no longer have to manually annotate errors. It lists all possible errors that the route can throw and generates a proper OpenAPI spec for them.

This is somewhat similar to neverthrow and covers a lot of Effect.ts error handling, but with Elysia’s ergonomics.

See Error Handling to learn how to structure your code to make Elysia infer errors.


Reverting breaking changes to the lifecycle API

Section titled “Reverting breaking changes to the lifecycle API”

Previously, in the beta, we renamed the lifecycle API to remove the on prefix. But after a lot of feedback, and to make migrating to Elysia 2 smoother, we decided to revert the lifecycle API to the original on prefix.

Now Elysia 1 plugins should just work with Elysia 2 without any modifications.

Error hook

However, the two-parameter onError hook still has one breaking change: code is now an Error instance. Most plugins should still work.


We have a rule to reduce explicit types as much as possible by making Elysia infer them for you. But sometimes, the cases are too limited, and giving you complete control is better than trying to infer the types for you.

With Elysia 2, you can now annotate and control macro types explicitly.

Macro Type

This is less ergonomic than we want, but it gives you more control. Consider it an opt-in advanced feature for type wizards and plugin developers.

See Macro Type Lambda for how to use it.


Along with macro types, we are now introducing Eden Plugin with an explicit type.

Eden Plugin

This lets you control the plugin’s type so you can add custom fields or create new extensions to Eden Treaty.

See Eden Treaty Plugin for how to use it.


Elysia is a Web Standard framework similar to Hono. This means Elysia should be able to run on any runtime that supports Web Standard APIs.

Because Node.js doesn’t provide a Web Standard HTTP server out of the box, we have to provide a compatibility layer to make Elysia run on Node.js, similar to other frameworks like h3, Hono, Next.js, Nuxt, and Astro.

We are now using srvx and crossws on Node.js, which also form the foundation of h3.

This allows Elysia to maintain good performance and stability on Node.js while still supporting Web Standard APIs.


We have added an officially maintained compression plugin to Elysia 2. It is a simple plugin that compresses the response body using gzip or brotli.

import { compress } from '@elysia/compress'
new Elysia()
.use(compress())

It will use Bun’s fast path when possible and fall back to the runtime’s native compression.

See Compression Plugin for more details.


Elysia 2 RC was supposed to be released about two months ago. But because we wanted to make sure the release was stable and smooth, we decided to delay it for intensive testing.

The number of test cases has tripled since the beta release and is around 10 times what it was in 1.4, with several performance, memory-usage, and type-inference gates to make sure Elysia doesn’t regress in the future.

We developed Elysia 2 while integrating it with our internal servers to make sure it’s ready for production.

We are quite confident that Elysia 2 RC is stable and ready for production.

What you will see in Elysia 2 is not better performance, but much lower memory usage and startup times.


We recommend starting new projects with Elysia 2 RC and planning to migrate existing projects to Elysia 2 RC. The migration should be relatively easy with the codemod we provide:

Terminal window
bunx @elysia/codemod@latest

You can create a new project with Elysia 2 RC using:

Terminal window
bun create elysia && bun add elysia@rc

We also update Elysiajs skills for LLM with a much better structure as well. We recommend updating your skills to the latest version with:

bunx skills add elysiajs/skills

This release also comes with a new documentation site (you are currently viewing it).

The new documentation is revised to be more beginner-friendly and updated for Elysia 2.

We have rewrite the entire from Vitepress to Astro Starlight for more control over what we can do with the documentation. We write a lot of new component like lifecycle playground with our in-house design language.

We recommended you checking it out from the documentation home page. Many pages has been summarized and rewritten to be more beginner-friendly, and we have added several new content.


I have never thought that it would be so hard to create something over a year and not release it.

From the outside, Elysia must seem like it has been stale for a year, even though we have been working harder than on any other version of Elysia.

It’s hard not to rush it. I wake up every day wanting to release it.

This reminds me of the song またおいで (Please come again) by South&. The song is about waiting for someone to come back.

I know that there are a lot of people who have been waiting.

I want to quickly go home.

I want to say that I have been working so hard every day.

I want to say that I have come back.

I want to give you the embrace you deserve.

But the right time for that will come.

It might be a year from now but I’ll bring my memory back.

Until then, I’ll let you have a piece of me in your heart.


Coincidentally, Elysia 2 RC is ready just before the end of the rainy season here, as the song keeps repeating:

“I’ll be back before the rainy season is over”

This wouldn’t be possible without all the support from the community and our GitHub sponsors, especially Jarred Sumner (Bun). Their support allows us to work on Elysia full-time.

For you it might have been another software release but for me it’s a year of my life.

I hope you can feel the love and care I put into it.

Please don’t cry, please don’t cry

I’ll be back when the rainy season is over

Please come again, please come again

May the scent of hollyhocks guide you here

I’ll stay within your heart.

Thank you for waiting for Elysia 2 RC.

- SaltyAom