Macro

재사용 가능한 라우트 옵션입니다.

다음과 같은 인증 검사가 있다고 상상해 봅시다:

typescript
import { Elysia, t } from 'elysia'

new Elysia()
	.post('/user', ({ body }) => body, {
		cookie: t.Object({
			session: t.String()
		}),
		beforeHandle({ cookie: { session } }) {
			if(!session.value) throw 'Unauthorized'
		}
	})
	.listen(3000)

인증이 필요한 여러 라우트가 있는 경우 동일한 옵션을 반복해서 작성해야 합니다.

대신 매크로를 사용하여 라우트 옵션을 재사용할 수 있습니다:

typescript
import { Elysia, t } from 'elysia'

new Elysia()
	.macro('auth', {
		cookie: t.Object({
			session: t.String()
		}),
		// psuedo auth check
		beforeHandle({ cookie: { session }, status }) {
			if(!session.value) return status(401)
		}
	})
	.post('/user', ({ body }) => body, {
		auth: true
	})
	.listen(3000)

authcookiebeforeHandle을 모두 라우트에 인라인합니다.

간단히 말해서, Macro재사용 가능한 라우트 옵션입니다. 함수와 유사하지만 타입 안전성을 가진 라우트 옵션입니다.

Assignment

body가 피보나치 수인지 확인하는 매크로를 정의해 봅시다:

typescript
function isFibonacci(n: number) {
	let a = 0, b = 1
	while(b < n) [a, b] = [b, a + b]
	return b === n || n === 0
}
  1. Enforce Body Schema

    Let's enforce a schema as body for POST `/` endpoint that accept a number.

  2. Check if body is in Fibonacci Sequence

    We can use `isFibonacci` function to check if the number is in Fibonacci sequence. If it is not, return 418 status

Show answer
  1. 타입을 강제하려면 매크로에 body 속성을 정의할 수 있습니다.
  2. 요청을 단락시키려면 status 함수를 사용하여 조기에 반환할 수 있습니다.
typescript
import { Elysia, t } from 'elysia'

function isPerfectSquare(x: number) {
    const s = Math.floor(Math.sqrt(x))
    return s * s === x
}

function isFibonacci(n: number) {
    if (n < 0) return false

    return isPerfectSquare(5 * n * n + 4) || isPerfectSquare(5 * n * n - 4)
}

new Elysia()
    .macro('isFibonacci', {
		body: t.Number(),
        beforeHandle({ body, status }) {
            if(!isFibonacci(body)) return status(418)
        }
    })
	.post('/fibo', ({ body }) => body, {
		isFibonacci: true
	})
    .listen(3000)
  • index.ts

Error

Error: No Elysia server is running in index.ts
Did you forget to call `.listen()`?
    at parse (file:///vercel/path0/docs/.vitepress/.temp/utils.J44kKQwQ.js:202:66)
    at file:///vercel/path0/docs/.vitepress/.temp/utils.J44kKQwQ.js:240:30
    at new Promise (<anonymous>)
    at execute (file:///vercel/path0/docs/.vitepress/.temp/utils.J44kKQwQ.js:238:49)
    at Proxy.run (file:///vercel/path0/docs/.vitepress/.temp/store.CFqL75-f.js:190:51)
    at Proxy.wrappedAction (file:///vercel/path0/node_modules/pinia/dist/pinia.mjs:1394:26)
    at setup (file:///vercel/path0/docs/.vitepress/.temp/playground.eEZCBItr.js:133:50)
    at playground_vue_vue_type_script_setup_true_lang_default.setup (file:///vercel/path0/docs/.vitepress/.temp/playground.eEZCBItr.js:438:22)
    at callWithErrorHandling (/vercel/path0/node_modules/@vue/runtime-core/dist/runtime-core.cjs.prod.js:86:19)
    at setupStatefulComponent (/vercel/path0/node_modules/@vue/runtime-core/dist/runtime-core.cjs.prod.js:6365:25)