--- name: elysiajs description: Create backend with ElysiaJS, a type-safe, high-performance framework. --- # ElysiaJS Development Skill Always consult [elysiajs.com/llms.txt](https://elysiajs.com/llms.txt) for code examples and latest API. ## Overview ElysiaJS is a TypeScript framework for building Bun-first (but not limited to Bun) type-safe, high-performance backend servers. This skill provides comprehensive guidance for developing with Elysia, including routing, validation, authentication, plugins, integrations, and deployment. ## When to Use This Skill Trigger this skill when the user asks to: - Create or modify ElysiaJS routes, handlers, or servers - Setup validation with TypeBox or other schema libraries (Zod, Valibot) - Implement authentication (JWT, session-based, macros, guards) - Add plugins (CORS, OpenAPI, Static files, JWT) - Integrate with external services (Drizzle ORM, Better Auth, Next.js, Eden Treaty) - Setup WebSocket endpoints for real-time features - Create unit tests for Elysia instances - Deploy Elysia servers to production ## Quick Start Quick scaffold: ```bash bun create elysia app ``` ### Basic Server ```typescript import { Elysia, t, status } from 'elysia' const app = new Elysia() .get('/', () => 'Hello World') .post('/user', ({ body }) => body, { body: t.Object({ name: t.String(), age: t.Number() }) }) .get('/id/:id', ({ params: { id } }) => { if(id > 1_000_000) return status(404, 'Not Found') return id }, { params: t.Object({ id: t.Number({ minimum: 1 }) }), response: { 200: t.Number(), 404: t.Literal('Not Found') } }) .listen(3000) ``` ## Basic Usage ### HTTP Methods ```typescript import { Elysia } from 'elysia' new Elysia() .get('/', 'GET') .post('/', 'POST') .put('/', 'PUT') .patch('/', 'PATCH') .delete('/', 'DELETE') .options('/', 'OPTIONS') .head('/', 'HEAD') ``` ### Path Parameters ```typescript .get('/user/:id', ({ params: { id } }) => id) .get('/post/:id/:slug', ({ params }) => params) ``` ### Query Parameters ```typescript .get('/search', ({ query }) => query.q) // GET /search?q=elysia → "elysia" ``` ### Request Body ```typescript .post('/user', ({ body }) => body) ``` ### Headers ```typescript .get('/', ({ headers }) => headers.authorization) ``` ## TypeBox Validation ### Basic Types ```typescript import { Elysia, t } from 'elysia' .post('/user', ({ body }) => body, { body: t.Object({ name: t.String(), age: t.Number(), email: t.String({ format: 'email' }), website: t.Optional(t.String({ format: 'uri' })) }) }) ``` ### Nested Objects ```typescript body: t.Object({ user: t.Object({ name: t.String(), address: t.Object({ street: t.String(), city: t.String() }) }) }) ``` ### Arrays ```typescript body: t.Object({ tags: t.Array(t.String()), users: t.Array(t.Object({ id: t.String(), name: t.String() })) }) ``` ### File Upload ```typescript .post('/upload', ({ body }) => body.file, { body: t.Object({ file: t.File({ type: 'image', // image/* mime types maxSize: '5m' // 5 megabytes }), files: t.Files({ // Multiple files type: ['image/png', 'image/jpeg'] }) }) }) ``` ### Response Validation ```typescript .get('/user/:id', ({ params: { id } }) => ({ id, name: 'John', email: 'john@example.com' }), { params: t.Object({ id: t.Number() }), response: { 200: t.Object({ id: t.Number(), name: t.String(), email: t.String() }), 404: t.String() } }) ``` ## Standard Schema (Zod, Valibot, ArkType) ### Zod ```typescript import { z } from 'zod' .post('/user', ({ body }) => body, { body: z.object({ name: z.string(), age: z.number().min(0), email: z.string().email() }) }) ``` ## Error Handling ```typescript .get('/user/:id', ({ params: { id }, status }) => { const user = findUser(id) if (!user) { return status(404, 'User not found') } return user }) ``` ## Guards (Apply to Multiple Routes) ```typescript .guard({ params: t.Object({ id: t.Number() }) }, app => app .get('/user/:id', ({ params: { id } }) => id) .delete('/user/:id', ({ params: { id } }) => id) ) ``` ## Macro ```typescript .macro({ hi: (word: string) => ({ beforeHandle() { console.log(word) } }) }) .get('/', () => 'hi', { hi: 'Elysia' }) ``` ### Project Structure (Recommended) Elysia takes an unopinionated approach but based on user request. But without any specific preference, we recommend a feature-based and domain driven folder structure where each feature has its own folder containing controllers, services, and models. ``` src/ ├── index.ts # Main server entry ├── modules/ │ ├── auth/ │ │ ├── index.ts # Auth routes (Elysia instance) │ │ ├── service.ts # Business logic │ │ └── model.ts # TypeBox schemas/DTOs │ └── user/ │ ├── index.ts │ ├── service.ts │ └── model.ts └── plugins/ └── custom.ts public/ # Static files (if using static plugin) test/ # Unit tests ``` Each file has its own responsibility as follows: - **Controller (index.ts)**: Handle HTTP routing, request validation, and cookie. - **Service (service.ts)**: Handle business logic, decoupled from Elysia controller if possible. - **Model (model.ts)**: Define the data structure and validation for the request and response. ## Best Practice Elysia is unopinionated on design pattern, but if not provided, we can relies on MVC pattern pair with feature based folder structure. - Controller: - Prefers Elysia as a controller for HTTP dependant controller - For non HTTP dependent, prefers service instead unless explicitly asked - Use `onError` to handle local custom errors - Register Model to Elysia instance via `Elysia.models({ ...models })` and prefix model by namespace `Elysia.prefix('model', 'Namespace.') - Prefers Reference Model by name provided by Elysia instead of using an actual `Model.name` - Service: - Prefers class (or abstract class if possible) - Prefers interface/type derive from `Model` - Return `status` (`import { status } from 'elysia'`) for error - Prefers `return Error` instead of `throw Error` - Models: - Always export validation model and type of validation model - Custom Error should be in contains in Model ## Elysia Key Concept Elysia has a every important concepts/rules to understand before use. ## Encapsulation - Isolates by Default Lifecycles (hooks, middleware) **don't leak** between instances unless scoped. **Scope levels:** - `local` (default) - current instance + descendants - `scoped` - parent + current + descendants - `global` - all instances ```ts .onBeforeHandle(() => {}) // only local instance .onBeforeHandle({ as: 'global' }, () => {}) // exports to all ``` ## Method Chaining - Required for Types **Must chain**. Each method returns new type reference. ❌ Don't: ```ts const app = new Elysia() app.state('build', 1) // loses type app.get('/', ({ store }) => store.build) // build doesn't exists ``` ✅ Do: ```ts new Elysia() .state('build', 1) .get('/', ({ store }) => store.build) ``` ## Explicit Dependencies Each instance independent. **Declare what you use.** ```ts const auth = new Elysia() .decorate('Auth', Auth) .model(Auth.models) new Elysia() .get('/', ({ Auth }) => Auth.getProfile()) // Auth doesn't exists new Elysia() .use(auth) // must declare .get('/', ({ Auth }) => Auth.getProfile()) ``` **Global scope when:** - No types added (cors, helmet) - Global lifecycle (logging, tracing) **Explicit when:** - Adds types (state, models) - Business logic (auth, db) ## Deduplication Plugins re-execute unless named: ```ts new Elysia() // rerun on `.use` new Elysia({ name: 'ip' }) // runs once across all instances ``` ## Order Matters Events apply to routes **registered after** them. ```ts .onBeforeHandle(() => console.log('1')) .get('/', () => 'hi') // has hook .onBeforeHandle(() => console.log('2')) // doesn't affect '/' ``` ## Type Inference **Inline functions only** for accurate types. For controllers, destructure in inline wrapper: ```ts .post('/', ({ body }) => Controller.greet(body), { body: t.Object({ name: t.String() }) }) ``` Get type from schema: ```ts type MyType = typeof MyType.static ``` ## Reference Model Model can be reference by name, especially great for documenting an API ```ts new Elysia() .model({ book: t.Object({ name: t.String() }) }) .post('/', ({ body }) => body.name, { body: 'book' }) ``` Model can be renamed by using `.prefix` / `.suffix` ```ts new Elysia() .model({ book: t.Object({ name: t.String() }) }) .prefix('model', 'Namespace') .post('/', ({ body }) => body.name, { body: 'Namespace.Book' }) ``` Once `prefix`, model name will be capitalized by default. ## Technical Terms The following are technical terms that is use for Elysia: - `OpenAPI Type Gen` - function name `fromTypes` from `@elysiajs/openapi` for generating OpenAPI from types, see `plugins/openapi.md` - `Eden`, `Eden Treaty` - e2e type safe RPC client for share type from backend to frontend ## Resources Use the following references as needed. It's recommended to checkout `route.md` for as it contains the most important foundation building blocks with examples. `plugin.md` and `validation.md` is important as well but can be check as needed. ### references/ Detailed documentation split by topic: - `bun-fullstack-dev-server.md` - Bun Fullstack Dev Server with HMR. React without bundler. - `cookie.md` - Detailed documentation on cookie - `deployment.md` - Production deployment guide / Docker - `eden.md` - e2e type safe RPC client for share type from backend to frontend - `guard.md` - Setting validation/lifecycle all at once - `macro.md` - Compose multiple schema/lifecycle as a reusable Elysia via key-value (recommended for complex setup, eg. authentication, authorization, Role-based Access Check) - `plugin.md` - Decouple part of Elysia into a standalone component - `route.md` - Elysia foundation building block: Routing, Handler and Context - `testing.md` - Unit tests with examples - `validation.md` - Setup input/output validation and list of all custom validation rules - `websocket.md` - Real-time features ### plugins/ Detailed documentation, usage and configuration reference for official Elysia plugin: - `bearer.md` - Add bearer capability to Elysia (`@elysiajs/bearer`) - `cors.md` - Out of box configuration for CORS (`@elysiajs/cors`) - `cron.md` - Run cron job with access to Elysia context (`@elysiajs/cron`) - `graphql-apollo.md` - Integration GraphQL Apollo (`@elysiajs/graphql-apollo`) - `graphql-yoga.md` - Integration with GraphQL Yoga (`@elysiajs/graphql-yoga`) - `html.md` - HTML and JSX plugin setup and usage (`@elysiajs/html`) - `jwt.md` - JWT / JWK plugin (`@elysiajs/jwt`) - `openapi.md` - OpenAPI documentation and OpenAPI Type Gen / OpenAPI from types (`@elysiajs/openapi`) - `opentelemetry.md` - OpenTelemetry, instrumentation, and record span utilities (`@elysiajs/opentelemetry`) - `server-timing.md` - Server Timing metric for debug (`@elysiajs/server-timing`) - `static.md` - Serve static files/folders for Elysia Server (`@elysiajs/static`) ### integrations/ Guide to integrate Elysia with external library/runtime: - `ai-sdk.md` - Using Vercel AI SDK with Elysia - `astro.md` - Elysia in Astro API route - `better-auth.md` - Integrate Elysia with better-auth - `cloudflare-worker.md` - Elysia on Cloudflare Worker adapter - `deno.md` - Elysia on Deno - `drizzle.md` - Integrate Elysia with Drizzle ORM - `expo.md` - Elysia in Expo API route - `nextjs.md` - Elysia in Nextjs API route - `nodejs.md` - Run Elysia on Node.js - `nuxt.md` - Elysia on API route - `prisma.md` - Integrate Elysia with Prisma - `react-email.d` - Create and Send Email with React and Elysia - `sveltekit.md` - Run Elysia on Svelte Kit API route - `tanstack-start.md` - Run Elysia on Tanstack Start / React Query - `vercel.md` - Deploy Elysia to Vercel ### examples/ (optional) - `basic.ts` - Basic Elysia example - `body-parser.ts` - Custom body parser example via `.onParse` - `complex.ts` - Comprehensive usage of Elysia server - `cookie.ts` - Setting cookie - `error.ts` - Error handling - `file.ts` - Returning local file from server - `guard.ts` - Setting mulitple validation schema and lifecycle - `map-response.ts` - Custom response mapper - `redirect.ts` - Redirect response - `rename.ts` - Rename context's property - `schema.ts` - Setup validation - `state.ts` - Setup global state - `upload-file.ts` - File upload with validation - `websocket.ts` - Web Socket for realtime communication ### patterns/ (optional) - `patterns/mvc.md` - Detail guideline for using Elysia with MVC patterns