Skip to content
在 AI 工具中打开 Anthropic

宏 ​

宏类似于一个函数,能够对生命周期事件、模式、上下文进行控制,并具备完全的类型安全。

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

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 开始,宏对象中的每个属性都可以是一个函数或一个对象。

如果属性是对象,它将被转换为一个接受布尔参数的函数,并且在参数为 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 
					} 
				} 
         	} 
        } 
    })

错误处理 ​

你可以通过返回一个 status 来返回错误的 HTTP 状态。

ts
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 来标注正确的 HTTP 状态码,而不是 throw new Error()。

如果你抛出错误,Elysia 默认会将其转换为 500 Internal Server Error。

同样建议使用 return status 替代 throw status,以确保 Eden 和 OpenAPI Type Gen 的类型推断正确。

解析 (Resolve) ​

你可以通过返回一个带有 resolve 函数的对象来为上下文添加属性。

ts
import { Elysia } from 'elysia'

new Elysia()
	.macro({
		user: (enabled: true) => ({
			resolve: () => ({
				user: 'Pardofelis'
			})
		})
	})
	.get('/', ({ user }) => user, {
                          // ^?
		user: true
	})

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

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

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

带有解析的宏扩展 ​

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

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

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

模式 Schema ​

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

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

带有模式的宏将自动进行验证和类型推断,确保类型安全,并且可以与现有模式共存。

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

同一宏内带生命周期的模式 ​

类似于 带有解析的宏扩展,

宏模式也支持在同一宏内的生命周期中进行类型推断,但仅限于命名的单一宏,这是由于 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 } }) {
//                             ^?
		}
	})

如果你想在同一宏内使用生命周期类型推断,建议使用命名的单一宏,而非多个叠加宏。

不要将此与使用宏模式推断路由生命周期事件中的类型混淆。后者运行良好,此限制仅针对在同一宏中使用生命周期。

扩展 Extension ​

宏可以扩展其他宏,允许你基于已有宏进行构建。

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



// ---cut-after---
//

这允许你基于已有宏构建并为其添加更多功能。

去重 ​

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

默认情况下,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 细节,它将合并细节,但更优先使用路由的细节而非宏的细节。