elysiajs-expert
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseElysiaJS Expert Skill
ElysiaJS 专家技能指南
This skill provides comprehensive expertise for building high-performance, fully type-safe web applications with Elysia on the Bun runtime. It assumes the skill is active for Bun-specific patterns (file I/O, SQLite, testing, builds).
bun-expert本技能为基于Bun运行时,使用Elysia构建高性能、完全类型安全的Web应用提供全面的专业指导。它假设已激活技能,以支持Bun特定的使用模式(文件I/O、SQLite、测试、构建)。
bun-expertWhen to Use This Skill
适用场景
- Building REST APIs with Elysia
- Implementing type-safe request/response validation with TypeBox
- Setting up authentication (JWT, Bearer tokens, sessions)
- Creating WebSocket servers
- Generating OpenAPI/Swagger documentation
- Building full-stack applications with Eden Treaty
- Configuring Elysia plugins (CORS, static files, cron, GraphQL, tRPC)
- Testing Elysia applications
- Production deployment optimization
- 使用Elysia构建REST API
- 基于TypeBox实现类型安全的请求/响应验证
- 配置认证机制(JWT、Bearer令牌、会话)
- 创建WebSocket服务器
- 生成OpenAPI/Swagger文档
- 结合Eden Treaty构建全栈应用
- 配置Elysia插件(CORS、静态文件、定时任务、GraphQL、tRPC)
- 测试Elysia应用
- 生产环境部署优化
Quick Start
快速开始
typescript
import { Elysia, t } from 'elysia'
const app = new Elysia()
.get('/', () => 'Hello Elysia')
.get('/user/:id', ({ params }) => `User ${params.id}`)
.post('/user', ({ body }) => body, {
body: t.Object({
name: t.String(),
email: t.String({ format: 'email' })
})
})
.listen(3000)
export type App = typeof app // Export for Eden clienttypescript
import { Elysia, t } from 'elysia'
const app = new Elysia()
.get('/', () => 'Hello Elysia')
.get('/user/:id', ({ params }) => `User ${params.id}`)
.post('/user', ({ body }) => body, {
body: t.Object({
name: t.String(),
email: t.String({ format: 'email' })
})
})
.listen(3000)
export type App = typeof app // 导出给Eden客户端使用Core Concepts
核心概念
Elysia Constructor Options
Elysia构造函数选项
typescript
new Elysia({
name: 'my-app', // Plugin deduplication identifier
prefix: '/api', // Route prefix
seed: config, // Deduplication checksum seed
websocket: { // WebSocket configuration
idleTimeout: 30,
maxPayloadLength: 16777216
}
})typescript
new Elysia({
name: 'my-app', // 插件去重标识符
prefix: '/api', // 路由前缀
seed: config, // 去重校验和种子
websocket: { // WebSocket配置
idleTimeout: 30,
maxPayloadLength: 16777216
}
})HTTP Methods
HTTP方法
typescript
app
.get('/path', handler) // GET request
.post('/path', handler) // POST request
.put('/path', handler) // PUT request
.delete('/path', handler) // DELETE request
.patch('/path', handler) // PATCH request
.options('/path', handler) // OPTIONS request
.all('/path', handler) // All methods
.route('CUSTOM', '/path', handler) // Custom HTTP verbtypescript
app
.get('/path', handler) // GET请求
.post('/path', handler) // POST请求
.put('/path', handler) // PUT请求
.delete('/path', handler) // DELETE请求
.patch('/path', handler) // PATCH请求
.options('/path', handler) // OPTIONS请求
.all('/path', handler) // 所有方法
.route('CUSTOM', '/path', handler) // 自定义HTTP动词Path Parameters
路径参数
typescript
.get('/user/:id', ({ params }) => params.id) // Required param
.get('/user/:id?', ({ params }) => params.id ?? 'n/a') // Optional param
.get('/files/*', ({ params }) => params['*']) // Wildcard
.get('/org/:org/repo/:repo', ({ params }) => params) // Multiple paramstypescript
.get('/user/:id', ({ params }) => params.id) // 必填参数
.get('/user/:id?', ({ params }) => params.id ?? 'n/a') // 可选参数
.get('/files/*', ({ params }) => params['*']) // 通配符
.get('/org/:org/repo/:repo', ({ params }) => params) // 多个参数Context Object
上下文对象
Every handler receives a context object with:
typescript
{
body, // Parsed request body
query, // Query string as object
params, // Path parameters
headers, // Request headers (lowercase keys)
cookie, // Cookie jar with get/set
store, // Global mutable state
set, // Response setters (status, headers)
request, // Raw Request object
path, // Request path
server, // Bun server instance
redirect, // Redirect function
status, // Status response function
// + decorated/derived properties
}每个处理器都会接收一个上下文对象,包含以下属性:
typescript
{
body, // 解析后的请求体
query, // 解析为对象的查询字符串
params, // 路径参数
headers, // 请求头(键为小写)
cookie, // 包含get/set方法的Cookie容器
store, // 全局可变状态
set, // 响应设置器(状态码、响应头)
request, // 原始Request对象
path, // 请求路径
server, // Bun服务器实例
redirect, // 重定向函数
status, // 状态码响应函数
// + 装饰/派生的属性
}Response Patterns
响应模式
typescript
// String
.get('/', () => 'Hello')
// JSON (auto-serialized)
.get('/json', () => ({ hello: 'world' }))
// Status with response
.get('/error', ({ status }) => status(418, "I'm a teapot"))
// Custom headers
.get('/custom', ({ set }) => {
set.headers['x-powered-by'] = 'Elysia'
return 'Hello'
})
// Redirect
.get('/old', ({ redirect }) => redirect('/new'))
// File
import { file } from 'elysia'
.get('/image', () => file('image.png'))
// Streaming (generator)
.get('/stream', function* () {
yield 'Hello '
yield 'World'
})
// Async streaming
.get('/async', async function* () {
for (let i = 0; i < 10; i++) {
yield `Event ${i}\n`
await Bun.sleep(100)
}
})typescript
// 字符串响应
.get('/', () => 'Hello')
// JSON响应(自动序列化)
.get('/json', () => ({ hello: 'world' }))
// 带状态码的响应
.get('/error', ({ status }) => status(418, "I'm a teapot"))
// 自定义响应头
.get('/custom', ({ set }) => {
set.headers['x-powered-by'] = 'Elysia'
return 'Hello'
})
// 重定向
.get('/old', ({ redirect }) => redirect('/new'))
// 文件响应
import { file } from 'elysia'
.get('/image', () => file('image.png'))
// 流式响应(生成器)
.get('/stream', function* () {
yield 'Hello '
yield 'World'
})
// 异步流式响应
.get('/async', async function* () {
for (let i = 0; i < 10; i++) {
yield `Event ${i}\n`
await Bun.sleep(100)
}
})Lifecycle Hooks (Execution Order)
生命周期钩子(执行顺序)
Request → Parse → Transform → Validation → BeforeHandle → Handler → AfterHandle → MapResponse → AfterResponse
Request → Parse → Transform → Validation → BeforeHandle → Handler → AfterHandle → MapResponse → AfterResponse
onRequest (Global, Before Routing)
onRequest(全局,路由匹配前)
typescript
.onRequest(({ request, ip, set, status }) => {
// Rate limiting, CORS preflight, request logging
if (rateLimiter.exceeded(ip)) return status(429)
})typescript
.onRequest(({ request, ip, set, status }) => {
// 限流、CORS预检、请求日志
if (rateLimiter.exceeded(ip)) return status(429)
})onParse (Body Parser)
onParse(请求体解析)
typescript
.onParse(({ request, contentType }) => {
if (contentType === 'application/custom')
return request.text()
})
// Or specify parser explicitly
.post('/', handler, { parse: 'json' }) // 'json' | 'text' | 'formdata' | 'urlencoded' | 'none'typescript
.onParse(({ request, contentType }) => {
if (contentType === 'application/custom')
return request.text()
})
// 或者显式指定解析器
.post('/', handler, { parse: 'json' }) // 'json' | 'text' | 'formdata' | 'urlencoded' | 'none'onTransform (Before Validation)
onTransform(验证前)
typescript
.get('/id/:id', handler, {
transform({ params }) {
params.id = +params.id // Convert to number before validation
}
})typescript
.get('/id/:id', handler, {
transform({ params }) {
params.id = +params.id // 验证前转换为数字
}
})derive (Creates Context Properties - Before Validation)
derive(创建上下文属性 - 验证前)
typescript
.derive(({ headers }) => ({
bearer: headers.authorization?.startsWith('Bearer ')
? headers.authorization.slice(7)
: null
}))
.get('/protected', ({ bearer }) => bearer)typescript
.derive(({ headers }) => ({
bearer: headers.authorization?.startsWith('Bearer ')
? headers.authorization.slice(7)
: null
}))
.get('/protected', ({ bearer }) => bearer)onBeforeHandle (After Validation)
onBeforeHandle(验证后)
typescript
.onBeforeHandle(({ cookie, status }) => {
if (!validateSession(cookie.session.value))
return status(401, 'Unauthorized')
})
// Local hook
.get('/protected', handler, {
beforeHandle({ headers, status }) {
if (!headers.authorization) return status(401)
}
})typescript
.onBeforeHandle(({ cookie, status }) => {
if (!validateSession(cookie.session.value))
return status(401, 'Unauthorized')
})
// 局部钩子
.get('/protected', handler, {
beforeHandle({ headers, status }) {
if (!headers.authorization) return status(401)
}
})resolve (Creates Context Properties - After Validation, Type-Safe)
resolve(创建上下文属性 - 验证后,类型安全)
typescript
.guard({
headers: t.Object({ authorization: t.TemplateLiteral('Bearer ${string}') })
})
.resolve(({ headers }) => ({
token: headers.authorization.split(' ')[1],
userId: decodeToken(headers.authorization)
}))
.get('/me', ({ userId }) => userId)typescript
.guard({
headers: t.Object({ authorization: t.TemplateLiteral('Bearer ${string}') })
})
.resolve(({ headers }) => ({
token: headers.authorization.split(' ')[1],
userId: decodeToken(headers.authorization)
}))
.get('/me', ({ userId }) => userId)onAfterHandle (Transform Response)
onAfterHandle(转换响应)
typescript
.onAfterHandle(({ responseValue, set }) => {
if (isHtml(responseValue))
set.headers['content-type'] = 'text/html'
})typescript
.onAfterHandle(({ responseValue, set }) => {
if (isHtml(responseValue))
set.headers['content-type'] = 'text/html'
})mapResponse (Custom Response Mapping)
mapResponse(自定义响应映射)
typescript
.mapResponse(({ responseValue, set }) => {
set.headers['content-encoding'] = 'gzip'
return new Response(Bun.gzipSync(JSON.stringify(responseValue)))
})typescript
.mapResponse(({ responseValue, set }) => {
set.headers['content-encoding'] = 'gzip'
return new Response(Bun.gzipSync(JSON.stringify(responseValue)))
})onError (Error Handling)
onError(错误处理)
typescript
import { Elysia, NotFoundError } from 'elysia'
.onError(({ code, error, status }) => {
switch(code) {
case 'NOT_FOUND': return status(404, 'Not Found')
case 'VALIDATION': return { errors: error.all }
case 'PARSE': return status(400, 'Invalid body')
case 'INTERNAL_SERVER_ERROR': return status(500)
default: return new Response(error.toString())
}
})typescript
import { Elysia, NotFoundError } from 'elysia'
.onError(({ code, error, status }) => {
switch(code) {
case 'NOT_FOUND': return status(404, 'Not Found')
case 'VALIDATION': return { errors: error.all }
case 'PARSE': return status(400, 'Invalid body')
case 'INTERNAL_SERVER_ERROR': return status(500)
default: return new Response(error.toString())
}
})onAfterResponse (Cleanup, Logging)
onAfterResponse(清理、日志)
typescript
.onAfterResponse(({ set, request }) => {
console.log(`${request.method} ${request.url} - ${set.status}`)
})typescript
.onAfterResponse(({ set, request }) => {
console.log(`${request.method} ${request.url} - ${set.status}`)
})Hook Scoping
钩子作用域
typescript
// Hooks are LOCAL by default in Elysia 1.0+
.onBeforeHandle({ as: 'local' }, handler) // Current instance only
.onBeforeHandle({ as: 'scoped' }, handler) // Parent + current + descendants
.onBeforeHandle({ as: 'global' }, handler) // All instancestypescript
// 在Elysia 1.0+中,钩子默认是局部的
.onBeforeHandle({ as: 'local' }, handler) // 仅当前实例生效
.onBeforeHandle({ as: 'scoped' }, handler) // 父实例 + 当前实例 + 子实例生效
.onBeforeHandle({ as: 'global' }, handler) // 所有实例生效TypeBox Validation (Elysia.t)
TypeBox验证(Elysia.t)
Basic Types
基础类型
typescript
import { Elysia, t } from 'elysia'
.post('/user', handler, {
body: t.Object({
name: t.String({ minLength: 2, maxLength: 100 }),
email: t.String({ format: 'email' }),
age: t.Number({ minimum: 0, maximum: 150 }),
active: t.Boolean(),
tags: t.Array(t.String()),
role: t.Union([t.Literal('admin'), t.Literal('user')]),
metadata: t.Optional(t.Object({ createdAt: t.String() }))
})
})typescript
import { Elysia, t } from 'elysia'
.post('/user', handler, {
body: t.Object({
name: t.String({ minLength: 2, maxLength: 100 }),
email: t.String({ format: 'email' }),
age: t.Number({ minimum: 0, maximum: 150 }),
active: t.Boolean(),
tags: t.Array(t.String()),
role: t.Union([t.Literal('admin'), t.Literal('user')]),
metadata: t.Optional(t.Object({ createdAt: t.String() }))
})
})Schema Locations
验证规则位置
typescript
.post('/example', handler, {
body: t.Object({ ... }), // Request body
query: t.Object({ ... }), // Query string
params: t.Object({ ... }), // Path params
headers: t.Object({ ... }), // Headers (lowercase keys!)
cookie: t.Cookie({ ... }), // Cookies
response: t.Object({ ... }) // Response validation
})
// Response per status code
.get('/user', handler, {
response: {
200: t.Object({ user: UserSchema }),
400: t.Object({ error: t.String() }),
404: t.Object({ message: t.String() })
}
})typescript
.post('/example', handler, {
body: t.Object({ ... }), // 请求体验证
query: t.Object({ ... }), // 查询字符串验证
params: t.Object({ ... }), // 路径参数验证
headers: t.Object({ ... }), // 请求头验证(键为小写!)
cookie: t.Cookie({ ... }), // Cookie验证
response: t.Object({ ... }) // 响应验证
})
// 按状态码配置响应验证
.get('/user', handler, {
response: {
200: t.Object({ user: UserSchema }),
400: t.Object({ error: t.String() }),
404: t.Object({ message: t.String() })
}
})Elysia-Specific Types
Elysia专属类型
typescript
t.Numeric() // Coerces string to number (query/params)
t.File({ format: 'image/*' }) // Single file upload
t.Files() // Multiple files
t.Cookie({ session: t.String() }, {
secure: true, httpOnly: true, sameSite: 'strict'
})
t.TemplateLiteral('Bearer ${string}') // Template literal validation
t.UnionEnum(['draft', 'published']) // Enum-like uniontypescript
t.Numeric() // 将字符串转换为数字(查询/路径参数)
t.File({ format: 'image/*' }) // 单文件上传
t.Files() // 多文件上传
t.Cookie({ session: t.String() }, {
secure: true, httpOnly: true, sameSite: 'strict'
})
t.TemplateLiteral('Bearer ${string}') // 模板字面量验证
t.UnionEnum(['draft', 'published']) // 枚举风格联合类型Custom Error Messages
自定义错误消息
typescript
t.Object({
email: t.String({
format: 'email',
error: 'Please provide a valid email'
}),
age: t.Number({
minimum: 18,
error({ value }) {
return `Age must be 18+ (got ${value})`
}
})
})typescript
t.Object({
email: t.String({
format: 'email',
error: 'Please provide a valid email'
}),
age: t.Number({
minimum: 18,
error({ value }) {
return `Age must be 18+ (got ${value})`
}
})
})Standard Schema Support (Zod, Valibot)
标准Schema支持(Zod、Valibot)
typescript
import { z } from 'zod'
import * as v from 'valibot'
.get('/user/:id', handler, {
params: z.object({ id: z.coerce.number() }),
query: v.object({ name: v.literal('test') })
})typescript
import { z } from 'zod'
import * as v from 'valibot'
.get('/user/:id', handler, {
params: z.object({ id: z.coerce.number() }),
query: v.object({ name: v.literal('test') })
})State Management
状态管理
state (Global Mutable Store)
state(全局可变存储)
typescript
.state('counter', 0)
.state('users', new Map())
.get('/count', ({ store }) => store.counter++)typescript
.state('counter', 0)
.state('users', new Map())
.get('/count', ({ store }) => store.counter++)decorate (Immutable Context Properties)
decorate(不可变上下文属性)
typescript
.decorate('logger', new Logger())
.decorate('version', '1.0.0')
.decorate({ db: database, cache: redis })
.get('/', ({ logger, version }) => {
logger.log('Request')
return version
})typescript
.decorate('logger', new Logger())
.decorate('version', '1.0.0')
.decorate({ db: database, cache: redis })
.get('/', ({ logger, version }) => {
logger.log('Request')
return version
})Groups and Guards
分组与守卫
Groups (Route Prefixes)
分组(路由前缀)
typescript
.group('/api/v1', app => app
.get('/users', handler)
.post('/users', handler)
)
// With guard configuration
.group('/admin', {
headers: t.Object({ 'x-admin-key': t.String() })
}, app => app
.get('/stats', handler)
)typescript
.group('/api/v1', app => app
.get('/users', handler)
.post('/users', handler)
)
// 结合守卫配置
.group('/admin', {
headers: t.Object({ 'x-admin-key': t.String() })
}, app => app
.get('/stats', handler)
)Guards (Shared Validation/Hooks)
守卫(共享验证/钩子)
typescript
.guard({
headers: t.Object({ authorization: t.String() }),
beforeHandle: checkAuth
}, app => app
.get('/protected1', handler1)
.get('/protected2', handler2)
)typescript
.guard({
headers: t.Object({ authorization: t.String() }),
beforeHandle: checkAuth
}, app => app
.get('/protected1', handler1)
.get('/protected2', handler2)
)Plugin Architecture
插件架构
Creating Plugins
创建插件
typescript
// As Elysia instance (recommended)
const userPlugin = new Elysia({ name: 'user' })
.state('users', [])
.decorate('userService', new UserService())
.get('/users', ({ store }) => store.users)
// As function (access parent config)
const configPlugin = (config: Config) =>
new Elysia({ name: 'config', seed: config })
.decorate('config', config)
// Usage
new Elysia()
.use(userPlugin)
.use(configPlugin({ apiKey: '...' }))typescript
// 推荐方式:作为Elysia实例
const userPlugin = new Elysia({ name: 'user' })
.state('users', [])
.decorate('userService', new UserService())
.get('/users', ({ store }) => store.users)
// 函数方式(可访问父实例配置)
const configPlugin = (config: Config) =>
new Elysia({ name: 'config', seed: config })
.decorate('config', config)
// 使用方式
new Elysia()
.use(userPlugin)
.use(configPlugin({ apiKey: '...' }))Plugin Scoping
插件作用域
typescript
const authPlugin = new Elysia()
.onBeforeHandle({ as: 'scoped' }, checkAuth) // Applies to parent too
.derive({ as: 'global' }, getUser) // Applies everywhere
.as('scoped') // Lift entire plugintypescript
const authPlugin = new Elysia()
.onBeforeHandle({ as: 'scoped' }, checkAuth) // 同时作用于父实例
.derive({ as: 'global' }, getUser) // 作用于所有实例
.as('scoped') // 提升整个插件的作用域Lazy Loading
懒加载
typescript
.use(import('./heavy-plugin'))
await app.modules // Wait for all async pluginstypescript
.use(import('./heavy-plugin'))
await app.modules // 等待所有异步插件加载完成WebSocket Support
WebSocket支持
Basic WebSocket
基础WebSocket
typescript
.ws('/ws', {
message(ws, message) {
ws.send('Received: ' + message)
}
})typescript
.ws('/ws', {
message(ws, message) {
ws.send('Received: ' + message)
}
})Full WebSocket Handler
完整WebSocket处理器
typescript
.ws('/chat', {
// Validation
body: t.Object({ message: t.String() }),
query: t.Object({ room: t.String() }),
open(ws) {
const { room } = ws.data.query
ws.subscribe(room)
ws.publish(room, 'User joined')
},
message(ws, { message }) {
ws.publish(ws.data.query.room, message)
},
close(ws) {
ws.publish(ws.data.query.room, 'User left')
},
// Authentication
beforeHandle({ headers, status }) {
if (!headers.authorization) return status(401)
}
})typescript
.ws('/chat', {
// 验证规则
body: t.Object({ message: t.String() }),
query: t.Object({ room: t.String() }),
open(ws) {
const { room } = ws.data.query
ws.subscribe(room)
ws.publish(room, 'User joined')
},
message(ws, { message }) {
ws.publish(ws.data.query.room, message)
},
close(ws) {
ws.publish(ws.data.query.room, 'User left')
},
// 认证逻辑
beforeHandle({ headers, status }) {
if (!headers.authorization) return status(401)
}
})WebSocket Methods
WebSocket方法
typescript
ws.send(data) // Send to connection
ws.publish(topic, data) // Publish to topic
ws.subscribe(topic) // Subscribe to topic
ws.unsubscribe(topic) // Unsubscribe
ws.close() // Close connection
ws.data // Access context (query, params)
ws.id // Unique connection IDtypescript
ws.send(data) // 向连接发送数据
ws.publish(topic, data) // 向主题发布数据
ws.subscribe(topic) // 订阅主题
ws.unsubscribe(topic) // 取消订阅
ws.close() // 关闭连接
ws.data // 访问上下文(查询、参数)
ws.id // 唯一连接IDMacro Patterns
宏模式
typescript
const authPlugin = new Elysia({ name: 'auth' })
.macro({
isSignIn: {
async resolve({ cookie, status }) {
if (!cookie.session.value) return status(401)
return { user: await getUser(cookie.session.value) }
}
}
})
// Usage
.use(authPlugin)
.get('/profile', ({ user }) => user, { isSignIn: true })typescript
const authPlugin = new Elysia({ name: 'auth' })
.macro({
isSignIn: {
async resolve({ cookie, status }) {
if (!cookie.session.value) return status(401)
return { user: await getUser(cookie.session.value) }
}
}
})
// 使用方式
.use(authPlugin)
.get('/profile', ({ user }) => user, { isSignIn: true })Official Plugins
官方插件
@elysiajs/openapi (API Documentation)
@elysiajs/openapi(API文档)
typescript
import { openapi } from '@elysiajs/openapi'
.use(openapi({
provider: 'scalar', // 'scalar' | 'swagger-ui' | null
path: '/docs',
documentation: {
info: { title: 'My API', version: '1.0.0' },
tags: [{ name: 'User', description: 'User endpoints' }],
components: {
securitySchemes: {
bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }
}
}
},
exclude: { methods: ['OPTIONS'], paths: ['/health'] }
}))
.get('/user', handler, {
detail: {
tags: ['User'],
summary: 'Get user',
security: [{ bearerAuth: [] }]
}
})typescript
import { openapi } from '@elysiajs/openapi'
.use(openapi({
provider: 'scalar', // 'scalar' | 'swagger-ui' | null
path: '/docs',
documentation: {
info: { title: 'My API', version: '1.0.0' },
tags: [{ name: 'User', description: 'User endpoints' }],
components: {
securitySchemes: {
bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }
}
}
},
exclude: { methods: ['OPTIONS'], paths: ['/health'] }
}))
.get('/user', handler, {
detail: {
tags: ['User'],
summary: 'Get user',
security: [{ bearerAuth: [] }]
}
})@elysiajs/jwt (JSON Web Token)
@elysiajs/jwt(JSON Web Token)
typescript
import { jwt } from '@elysiajs/jwt'
.use(jwt({
name: 'jwt',
secret: process.env.JWT_SECRET!,
exp: '7d'
}))
.post('/login', async ({ jwt, body, cookie: { auth } }) => {
const token = await jwt.sign({ userId: body.id })
auth.set({ value: token, httpOnly: true, maxAge: 7 * 86400 })
return { token }
})
.get('/profile', async ({ jwt, bearer, status }) => {
const profile = await jwt.verify(bearer)
if (!profile) return status(401)
return profile
})typescript
import { jwt } from '@elysiajs/jwt'
.use(jwt({
name: 'jwt',
secret: process.env.JWT_SECRET!,
exp: '7d'
}))
.post('/login', async ({ jwt, body, cookie: { auth } }) => {
const token = await jwt.sign({ userId: body.id })
auth.set({ value: token, httpOnly: true, maxAge: 7 * 86400 })
return { token }
})
.get('/profile', async ({ jwt, bearer, status }) => {
const profile = await jwt.verify(bearer)
if (!profile) return status(401)
return profile
})@elysiajs/bearer (Token Extraction)
@elysiajs/bearer(令牌提取)
typescript
import { bearer } from '@elysiajs/bearer'
.use(bearer())
.get('/protected', ({ bearer, status }) => {
if (!bearer) return status(401)
return `Token: ${bearer}`
})typescript
import { bearer } from '@elysiajs/bearer'
.use(bearer())
.get('/protected', ({ bearer, status }) => {
if (!bearer) return status(401)
return `Token: ${bearer}`
})@elysiajs/cors (Cross-Origin)
@elysiajs/cors(跨域资源共享)
typescript
import { cors } from '@elysiajs/cors'
.use(cors({
origin: ['https://app.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 600
}))typescript
import { cors } from '@elysiajs/cors'
.use(cors({
origin: ['https://app.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 600
}))@elysiajs/static (Static Files)
@elysiajs/static(静态文件服务)
typescript
import { staticPlugin } from '@elysiajs/static'
.use(staticPlugin({
assets: 'public',
prefix: '/static',
indexHTML: true
}))typescript
import { staticPlugin } from '@elysiajs/static'
.use(staticPlugin({
assets: 'public',
prefix: '/static',
indexHTML: true
}))@elysiajs/html (HTML/JSX)
@elysiajs/html(HTML/JSX支持)
typescript
import { html } from '@elysiajs/html'
.use(html())
.get('/', () => `
<html>
<body><h1>Hello</h1></body>
</html>
`)typescript
import { html } from '@elysiajs/html'
.use(html())
.get('/', () => `
<html>
<body><h1>Hello</h1></body>
</html>
`)@elysiajs/cron (Scheduled Tasks)
@elysiajs/cron(定时任务)
typescript
import { cron } from '@elysiajs/cron'
.use(cron({
name: 'heartbeat',
pattern: '*/10 * * * * *', // Every 10 seconds
run() { console.log('tick') }
}))typescript
import { cron } from '@elysiajs/cron'
.use(cron({
name: 'heartbeat',
pattern: '*/10 * * * * *', // 每10秒执行一次
run() { console.log('tick') }
}))@elysiajs/graphql-yoga (GraphQL)
@elysiajs/graphql-yoga(GraphQL集成)
typescript
import { yoga } from '@elysiajs/graphql-yoga'
.use(yoga({
typeDefs: `type Query { hello: String }`,
resolvers: { Query: { hello: () => 'Hello' } },
path: '/graphql'
}))typescript
import { yoga } from '@elysiajs/graphql-yoga'
.use(yoga({
typeDefs: `type Query { hello: String }`,
resolvers: { Query: { hello: () => 'Hello' } },
path: '/graphql'
}))@elysiajs/trpc (tRPC Integration)
@elysiajs/trpc(tRPC集成)
typescript
import { trpc, compile as c } from '@elysiajs/trpc'
import { initTRPC } from '@trpc/server'
const tr = initTRPC.create()
const router = tr.router({
greet: tr.procedure
.input(c(t.String()))
.query(({ input }) => `Hello ${input}`)
})
.use(trpc(router, { endpoint: '/trpc' }))typescript
import { trpc, compile as c } from '@elysiajs/trpc'
import { initTRPC } from '@trpc/server'
const tr = initTRPC.create()
const router = tr.router({
greet: tr.procedure
.input(c(t.String()))
.query(({ input }) => `Hello ${input}`)
})
.use(trpc(router, { endpoint: '/trpc' }))@elysiajs/server-timing (Performance Headers)
@elysiajs/server-timing(性能监控头)
typescript
import { serverTiming } from '@elysiajs/server-timing'
.use(serverTiming({
enabled: process.env.NODE_ENV !== 'production'
}))typescript
import { serverTiming } from '@elysiajs/server-timing'
.use(serverTiming({
enabled: process.env.NODE_ENV !== 'production'
}))Eden Treaty (Type-Safe Client)
Eden Treaty(类型安全客户端)
Setup
配置
typescript
// server.ts
const app = new Elysia()
.get('/user/:id', ({ params }) => ({ id: params.id }))
.post('/user', ({ body }) => body, {
body: t.Object({ name: t.String() })
})
.listen(3000)
export type App = typeof app
// client.ts
import { treaty } from '@elysiajs/eden'
import type { App } from './server'
const api = treaty<App>('localhost:3000')typescript
// server.ts
const app = new Elysia()
.get('/user/:id', ({ params }) => ({ id: params.id }))
.post('/user', ({ body }) => body, {
body: t.Object({ name: t.String() })
})
.listen(3000)
export type App = typeof app
// client.ts
import { treaty } from '@elysiajs/eden'
import type { App } from './server'
const api = treaty<App>('localhost:3000')Path Syntax
路径语法
typescript
api.index.get() // /
api.user({ id: '123' }).get() // /user/123
api.deep.nested.path.get() // /deep/nested/pathtypescript
api.index.get() // /
api.user({ id: '123' }).get() // /user/123
api.deep.nested.path.get() // /deep/nested/pathRequest Parameters
请求参数
typescript
// POST with body
const { data, error } = await api.user.post({ name: 'John' })
// With headers/query
await api.user.post({ name: 'John' }, {
headers: { authorization: 'Bearer token' },
query: { source: 'web' }
})
// GET with query
await api.users.get({ query: { page: 1, limit: 10 } })typescript
// 带请求体的POST请求
const { data, error } = await api.user.post({ name: 'John' })
// 带请求头和查询参数
await api.user.post({ name: 'John' }, {
headers: { authorization: 'Bearer token' },
query: { source: 'web' }
})
// 带查询参数的GET请求
await api.users.get({ query: { page: 1, limit: 10 } })Error Handling
错误处理
typescript
const { data, error, status } = await api.user.post({ name })
if (error) {
switch(error.status) {
case 400: throw new ValidationError(error.value)
case 401: throw new AuthError(error.value)
default: throw error.value
}
}
return data // Type-safe, non-null after error checktypescript
const { data, error, status } = await api.user.post({ name })
if (error) {
switch(error.status) {
case 400: throw new ValidationError(error.value)
case 401: throw new AuthError(error.value)
default: throw error.value
}
}
return data // 错误检查后,data为类型安全的非空值WebSocket Client
WebSocket客户端
typescript
const chat = api.chat.subscribe()
chat.on('open', () => chat.send('hello'))
chat.subscribe(message => console.log(message))
chat.raw // Native WebSocket accesstypescript
const chat = api.chat.subscribe()
chat.on('open', () => chat.send('hello'))
chat.subscribe(message => console.log(message))
chat.raw // 原生WebSocket实例Stream Handling
流处理
typescript
const { data } = await api.stream.get()
for await (const chunk of data) {
console.log(chunk)
}typescript
const { data } = await api.stream.get()
for await (const chunk of data) {
console.log(chunk)
}Eden Configuration
Eden配置
typescript
const api = treaty<App>('localhost:3000', {
fetch: { credentials: 'include' },
headers: { authorization: 'Bearer token' },
headers: (path) => ({ /* dynamic headers */ }),
onRequest: (path, options) => { /* modify request */ },
onResponse: (response) => { /* modify response */ }
})typescript
const api = treaty<App>('localhost:3000', {
fetch: { credentials: 'include' },
headers: { authorization: 'Bearer token' },
headers: (path) => ({ /* 动态请求头 */ }),
onRequest: (path, options) => { /* 修改请求配置 */ },
onResponse: (response) => { /* 修改响应 */ }
})Unit Testing with Eden
使用Eden进行单元测试
typescript
import { treaty } from '@elysiajs/eden'
import { app } from './server'
// Pass instance directly - no network calls
const api = treaty(app)
const { data } = await api.user.post({ name: 'Test' })
expect(data.name).toBe('Test')typescript
import { treaty } from '@elysiajs/eden'
import { app } from './server'
// 直接传入实例 - 无需网络请求
const api = treaty(app)
const { data } = await api.user.post({ name: 'Test' })
expect(data.name).toBe('Test')Testing Patterns
测试模式
Unit Testing with bun:test
使用bun:test进行单元测试
typescript
import { describe, expect, it } from 'bun:test'
import { Elysia } from 'elysia'
describe('API', () => {
const app = new Elysia()
.get('/hello', () => 'Hello')
.post('/user', ({ body }) => body)
it('returns hello', async () => {
const res = await app.handle(new Request('http://localhost/hello'))
expect(await res.text()).toBe('Hello')
})
it('creates user', async () => {
const res = await app.handle(new Request('http://localhost/user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Test' })
}))
expect(await res.json()).toEqual({ name: 'Test' })
})
})typescript
import { describe, expect, it } from 'bun:test'
import { Elysia } from 'elysia'
describe('API', () => {
const app = new Elysia()
.get('/hello', () => 'Hello')
.post('/user', ({ body }) => body)
it('返回Hello', async () => {
const res = await app.handle(new Request('http://localhost/hello'))
expect(await res.text()).toBe('Hello')
})
it('创建用户', async () => {
const res = await app.handle(new Request('http://localhost/user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Test' })
}))
expect(await res.json()).toEqual({ name: 'Test' })
})
})Testing with Eden
使用Eden进行测试
typescript
import { treaty } from '@elysiajs/eden'
import { app } from './server'
const api = treaty(app)
it('should create user with type safety', async () => {
const { data, error } = await api.users.post({
name: 'John',
email: 'john@example.com'
})
expect(error).toBeNull()
expect(data?.name).toBe('John')
})typescript
import { treaty } from '@elysiajs/eden'
import { app } from './server'
const api = treaty(app)
it('应通过类型安全创建用户', async () => {
const { data, error } = await api.users.post({
name: 'John',
email: 'john@example.com'
})
expect(error).toBeNull()
expect(data?.name).toBe('John')
})Production Patterns
生产环境模式
Recommended Project Structure
推荐项目结构
src/
├── modules/
│ ├── auth/
│ │ ├── index.ts # Routes
│ │ ├── service.ts # Business logic
│ │ └── model.ts # TypeBox schemas
│ ├── user/
│ └── product/
├── shared/
│ ├── middleware/
│ └── utils/
├── config/
│ └── env.ts
├── index.ts
└── server.tssrc/
├── modules/
│ ├── auth/
│ │ ├── index.ts # 路由定义
│ │ ├── service.ts # 业务逻辑
│ │ └── model.ts # TypeBox Schema
│ ├── user/
│ └── product/
├── shared/
│ ├── middleware/
│ └── utils/
├── config/
│ └── env.ts
├── index.ts
└── server.tsModule Pattern
模块模式
typescript
// src/modules/user/index.ts
import { Elysia } from 'elysia'
import { UserService } from './service'
import { CreateUserSchema, UserSchema } from './model'
export const userRoutes = new Elysia({ prefix: '/users' })
.post('/', ({ body }) => UserService.create(body), {
body: CreateUserSchema,
response: UserSchema
})
.get('/:id', ({ params }) => UserService.findById(params.id))typescript
// src/modules/user/index.ts
import { Elysia } from 'elysia'
import { UserService } from './service'
import { CreateUserSchema, UserSchema } from './model'
export const userRoutes = new Elysia({ prefix: '/users' })
.post('/', ({ body }) => UserService.create(body), {
body: CreateUserSchema,
response: UserSchema
})
.get('/:id', ({ params }) => UserService.findById(params.id))Production Build
生产构建
bash
undefinedbash
undefinedCompile to binary
编译为二进制文件
bun build --compile --minify-whitespace --minify-syntax
--target bun-linux-x64 --outfile server src/index.ts
--target bun-linux-x64 --outfile server src/index.ts
undefinedbun build --compile --minify-whitespace --minify-syntax
--target bun-linux-x64 --outfile server src/index.ts
--target bun-linux-x64 --outfile server src/index.ts
undefinedCluster Mode
集群模式
typescript
import cluster from 'node:cluster'
import os from 'node:os'
if (cluster.isPrimary) {
for (let i = 0; i < os.availableParallelism(); i++) {
cluster.fork()
}
} else {
await import('./server')
}typescript
import cluster from 'node:cluster'
import os from 'node:os'
if (cluster.isPrimary) {
for (let i = 0; i < os.availableParallelism(); i++) {
cluster.fork()
}
} else {
await import('./server')
}Best Practices
最佳实践
- Always use method chaining - Maintains type inference
- Name plugins - Enables deduplication
- Use resolve over derive - When validation is needed first
- Export type App - For Eden client type safety
- Use guards - For shared validation across routes
- Local hooks by default - Explicit or
as: 'scoped'as: 'global' - Extract services - Outside Elysia for testability
- Use status() function - For type-safe status responses
- 始终使用方法链 - 保持类型推断能力
- 为插件命名 - 启用去重功能
- 优先使用resolve而非derive - 当需要先完成验证时
- 导出App类型 - 为Eden客户端提供类型安全
- 使用守卫 - 为多个路由共享验证逻辑
- 默认使用局部钩子 - 显式指定或
as: 'scoped'as: 'global' - 提取服务层 - 放在Elysia外部以提升可测试性
- 使用status()函数 - 实现类型安全的状态码响应
References
参考文档
See <reference/core-api.md> for complete API documentation.
See <reference/lifecycle-hooks.md> for hook execution details.
See <reference/plugins.md> for all plugin configurations.
See <patterns/authentication.md> for auth implementations.
See <patterns/testing.md> for testing strategies.
查看<reference/core-api.md>获取完整API文档。
查看<reference/lifecycle-hooks.md>获取钩子执行细节。
查看<reference/plugins.md>获取所有插件配置。
查看<patterns/authentication.md>获取认证实现方案。
查看<patterns/testing.md>获取测试策略。