Skip to content

¥Macro

宏类似于一个函数,可以控制生命周期事件、Schema 和上下文,并具有完全的类型安全性。

¥Macro is similar to a function that have a control over the lifecycle event, schema, context with full type safety.

一旦定义,它将在钩子中可用,并可通过添加属性来激活。

¥Once defined, it will be available in hook and can be activated by adding the property.

typescript
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" 记录为结果。

¥Accessing the path should log "Elysia" as the results.

属性简写

¥Property shorthand

从 Elysia 1.2.10 版本开始,宏对象中的每个属性可以是函数或对象。

¥Starting from Elysia 1.2.10, each property in the macro object can be a function or an object.

如果属性是对象,它将被转换为接受布尔参数的函数,并在参数为 true 时执行该函数。

¥If the property is an object, it will be translated to a function that accept a boolean parameter, and will be executed if the parameter is true.

typescript
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
					}
				}
         	}
        }
    })

API

宏与钩子具有相同的 API。

¥macro has the same API as hook.

在前面的示例中,我们创建了一个接受字符串的 hi 宏。

¥In previous example, we create a hi macro accepting a string.

然后,我们将 hi 赋值给 "Elysia",该值被返回给 hi 函数,之后该函数向 beforeHandle 堆栈添加了一个新事件。

¥We then assigned hi to "Elysia", the value was then sent back to the hi function, and then the function added a new event to beforeHandle stack.

这相当于将函数推送到 beforeHandle,如下所示:

¥Which is an equivalent of pushing function to beforeHandle as the following:

typescript
import { Elysia } from 'elysia'

const app = new Elysia()
    .get('/', () => 'hi', {
        beforeHandle() {
            console.log('Elysia')
        }
    })

当逻辑比接受新函数更复杂时,宏会大放异彩,例如为每个路由创建一个授权层。

¥macro shine when a logic is more complex than accepting a new function, for example creating an authorization layer for each route.

typescript
import { 
Elysia
} from 'elysia'
import {
auth
} from './auth'
const
app
= new
Elysia
()
.
use
(
auth
)
.
get
('/', ({
user
}) =>
user
, {
isAuth
: true,
role
: 'admin'
})

宏还可以向上下文注册一个新属性,从而允许我们直接从上下文访问该属性的值。

¥Macro can also register a new property to the context, allowing us to access the value directly from the context.

该字段可以接受从字符串到函数的任何内容,这使我们能够创建自定义生命周期事件。

¥The field can accept anything ranging from string to function, allowing us to create a custom life cycle event.

宏将根据钩子中的定义从上到下按顺序执行,确保堆栈以正确的顺序处理。

¥macro will be executed in order from top-to-bottom according to definition in hook, ensure that the stack is handled in the correct order.

解析

¥Resolve

通过返回带有 resolve 函数的对象,可以向上下文添加属性。

¥You add a property to the context by returning an object with a resolve function.

ts
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
macro
({
user
: (
enabled
: true) => ({
resolve
: () => ({
user
: 'Pardofelis'
}) }) }) .
get
('/', ({
user
}) =>
user
, {
user
: true
})

在上面的例子中,我们通过返回一个带有 resolve 函数的对象,向上下文中添加了一个新的属性 user。

¥In the example above, we add a new property user to the context by returning an object with a resolve function.

以下是宏解析可能有用的示例:

¥Here's an example that macro resolve could be useful:

  • 执行身份验证并将用户添加到上下文

  • 运行额外的数据库查询并将数据添加到上下文

  • 向上下文添加新属性

带解析的宏扩展

¥Macro extension with resolve

由于 TypeScript 的限制,扩展其他宏的宏无法将类型推断到 resolve 函数中。

¥Due to TypeScript limitation, macro that extends other macro cannot infer type into resolve function.

我们提供了一个命名的单宏作为解决此限制的解决方法。

¥We provide a named single macro as a workaround to this limitation.

typescript
import { 
Elysia
,
t
} from 'elysia'
new
Elysia
()
.
macro
('user', {
resolve
: () => ({
user
: 'lilith' as
const
}) }) .
macro
('user2', {
user
: true,
resolve
: ({
user
}) => {
} })

Schema

你可以为宏定义自定义架构,以确保使用该宏的路由传递正确的类型。

¥You can define a custom schema for your macro, to make sure that the route using the macro is passing the correct type.

typescript
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
})

带有 Schema 的宏会自动验证和推断类型以确保类型安全,并且它可以与现有 Schema 共存。

¥Macro with schema will automatically validate and infer type to ensure type safety, and it can co-exist with existing schema as well.

你还可以堆叠来自不同宏的多个模式,甚至来自标准验证器,它们将无缝协作。

¥You can also stack multiple schema from different macro, or even from Standard Validator and it will work together seamlessly.

Schema 生命周期在同一个宏中

¥Schema with lifecycle in the same macro

带解析的宏扩展 类似,

¥Similar to Macro extension with resolve,

宏 Schema 也支持同一宏内生命周期的类型推断,但由于 TypeScript 的限制,仅限于命名单个宏。

¥Macro schema also support type inference for lifecycle within the same macro BUT only with named single macro due to TypeScript limitation.

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
} }) {
} })

如果你想在同一个宏中使用生命周期类型推断,你可能需要使用命名的单个宏而不是多个堆叠宏。

¥If you want to use lifecycle type inference within the same macro, you might want to use a named single macro instead of multiple stacked macro

不要与使用宏模式推断路由生命周期事件的类型相混淆。这样就很好了,但这个限制仅适用于在同一个宏中使用生命周期。

扩展

¥Extension

宏可以扩展其他宏,允许你在现有宏的基础上进行构建。

¥Macro can extends other macro, allowing you to build upon existing one.

typescript
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
})

这允许你在现有宏的基础上构建并添加更多功能。

¥This allow you to build upon existing macro, and add more functionality to it.

数据去重

¥Deduplication

宏会自动删除重复的生命周期事件,确保每个生命周期事件只执行一次。

¥Macro will automatically deduplicate the lifecycle event, ensuring that each lifecycle event is only executed once.

默认情况下,Elysia 将使用属性值作为种子,但你可以通过提供自定义种子来覆盖它。

¥By default, Elysia will use the property value as the seed, but you can override it by providing a custom seed.

typescript
import { 
Elysia
,
t
} from 'elysia'
new
Elysia
()
.
macro
({
sartre
: (
role
: string) => ({
seed
:
role
,
body
:
t
.
Object
({
sartre
:
t
.
Literal
('Sartre')
}) }) })

但是,如果你意外创建了循环依赖,Elysia 的限制堆栈为 16,以防止运行时和类型推断中的无限循环。

¥However, if you evert accidentally create a circular dependency, Elysia have a limit stack of 16 to prevent infinite loop in both runtime and type inference.

如果路由已包含 OpenAPI 详细信息,它会将这些详细信息合并在一起,但优先使用路由详细信息而不是宏详细信息。

¥If the route already has OpenAPI detail, it will merge the detail together but prefers the route detail over macro detail.