Encapsulation

Elysia hooks are encapsulated to its own instance only.

If you create a new instance, it will not share hook with other instances.

ts
import { Elysia } from 'elysia'

const profile = new Elysia()
	.onBeforeHandle(
		({ query: { name }, status }) => {
			if(!name)
				return status(401)
		}
	)
	.get('/profile', () => 'Hi!')

new Elysia()
	.use(profile)
	.patch('/rename', () => 'Ok! XD')
	.listen(3000)
localhost

GET

Try changing the path in the URL bar to /rename and see the result


Elysia isolate lifecycle unless explicitly stated.

This is similar to export in JavaScript, where you need to export the function to make it available outside the module.

To "export" the lifecycle to other instances, you must add specify the scope.

Scope

There are 3 scopes available:

  1. local (default) - apply to only current instance and descendant only
  2. scoped - apply to parent, current instance and descendants
  3. global - apply to all instance that apply the plugin (all parents, current, and descendants)

In our case, we want to apply the sign-in check to the app which is a direct parent, so we can use either scoped or global.

ts
import { Elysia } from 'elysia'

const profile = new Elysia()
	.onBeforeHandle(
		{ as: 'scoped' }, 
		({ cookie }) => {
			throwIfNotSignIn(cookie)
		}
	)
	.get('/profile', () => 'Hi there!')

const app = new Elysia()
	.use(profile)
	// This has sign in check
	.patch('/rename', ({ body }) => updateProfile(body))
localhost

GET

Casting lifecycle to "scoped" will export lifecycle to parent instance. While "global" will export lifecycle to all instances that has a plugin.

Learn more about this in scope.

Guard

Similar to lifecycle, schema is also encapsulated to its own instance.

We can specify guard scope similar to lifecycle.

typescript
import { Elysia } from 'elysia'

const user = new Elysia()
	.guard({
		as: 'scoped', 
		query: t.Object({
			age: t.Number(),
			name: t.Optional(t.String())
		}),
		beforeHandle({ query: { age }, status }) {
			if(age < 18) return status(403)
		}
	})
	.get('/profile', () => 'Hi!')
	.get('/settings', () => 'Settings')

It's very important to note that every hook will affect all routes after its declaration.

See Scope for more information.

Assignment

Let's define a scope for nameCheck, and ageCheck to make our app works.

  1. Name Check

    Make sure that user provides `name` query is applied to **only instance that use plugin** (self, and parent)

  2. Age Check

    Make sure that user is at least 18 years old is applied **all** endpoint

Show answer

We can modify scope as follows:

  1. modify nameCheck scope to scope
  2. modify ageCheck scope to global
typescript
import { Elysia, t } from 'elysia'

const nameCheck = new Elysia()
	.onBeforeHandle(
		{ as: 'scoped' }, 
		({ query: { name }, status }) => {
			if(!name) return status(401)
		}
	)

const ageCheck = new Elysia()
	.guard({
		as: 'global', 
		query: t.Object({
			age: t.Number(),
			name: t.Optional(t.String())
		}),
		beforeHandle({ query: { age }, status }) {
			if(age < 18) return status(403)
		}
	})

const name = new Elysia()
	.use(nameCheck)
	.patch('/rename', () => 'Ok! XD')

const profile = new Elysia()
	.use(ageCheck)
	.use(name)
	.get('/profile', () => 'Hi!')

new Elysia()
	.use(profile)
	.listen(3000)
  • index.ts