Elysia 0.8 – Gate of Steiner
Named after the ending song of Steins;Gate Zero, "Gate of Steiner".
Elysia 0.8 focuses on API stability and a solid foundation for the upcoming 1.0 release, while bringing a number of improvements and new features:
- Macro API
- New Life Cycle –
resolve,mapResponse - Error Function
- Static Content
- Default Property
- Default Header
- Performance and notable improvements
Macro API
Macro allows us to define a custom field to hook and guard by exposing full control over the lifecycle event stack, enabling the composition of custom logic into a simple configuration with full type safety.
import { Elysia } from 'elysia'
import { auth } from '@services/auth'
const app = new Elysia()
.use(auth)
.get('/', ({ user }) => user.profile, {
role: 'admin'
})
Macro has full access to the lifecycle stack, allowing us to add, modify, or delete existing events directly for each route:
const plugin = new Elysia({ name: 'plugin' }).macro(({ beforeHandle }) => {
return {
role(type: 'admin' | 'user') {
beforeHandle(
{ insert: 'before' },
async ({ cookie: { session } }) => {
const user = await validateSession(session.value)
await validateRole('admin', user)
}
)
}
}
})
The documentation for the Macro API is now available in the patterns section.
New Life Cycle
Elysia introduces a new lifecycle to fix an existing problem and add highly requested APIs, including Resolve and MapResponse:
resolve: a safe version ofderive. Executes in the same queue asbeforeHandle.mapResponse: executes just afterafterResponseto provide a transform function from a primitive value to a Web StandardResponse.
Resolve
A “safe” version of derive.
import { Elysia } from 'elysia'
new Elysia()
.guard({
headers: t.Object({
authorization: t.TemplateLiteral('Bearer ${string}')
})
}, (app) =>
app
.resolve(({ headers: { authorization } }) => {
return { bearer: authorization.split(' ')[1] }
})
.get('/', ({ bearer }) => bearer)
.listen(3000)
)
MapResponse
Executed just after afterHandle; designed to provide custom response mapping from a primitive value into a Web Standard Response.
import { Elysia, mapResponse } from 'elysia'
import { gzipSync } from 'bun'
new Elysia()
.mapResponse(({ response }) => {
return new Response(
typeof response === 'object'
? JSON.stringify(response)
: response.toString()
)
})
.listen(3000)
Why not use afterHandle? afterHandle is intended to read and modify a primitive value, whereas plugins that produce a Response (e.g., HTML, compression) need a dedicated mapping step. Introducing a separate lifecycle avoids interference between value mutation and response generation.
Error Function
You can set the status code by using set.status or returning a Response. This approach can be verbose and may require external utilities.
The new error function returns a status code alongside the value, helping Eden infer types correctly.
import { Elysia, error } from 'elysia'
new Elysia()
.get('/', () => error(418, "I'm a teapot"))
.listen(3000)
Equivalent to:
new Elysia()
.get('/', ({ set }) => {
set.status = 418
return "I'm a teapot"
})
.listen(3000)
The error function automatically differentiates the status code, simplifying type inference while preserving backward compatibility with set.status.
Static Content
Static content is a response that rarely changes. The new API allows the response to be compiled ahead of time, improving performance by 20–25 %.
new Elysia()
.get('/', Bun.file('video/kyuukurarin.mp4'))
.listen(3000)
Notice the handler is now an inline value rather than a function.
Default Property
Elysia 0.8 updates TypeBox to 0.32, adding the requested default field support.
new Elysia()
.get('/', ({ query: { name } }) => name, {
query: t.Object({
name: t.String({
default: 'Elysia'
})
})
})
.listen(3000)
This allows default values to be defined directly in the schema, especially useful with reference schemas.
Default Header
Elysia now supports setting default headers out of the box, improving performance by avoiding per‑request header mutation.
new Elysia()
.headers({
'X-Powered-By': 'Elysia'
})
The CORS plugin has also been updated to use this new API for better performance.
Performance and Notable Improvements
Removal of bind
.bind slows down path lookup by ~5 %; removing it speeds up that process.
Static Query Analysis
Elysia’s static code analysis can now infer a query if the query name is referenced in the code, yielding a 15–20 % speed‑up.
Video Stream
The content-range header is added to File and Blob by default to handle large files in chunks. It is omitted for status codes 206, 304, 412, or 416, or if the header is explicitly provided. See Discussion 371 for more details.
Runtime Memory Improvement
Elysia reuses the return value of lifecycle events instead of allocating new objects, slightly reducing memory usage under peak load.
Plugins
Most official plugins now use the new Elysia.headers, Static Content, mapResponse, and comply with static code analysis, improving overall performance.
Validation Error
Elysia now returns validation errors as JSON (instead of plain text) with the following structure:
{
"type": "query",
"at": "password",
"message": "Required property",
"expected": {
"email": "[email protected]",
"password": ""
},
"found": {
"email": "[email protected]",
"password": "..."
},
"errors": [
{
"type": 45,
"schema": {
"type": "string"
},
"path": "/password",
"message": "Required property"
},
{
"type": 54,
"schema": {
"type": "string"
},
"path": "/password",
"message": "Expected string"
}
]
}
expect and errors fields are omitted in production to avoid leaking schema information.
Notable Improvements
- Lazy query reference
- Add
content-rangeheader toBlobby default - Update TypeBox to 0.32
- Override lifecycle response of
beandaf
Breaking Change
afterHandleno longer triggers early return
Changes
- Validation response now JSON
deriveis differentiated fromdecorator['request']asdecorator['derive']deriveno longer shows inferred type inonRequest
Bug Fixes
- Remove
headers,pathfromPreContext - Remove
derivefromPreContext - Elysia types no longer output custom
error
Afterword
It has been a great journey since the first release. Elysia has evolved from a generic REST API framework to an ergonomic framework that supports end‑to‑end type safety and OpenAPI documentation generation.
We’re excited to continue introducing more features. Thanks to the community, Elysia ranks 32nd in the TechEmpower benchmark, surpassing Go’s Gin and Echo.
We’re preparing for the first stable release of Elysia (1.0), aiming for Q1 2024. A soft API lockdown will prevent breaking changes, ensuring that your app works from 0.7 onward.
Keep fighting for all that is beautiful in this world. Until then, El Psy Congroo.
A drop in the darkness
小さな命
Unique and precious forever
Bittersweet memories 夢幻の刹那
Make this moment last, last forever
We drift through the heavens 果てない想い
Filled with the love from up above
He guides my travels せまる刻限
Shed a tear and leap to a new world
← Blog
← Elysia: Ergonomic Framework for Humans
Back to the next page: At Glance