Skip to main content
This guide covers migration between major versions of Elysia. Always review the full changelog before upgrading.

Migrating to Elysia 1.4

Elysia 1.4 introduces several improvements focused on security, performance, and developer experience.

Breaking changes

No breaking changes were introduced in 1.4. This is a backward-compatible release.

Key improvements

1

Enhanced security

Multiple security improvements were added:
  • Cookie prototype pollution prevention
  • Constant-time comparison for signed cookie verification
  • Cookie injection and RCE prevention
  • Reject invalid cookie signatures when using cookie rotation
If you’re using signed cookies, ensure you’re on 1.4.17 or later for critical security fixes.
2

Response handling improvements

The response handling system has been significantly improved:
// Set-Cookie headers are now preserved in mounted handlers
app.mount('/api', otherApp)

// Multiple set-cookie headers work correctly
app.get('/login', ({ cookie }) => {
  cookie.session.value = 'abc'
  cookie.user.value = 'john'
  return 'OK'
})
3

Standard Schema support

Elysia now supports Standard Schema validation alongside TypeBox:
import { Elysia } from 'elysia'
import { z } from 'zod'

const app = new Elysia()
  .get('/user/:id', ({ params }) => params, {
    params: z.object({
      id: z.string()
    })
  })
Update your dependencies:
bun update elysia
If using @types/bun, ensure compatibility:
bun add -d @types/bun@latest

Migrating to Elysia 1.3

Elysia 1.3 introduced significant performance improvements and new features.

Breaking changes

The as('plugin') method has been removed. Use as('scoped') instead.
// Before
const plugin = new Elysia()
  .derive(() => ({ value: 'hello' }))
  .as('plugin')

// After
const plugin = new Elysia()
  .derive(() => ({ value: 'hello' }))
  .as('scoped')
The root index path has been removed from Eden Treaty type generation.If you were relying on this, update your client code accordingly.
The inference.request configuration option has been removed.

Major features

1

Exact Mirror

Elysia 1.3 introduces Exact Mirror for improved type inference:
const app = new Elysia()
  .get('/user', ({ query }) => query, {
    query: t.Object({
      'user-name': t.String(),
      'user-id': t.Number()
    })
  })
2

Bun System Router

Enable native Bun routing for better performance:
const app = new Elysia({
  systemRouter: true
})
System Router is automatically enabled when using Bun 1.2.14 or later.
3

Standalone Validator

Validators can now be used independently:
import { t } from 'elysia'

const validator = t.Compile(t.Object({
  name: t.String(),
  age: t.Number()
}))

const result = validator.Check({ name: 'John', age: 25 })
4

File type validation

Automatic file type checking using the file-type library:
app.post('/upload', ({ body }) => body, {
  body: t.Object({
    file: t.File({
      type: ['image/png', 'image/jpeg']
    })
  })
})

Performance improvements

Elysia 1.3 includes numerous performance optimizations:
  • Reduced type checking overhead with AOT compilation
  • Optimized memory usage on route registration (~36% reduction)
  • Improved startup time with multiple routes
  • Dynamic cookie validator creation during compilation
  • Optimized query parsing with parseQueryFromURL

Migrating to Elysia 1.2

Elysia 1.2 brought universal runtime support and improved WebSocket handling.

Breaking changes

Elysia 1.2 removed the error function. Use status instead.
// Before (1.1)
app.get('/user', ({ error }) => {
  return error(404, 'Not found')
})

// After (1.2+)
app.get('/user', ({ status }) => {
  status(404)
  return 'Not found'
})

Universal Runtime Support

Elysia 1.2 introduces adapters for multiple runtimes:
import { Elysia } from 'elysia'

const app = new Elysia()
  .get('/', () => 'Hello Bun')
  .listen(3000)

Macro improvements

Macros now support resolve functionality:
app.macro(({ onResolve }) => ({
  auth(enabled: boolean) {
    if (!enabled) return
    
    onResolve(async ({ headers }) => {
      const token = headers.authorization?.split(' ')[1]
      if (!token) throw new Error('Unauthorized')
      
      return {
        user: await validateToken(token)
      }
    })
  }
}))

app.get('/profile', ({ user }) => user, {
  auth: true
})

Migrating to Elysia 1.1

Elysia 1.1 introduced Trace v2 and improved type coercion.

Breaking changes

Query parameters are now parsed as string instead of string | string[] unless explicitly specified.
// Before (1.0)
app.get('/search', ({ query }) => {
  const term: string | string[] = query.q
})

// After (1.1)
app.get('/search', ({ query }) => {
  const term: string = query.q // Single value
})

// For arrays, use t.Array
app.get('/filter', ({ query }) => query, {
  query: t.Object({
    tags: t.Array(t.String()) // string[]
  })
})
The lifecycle hook has been renamed for clarity:
// Before
app.onResponse(() => {
  console.log('Response sent')
})

// After
app.onAfterResponse(() => {
  console.log('Response sent')
})

New features

Optional path parameters

Define optional segments in your routes:
app.get('/user/:id?', ({ params }) => {
  return params.id ?? 'all users'
})

Generator response streams

Stream responses using generators:
app.get('/stream', function* () {
  yield 'chunk 1'
  yield 'chunk 2'
  yield 'chunk 3'
})

Data type coercion

Automatic type coercion for common types:
app.get('/user/:id', ({ params }) => {
  // id is automatically a number
  return { id: params.id }
}, {
  params: t.Object({
    id: t.Numeric()
  })
})

Trace v2

Enhanced tracing capabilities:
app.trace(async ({ handle }) => {
  console.log('Start:', handle.time)
})

Migrating to Elysia 1.0

Elysia 1.0 was the first stable release with significant API changes from 0.x versions.

Major changes from 0.x

1

Macro v1 removed

The original macro API has been completely redesigned. Review the macro documentation for the new API.
2

ObjectString/ArrayString security

Default values are no longer produced automatically for security reasons:
// Define defaults explicitly
app.get('/search', ({ query }) => query, {
  query: t.Object({
    tags: t.Array(t.String(), { default: [] })
  })
})
3

Cookie parsing improvements

Cookies now dynamically parse when format is likely JSON:
app.get('/', ({ cookie }) => {
  cookie.data.value = { user: 'john', role: 'admin' }
})

Version compatibility

Elysia follows semantic versioning:
VersionBun VersionTypeScriptStatus
1.4.x>= 1.2.0>= 5.0Current
1.3.x>= 1.2.0>= 5.0Supported
1.2.x>= 1.0.0>= 5.0Supported
1.1.x>= 1.0.0>= 5.0Legacy
1.0.x>= 1.0.0>= 5.0Legacy
Elysia is designed for Bun but supports other runtimes via adapters starting from version 1.2.

Getting help

If you encounter issues during migration:

Build docs developers (and LLMs) love