宏
宏类似于一个函数,能够对生命周期事件、模式、上下文进行控制,并具备完全的类型安全。
一旦定义,它将在钩子中可用,并且可以通过添加该属性来激活。
import { Elysia } from 'elysia'
const plugin = new Elysia({ name: 'plugin' })
.macro({
hi: (word: string) => ({
beforeHandle() {
console.log(word)
}
})
})
const app = new Elysia()
.use(plugin)
.get('/', () => 'hi', {
hi: 'Elysia'
})访问该路径时,应会在控制台输出 "Elysia"。
属性简写
从 Elysia 1.2.10 开始,宏对象中的每个属性都可以是一个函数或一个对象。
如果属性是对象,它将被转换为一个接受布尔参数的函数,并且在参数为 true 时执行。
import { Elysia } from 'elysia'
export const auth = new Elysia()
.macro({
// This property shorthand
isAuth: {
resolve: () => ({
user: 'saltyaom'
})
},
// is equivalent to
isAuth(enabled: boolean) {
if(!enabled) return
return {
resolve() {
return {
user
}
}
}
}
})错误处理
你可以通过返回一个 status 来返回错误的 HTTP 状态。
import { Elysia, status } from 'elysia'
new Elysia()
.macro({
auth: {
resolve({ headers }) {
if(!headers.authorization)
return status(401, 'Unauthorized')
return {
user: 'SaltyAom'
}
}
}
})
.get('/', ({ user }) => `Hello ${user}`, {
// ^?
auth: true
})建议您使用 return status 而不是 throw new Error() 来标注正确的 HTTP 状态码。
如果您抛出错误,Elysia 默认会将其转换为 500 Internal Server Error。
同样建议使用 return status 而不是 throw status,以确保 Eden 和 OpenAPI Type Gen 都能进行类型推断。
解析 (Resolve)
通过返回一个带有 resolve 函数的对象,您可以将属性添加到上下文中。
import { Elysia } from 'elysia'
new Elysia()
.macro({
user: (enabled: true) => ({
resolve: () => ({
user: 'Pardofelis'
})
})
})
.get('/', ({ user }) => user, {
user: true
})在上面的示例中,我们通过返回一个带有 resolve 函数的对象向上下文添加了一个新属性 user。
以下是一个宏解析可能有用的示例:
- 执行身份验证并将用户添加到上下文
- 运行额外的数据库查询并将数据添加到上下文
- 向上下文添加新属性
带有解析的宏扩展
由于 TypeScript 的限制,扩展其他宏的宏无法推断 resolve 函数的类型。
我们提供了一个命名的单一宏作为解决此限制的变通方法。
import { Elysia, t } from 'elysia'
new Elysia()
.macro('user', {
resolve: () => ({
user: 'lilith' as const
})
})
.macro('user2', {
user: true,
resolve: ({ user }) => {
}
})架构
您可以为您的宏定义一个自定义架构,以确保使用该宏的路由传递正确的类型。
import { Elysia, t } from 'elysia'
new Elysia()
.macro({
withFriends: {
body: t.Object({
friends: t.Tuple([t.Literal('Fouco'), t.Literal('Sartre')])
})
}
})
.post('/', ({ body }) => body.friends, {
body: t.Object({
name: t.Literal('Lilith')
}),
withFriends: true
})带有模式的宏将自动进行校验并推断类型,确保类型安全,并且可以与现有的模式共存。
您也可以堆叠来自不同宏的多个模式,甚至与标准验证器配合使用,它们将无缝协作。
同一宏内带生命周期的模式
类似于 带有解析的宏扩展,
宏模式也支持在同一宏内的生命周期中进行类型推断,但仅限于命名的单一宏,这是由于 TypeScript 的限制。
import { Elysia, t } from 'elysia'
new Elysia()
.macro('withFriends', {
body: t.Object({
friends: t.Tuple([t.Literal('Fouco'), t.Literal('Sartre')])
}),
beforeHandle({ body: { friends } }) {
}
})如果您想在同一宏内使用生命周期类型推断,建议使用命名的单一宏,而非多个叠加宏。
不要将此与使用宏模式推断路由生命周期事件中的类型混淆。后者运行良好,此限制仅针对在同一宏中使用生命周期。
扩展
宏可以扩展其他宏,允许您基于已有宏进行构建。
import { Elysia, t } from 'elysia'
new Elysia()
.macro({
sartre: {
body: t.Object({
sartre: t.Literal('Sartre')
})
},
fouco: {
body: t.Object({
fouco: t.Literal('Fouco')
})
},
lilith: {
fouco: true,
sartre: true,
body: t.Object({
lilith: t.Literal('Lilith')
})
}
})
.post('/', ({ body }) => body, {
lilith: true
})
这允许您基于已有宏构建,并为其添加更多功能。
去重
宏会自动去重生命周期事件,确保每个生命周期事件只执行一次。
默认情况下,Elysia 会使用属性值作为种子,但您也可以通过提供自定义种子来覆盖它。
import { Elysia, t } from 'elysia'
new Elysia()
.macro({
sartre: (role: string) => ({
seed: role,
body: t.Object({
sartre: t.Literal('Sartre')
})
})
})不过,如果您不慎创建了循环依赖,Elysia 会有一个16层的限制堆栈,以防止运行时和类型推断中出现无限循环。
如果路由已经有 OpenAPI 详情,它将合并该详情,但优先保留路由的详情,而非宏的详情。