Lifecycle

Lifecycle hook은 요청-응답 주기 동안 특정 이벤트에서 실행되는 함수입니다.

다음과 같은 특정 시점에 사용자 정의 로직을 실행할 수 있습니다:

이는 로깅, 인증 등과 같은 작업에 유용할 수 있습니다.

lifecycle hook을 등록하려면 route 메서드의 3번째 인수로 전달할 수 있습니다:

typescript
import { Elysia } from 'elysia'

new Elysia()
	.get('/1', () => 'Hello Elysia!')
	.get('/auth', () => {
		console.log('This is executed after "beforeHandle"')

		return 'Oh you are lucky!'
	}, {
		beforeHandle({ request, status }) {
			console.log('This is executed before handler')

			if(Math.random() <= 0.5)
				return status(418)
		}
	})
	.get('/2', () => 'Hello Elysia!')

beforeHandle이 값을 반환하면 handler를 건너뛰고 대신 해당 값을 반환합니다.

이는 사용자가 인증되지 않은 경우 401 Unauthorized 응답을 반환하려는 인증과 같은 작업에 유용합니다.

자세한 설명은 Life Cycle, Before Handle을 참조하세요.

Hook

lifecycle 이벤트를 가로채는 함수입니다. 함수가 lifecycle 이벤트에 "hook"을 걸기 때문입니다

Hook은 2가지 유형으로 분류할 수 있습니다:

  1. Local Hook - 특정 route에서 실행
  2. Interceptor Hook - hook이 등록된 후 모든 route에서 실행

Local Hook

local hook은 특정 route에서 실행됩니다.

local hook을 사용하려면 route handler에 hook을 인라인으로 추가할 수 있습니다:

typescript
// Similar to previous code snippet
import { Elysia } from 'elysia'

new Elysia()
	.get('/1', () => 'Hello Elysia!')
	.get('/auth', () => {
		console.log('Run after "beforeHandle"')

		return 'Oh you are lucky!'
	}, {
		// This is a Local Hook
		beforeHandle({ request, status }) {
			console.log('Run before handler')

			if(Math.random() <= 0.5)
				return status(418)
		}
	})
	.get('/2', () => 'Hello Elysia!')

Interceptor Hook

현재 인스턴스에 대해서만 hook이 호출된 후에 나오는 모든 handler에 hook을 등록합니다.

interceptor hook을 추가하려면 .on 다음에 lifecycle 이벤트를 사용할 수 있습니다:

typescript
import { Elysia } from 'elysia'

new Elysia()
	.get('/1', () => 'Hello Elysia!')
	.onBeforeHandle(({ request, status }) => {
		console.log('This is executed before handler')

		if(Math.random() <= 0.5)
			return status(418)
	})
	// "beforeHandle" is applied
	.get('/auth', () => {
		console.log('This is executed after "beforeHandle"')

		return 'Oh you are lucky!'
	})
	// "beforeHandle" is also applied
	.get('/2', () => 'Hello Elysia!')

Local Hook과 달리, Interceptor Hook은 hook이 등록된 후에 나오는 모든 route에 hook을 추가합니다.

과제

2가지 유형의 hook을 실습해 봅시다.

  1. Authentication

    Let's add a simple authentication middleware to GET "/auth" endpoint using beforeHandle hook. If query `name` is provided, we will let the request pass, otherwise we will return 401 status.

  2. Interceptor Hook

    Now, let's create another endpoint GET "/profile" that has the same logic with authentication. We can refactor our code by using interceptor hook to avoid duplication.

Show answer

beforeHandle을 사용하여 handler에 도달하기 전에 요청을 가로채고, status 메서드로 응답을 반환할 수 있습니다.

typescript
import { Elysia } from 'elysia'

new Elysia()
	.onBeforeHandle(({ query: { name }, status }) => {
		if(!name) return status(401)
	})
	.get('/auth', ({ query: { name = 'anon' } }) => {
		return `Hello ${name}!`
	})
	.get('/profile', ({ query: { name = 'anon' } }) => {
		return `Hello ${name}!`
	})
	.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)