xiaohongshu
xiaohongshu
elysiajs-expert
Expert guidance for ElysiaJS web framework development. Use when building REST APIs, GraphQL services, or WebSocket applications with Elysia on Bun. Covers routing, lifecycle hooks, TypeBox validation, Eden type-safe clients, authentication with JWT/Bearer, all official plugins (OpenAPI, CORS, JW...
Full skill instructions
This skill provides comprehensive expertise for building high-performance, fully type-safe web applications with Elysia on the Bun runtime. It assumes the bun-expert skill is active for Bun-specific patterns (file I/O, SQLite, testing, builds).
import { Elysia, t } from 'elysia'
const app = new Elysia()
.get('/', () => 'Hello Elysia')
.get('/user/:id', ({ params }) => `User ${params.id}`)
.post('/user', ({ body }) => body, {
body: t.Object({
name: t.String(),
email: t.String({ format: 'email' })
})
})
.listen(3000)
export type App = typeof app // Export for Eden client
new Elysia({
name: 'my-app', // Plugin deduplication identifier
prefix: '/api', // Route prefix
seed: config, // Deduplication checksum seed
websocket: { // WebSocket configuration
idleTimeout: 30,
maxPayloadLength: 16777216
}
})
app
.get('/path', handler) // GET request
.post('/path', handler) // POST request
.put('/path', handler) // PUT request
.delete('/path', handler) // DELETE request
.patch('/path', handler) // PATCH request
.options('/path', handler) // OPTIONS request
.all('/path', handler) // All methods
.route('CUSTOM', '/path', handler) // Custom HTTP verb
.get('/user/:id', ({ params }) => params.id) // Required param
.get('/user/:id?', ({ params }) => params.id ?? 'n/a') // Optional param
.get('/files/*', ({ params }) => params['*']) // Wildcard
.get('/org/:org/repo/:repo', ({ params }) => params) // Multiple params
Every handler receives a context object with:
{
body, // Parsed request body
query, // Query string as object
params, // Path parameters
headers, // Request headers (lowercase keys)
cookie, // Cookie jar with get/set
store, // Global mutable state
set, // Response setters (status, headers)
request, // Raw Request object
path, // Request path
server, // Bun server instance
redirect, // Redirect function
status, // Status response function
// + decorated/derived properties
}
// String
.get('/', () => 'Hello')
// JSON (auto-serialized)
.get('/json', () => ({ hello: 'world' }))
// Status with response
.get('/error', ({ status }) => status(418, "I'm a teapot"))
// Custom headers
.get('/custom', ({ set }) => {
set.headers['x-powered-by'] = 'Elysia'
return 'Hello'
})
// Redirect
.get('/old', ({ redirect }) => redirect('/new'))
// File
import { file } from 'elysia'
.get('/image', () => file('image.png'))
// Streaming (generator)
.get('/stream', function* () {
yield 'Hello '
yield 'World'
})
// Async streaming
.get('/async', async function* () {
for (let i = 0; i < 10; i++) {
yield `Event ${i}\n`
await Bun.sleep(100)
}
})
Request → Parse → Transform → Validation → BeforeHandle → Handler → AfterHandle → MapResponse → AfterResponse
.onRequest(({ request, ip, set, status }) => {
// Rate limiting, CORS preflight, request logging
if (rateLimiter.exceeded(ip)) return status(429)
})
.onParse(({ request, contentType }) => {
if (contentType === 'application/custom')
return request.text()
})
// Or specify parser explicitly
.post('/', handler, { parse: 'json' }) // 'json' | 'text' | 'formdata' | 'urlencoded' | 'none'
.get('/id/:id', handler, {
transform({ params }) {
params.id = +params.id // Convert to number before validation
}
})
.derive(({ headers }) => ({
bearer: headers.authorization?.startsWith('Bearer ')
? headers.authorization.slice(7)
: null
}))
.get('/protected', ({ bearer }) => bearer)
.onBeforeHandle(({ cookie, status }) => {
if (!validateSession(cookie.session.value))
return status(401, 'Unauthorized')
})
// Local hook
.get('/protected', handler, {
beforeHandle({ headers, status }) {
if (!headers.authorization) return status(401)
}
})
.guard({
headers: t.Object({ authorization: t.TemplateLiteral('Bearer ${string}') })
})
.resolve(({ headers }) => ({
token: headers.authorization.split(' ')[1],
userId: decodeToken(headers.authorization)
}))
.get('/me', ({ userId }) => userId)
.onAfterHandle(({ responseValue, set }) => {
if (isHtml(responseValue))
set.headers['content-type'] = 'text/html'
})
.mapResponse(({ responseValue, set }) => {
set.headers['content-encoding'] = 'gzip'
return new Response(Bun.gzipSync(JSON.stringify(responseValue)))
})
import { Elysia, NotFoundError } from 'elysia'
.onError(({ code, error, status }) => {
switch(code) {
case 'NOT_FOUND': return status(404, 'Not Found')
case 'VALIDATION': return { errors: error.all }
case 'PARSE': return status(400, 'Invalid body')
case 'INTERNAL_SERVER_ERROR': return status(500)
default: return new Response(error.toString())
}
})
.onAfterResponse(({ set, request }) => {
console.log(`${request.method} ${request.url} - ${set.status}`)
})
// Hooks are LOCAL by default in Elysia 1.0+
.onBeforeHandle({ as: 'local' }, handler) // Current instance only
.onBeforeHandle({ as: 'scoped' }, handler) // Parent + current + descendants
.onBeforeHandle({ as: 'global' }, handler) // All instances
import { Elysia, t } from 'elysia'
.post('/user', handler, {
body: t.Object({
name: t.String({ minLength: 2, maxLength: 100 }),
email: t.String({ format: 'email' }),
age: t.Number({ minimum: 0, maximum: 150 }),
active: t.Boolean(),
tags: t.Array(t.String()),
role: t.Union([t.Literal('admin'), t.Literal('user')]),
metadata: t.Optional(t.Object({ createdAt: t.String() }))
})
})
.post('/example', handler, {
body: t.Object({ ... }), // Request body
query: t.Object({ ... }), // Query string
params: t.Object({ ... }), // Path params
headers: t.Object({ ... }), // Headers (lowercase keys!)
cookie: t.Cookie({ ... }), // Cookies
response: t.Object({ ... }) // Response validation
})
// Response per status code
.get('/user', handler, {
response: {
200: t.Object({ user: UserSchema }),
400: t.Object({ error: t.String() }),
404: t.Object({ message: t.String() })
}
})
t.Numeric() // Coerces string to number (query/params)
t.File({ format: 'image/*' }) // Single file upload
t.Files() // Multiple files
t.Cookie({ session: t.String() }, {
secure: true, httpOnly: true, sameSite: 'strict'
})
t.TemplateLiteral('Bearer ${string}') // Template literal validation
t.UnionEnum(['draft', 'published']) // Enum-like union
t.Object({
email: t.String({
format: 'email',
error: 'Please provide a valid email'
}),
age: t.Number({
minimum: 18,
error({ value }) {
return `Age must be 18+ (got ${value})`
}
})
})
import { z } from 'zod'
import * as v from 'valibot'
.get('/user/:id', handler, {
params: z.object({ id: z.coerce.number() }),
query: v.object({ name: v.literal('test') })
})
.state('counter', 0)
.state('users', new Map())
.get('/count', ({ store }) => store.counter++)
.decorate('logger', new Logger())
.decorate('version', '1.0.0')
.decorate({ db: database, cache: redis })
.get('/', ({ logger, version }) => {
logger.log('Request')
return version
})
.group('/api/v1', app => app
.get('/users', handler)
.post('/users', handler)
)
// With guard configuration
.group('/admin', {
headers: t.Object({ 'x-admin-key': t.String() })
}, app => app
.get('/stats', handler)
)
.guard({
headers: t.Object({ authorization: t.String() }),
beforeHandle: checkAuth
}, app => app
.get('/protected1', handler1)
.get('/protected2', handler2)
)
// As Elysia instance (recommended)
const userPlugin = new Elysia({ name: 'user' })
.state('users', [])
.decorate('userService', new UserService())
.get('/users', ({ store }) => store.users)
// As function (access parent config)
const configPlugin = (config: Config) =>
new Elysia({ name: 'config', seed: config })
.decorate('config', config)
// Usage
new Elysia()
.use(userPlugin)
.use(configPlugin({ apiKey: '...' }))
const authPlugin = new Elysia()
.onBeforeHandle({ as: 'scoped' }, checkAuth) // Applies to parent too
.derive({ as: 'global' }, getUser) // Applies everywhere
.as('scoped') // Lift entire plugin
.use(import('./heavy-plugin'))
await app.modules // Wait for all async plugins
.ws('/ws', {
message(ws, message) {
ws.send('Received: ' + message)
}
})
.ws('/chat', {
// Validation
body: t.Object({ message: t.String() }),
query: t.Object({ room: t.String() }),
open(ws) {
const { room } = ws.data.query
ws.subscribe(room)
ws.publish(room, 'User joined')
},
message(ws, { message }) {
ws.publish(ws.data.query.room, message)
},
close(ws) {
ws.publish(ws.data.query.room, 'User left')
},
// Authentication
beforeHandle({ headers, status }) {
if (!headers.authorization) return status(401)
}
})
ws.send(data) // Send to connection
ws.publish(topic, data) // Publish to topic
ws.subscribe(topic) // Subscribe to topic
ws.unsubscribe(topic) // Unsubscribe
ws.close() // Close connection
ws.data // Access context (query, params)
ws.id // Unique connection ID
const authPlugin = new Elysia({ name: 'auth' })
.macro({
isSignIn: {
async resolve({ cookie, status }) {
if (!cookie.session.value) return status(401)
return { user: await getUser(cookie.session.value) }
}
}
})
// Usage
.use(authPlugin)
.get('/profile', ({ user }) => user, { isSignIn: true })
import { openapi } from '@elysiajs/openapi'
.use(openapi({
provider: 'scalar', // 'scalar' | 'swagger-ui' | null
path: '/docs',
documentation: {
info: { title: 'My API', version: '1.0.0' },
tags: [{ name: 'User', description: 'User endpoints' }],
components: {
securitySchemes: {
bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }
}
}
},
exclude: { methods: ['OPTIONS'], paths: ['/health'] }
}))
.get('/user', handler, {
detail: {
tags: ['User'],
summary: 'Get user',
security: [{ bearerAuth: [] }]
}
})
import { jwt } from '@elysiajs/jwt'
.use(jwt({
name: 'jwt',
secret: process.env.JWT_SECRET!,
exp: '7d'
}))
.post('/login', async ({ jwt, body, cookie: { auth } }) => {
const token = await jwt.sign({ userId: body.id })
auth.set({ value: token, httpOnly: true, maxAge: 7 * 86400 })
return { token }
})
.get('/profile', async ({ jwt, bearer, status }) => {
const profile = await jwt.verify(bearer)
if (!profile) return status(401)
return profile
})
import { bearer } from '@elysiajs/bearer'
.use(bearer())
.get('/protected', ({ bearer, status }) => {
if (!bearer) return status(401)
return `Token: ${bearer}`
})
import { cors } from '@elysiajs/cors'
.use(cors({
origin: ['https://app.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 600
}))
import { staticPlugin } from '@elysiajs/static'
.use(staticPlugin({
assets: 'public',
prefix: '/static',
indexHTML: true
}))
import { html } from '@elysiajs/html'
.use(html())
.get('/', () => `
<html>
<body><h1>Hello</h1></body>
</html>
`)
import { cron } from '@elysiajs/cron'
.use(cron({
name: 'heartbeat',
pattern: '*/10 * * * * *', // Every 10 seconds
run() { console.log('tick') }
}))
import { yoga } from '@elysiajs/graphql-yoga'
.use(yoga({
typeDefs: `type Query { hello: String }`,
resolvers: { Query: { hello: () => 'Hello' } },
path: '/graphql'
}))
import { trpc, compile as c } from '@elysiajs/trpc'
import { initTRPC } from '@trpc/server'
const tr = initTRPC.create()
const router = tr.router({
greet: tr.procedure
.input(c(t.String()))
.query(({ input }) => `Hello ${input}`)
})
.use(trpc(router, { endpoint: '/trpc' }))
import { serverTiming } from '@elysiajs/server-timing'
.use(serverTiming({
enabled: process.env.NODE_ENV !== 'production'
}))
// server.ts
const app = new Elysia()
.get('/user/:id', ({ params }) => ({ id: params.id }))
.post('/user', ({ body }) => body, {
body: t.Object({ name: t.String() })
})
.listen(3000)
export type App = typeof app
// client.ts
import { treaty } from '@elysiajs/eden'
import type { App } from './server'
const api = treaty<App>('localhost:3000')
api.index.get() // /
api.user({ id: '123' }).get() // /user/123
api.deep.nested.path.get() // /deep/nested/path
// POST with body
const { data, error } = await api.user.post({ name: 'John' })
// With headers/query
await api.user.post({ name: 'John' }, {
headers: { authorization: 'Bearer token' },
query: { source: 'web' }
})
// GET with query
await api.users.get({ query: { page: 1, limit: 10 } })
const { data, error, status } = await api.user.post({ name })
if (error) {
switch(error.status) {
case 400: throw new ValidationError(error.value)
case 401: throw new AuthError(error.value)
default: throw error.value
}
}
return data // Type-safe, non-null after error check
const chat = api.chat.subscribe()
chat.on('open', () => chat.send('hello'))
chat.subscribe(message => console.log(message))
chat.raw // Native WebSocket access
const { data } = await api.stream.get()
for await (const chunk of data) {
console.log(chunk)
}
const api = treaty<App>('localhost:3000', {
fetch: { credentials: 'include' },
headers: { authorization: 'Bearer token' },
headers: (path) => ({ /* dynamic headers */ }),
onRequest: (path, options) => { /* modify request */ },
onResponse: (response) => { /* modify response */ }
})
import { treaty } from '@elysiajs/eden'
import { app } from './server'
// Pass instance directly - no network calls
const api = treaty(app)
const { data } = await api.user.post({ name: 'Test' })
expect(data.name).toBe('Test')
import { describe, expect, it } from 'bun:test'
import { Elysia } from 'elysia'
describe('API', () => {
const app = new Elysia()
.get('/hello', () => 'Hello')
.post('/user', ({ body }) => body)
it('returns hello', async () => {
const res = await app.handle(new Request('http://localhost/hello'))
expect(await res.text()).toBe('Hello')
})
it('creates user', async () => {
const res = await app.handle(new Request('http://localhost/user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Test' })
}))
expect(await res.json()).toEqual({ name: 'Test' })
})
})
import { treaty } from '@elysiajs/eden'
import { app } from './server'
const api = treaty(app)
it('should create user with type safety', async () => {
const { data, error } = await api.users.post({
name: 'John',
email: '[email protected]'
})
expect(error).toBeNull()
expect(data?.name).toBe('John')
})
src/
├── modules/
│ ├── auth/
│ │ ├── index.ts # Routes
│ │ ├── service.ts # Business logic
│ │ └── model.ts # TypeBox schemas
│ ├── user/
│ └── product/
├── shared/
│ ├── middleware/
│ └── utils/
├── config/
│ └── env.ts
├── index.ts
└── server.ts
// src/modules/user/index.ts
import { Elysia } from 'elysia'
import { UserService } from './service'
import { CreateUserSchema, UserSchema } from './model'
export const userRoutes = new Elysia({ prefix: '/users' })
.post('/', ({ body }) => UserService.create(body), {
body: CreateUserSchema,
response: UserSchema
})
.get('/:id', ({ params }) => UserService.findById(params.id))
# Compile to binary
bun build --compile --minify-whitespace --minify-syntax \
--target bun-linux-x64 --outfile server src/index.ts
import cluster from 'node:cluster'
import os from 'node:os'
if (cluster.isPrimary) {
for (let i = 0; i < os.availableParallelism(); i++) {
cluster.fork()
}
} else {
await import('./server')
}
as: 'scoped' or as: 'global'See <reference/core-api.md> for complete API documentation. See <reference/lifecycle-hooks.md> for hook execution details. See <reference/plugins.md> for all plugin configurations. See <patterns/authentication.md> for auth implementations. See <patterns/testing.md> for testing strategies.
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience