Skip to content
我们的赞助商

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

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

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" 作为结果。

属性简写

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

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

typescript
import { Elysia } from 'elysia'

export const auth = new Elysia()
    .macro({
    	// 此属性简写
    	isAuth: {
      		resolve: () => ({
      			user: 'saltyaom'
      		})
        },
        // 等同于
        isAuth(enabled: boolean) {
        	if(!enabled) return

        	return {
				resolve() {
					return {
						user
					}
				}
         	}
        }
    })

API

macro 具有与 hook 相同的 API。

在上一个示例中,我们创建了一个接受 stringhi macro。

然后我们将 hi 分配为 "Elysia",该值随后被发送回 hi 函数,然后该函数将一个新事件添加到 beforeHandle 堆栈中。

这等同于将函数推送到 beforeHandle,如下所示:

typescript
import { Elysia } from 'elysia'

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

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

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

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

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

macro 将按照 hook 中定义的从上到下的顺序执行,确保堆栈以正确的顺序处理。

Resolve

通过返回一个包含 resolve 函数的对象,将属性添加到上下文中。

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

在上例中,我们通过返回一个包含 resolve 函数的对象,将一个新属性 user 添加到上下文中。

这是一个 macro resolve 可能有用的示例:

  • 执行身份验证并将用户添加到上下文中
  • 运行额外的数据库查询并将数据添加到上下文中
  • 将新属性添加到上下文中

使用 resolve 的 Macro 扩展

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

我们提供了一个命名单个 macro 作为此限制的变通方法。

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

Schema

您可以为 macro 定义自定义模式,以确保使用 macro 的路由传递正确的类型。

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 的 Macro 将自动验证并推断类型以确保类型安全,并且它可以与现有 schema 共存。

您还可以堆叠来自不同 macro 的多个 schema,甚至来自 Standard Validator 的 schema,它们将无缝协作。

同一 Macro 中的 Schema 与生命周期

类似于 使用 resolve 的 Macro 扩展

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

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

如果您想在同一 macro 内使用生命周期类型推断,您可能想使用命名单个 macro 而不是多个堆叠 macro

不要与使用 macro schema 将类型推断到路由的生命周期事件混淆。那样做没问题,此限制仅适用于同一 macro 内的生命周期。

Extension

Macro 可以扩展其他 macro,允许您基于现有的进行构建。

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

这允许您基于现有 macro 进行构建,并为其添加更多功能。

Deduplication

Macro 将自动对生命周期事件进行去重,确保每个生命周期事件仅执行一次。

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

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

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

如果路由已经具有 OpenAPI 细节,它将合并细节,但优先使用路由细节而不是 macro 细节。