Skip to content
cccskills
FreePublished on GitHub

api-framework-elysia

Bun-native HTTP framework — routing, TypeBox validation, Eden Treaty, plugins, lifecycle hooks

The English summary is being prepared. Showing the original description.

See how to install

Files included (7)

  • SKILL.md10.3 KB
  • examples/core.md9.2 KB
  • examples/eden-treaty.md5.5 KB
  • examples/lifecycle-errors.md9.0 KB
  • examples/websocket-testing.md7.2 KB
  • metadata.yaml492 B
  • reference.md8.0 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

API Development with Elysia

Quick Guide: Elysia is a Bun-native HTTP framework with end-to-end type safety. Use method chaining (not separate statements) so TypeScript infers the full route tree. Import t from elysia for TypeBox validation. Export the app type (export type App = typeof app) for Eden Treaty clients. Use status() (not the deprecated error() function) for error responses with type narrowing.


<critical_requirements>

CRITICAL: Before Using This Skill

(You MUST use method chaining on the Elysia instance -- separate .get() calls break type inference for Eden Treaty)

(You MUST use status() for error responses -- error() is deprecated since 1.3, prefer status())

(You MUST export the app type (export type App = typeof app) for Eden Treaty client generation)

</critical_requirements>


Auto-detection: Elysia, elysia, ElysiaJS, Eden Treaty, @elysiajs/eden, @elysiajs/openapi, t.Object, t.String, t.Number, t.File, TypeBox, .derive(), .decorate(), .guard(), .macro(), .ws(), onBeforeHandle, onAfterHandle, onRequest, treaty, bun:test

When to use:

  • Building APIs on Bun runtime with end-to-end type safety
  • Need RPC-style client with zero code generation (Eden Treaty)
  • TypeBox validation with AOT compilation (~18x faster than Zod on Bun)
  • Plugin-based architecture with automatic type propagation
  • WebSocket support with schema validation

When NOT to use:

  • Deploying to Node.js-only environments without Bun (use a Node-first framework)
  • Need OpenAPI-first design with createRoute patterns (other frameworks with Zod-OpenAPI integration are more mature for this)
  • Team already committed to Express/Fastify ecosystem

Key patterns covered:

  • Route definitions with method chaining and TypeBox validation
  • Plugin architecture with .use(), .derive(), .decorate(), .macro()
  • Scoping rules (local, scoped, global) and .guard()
  • End-to-end type safety with Eden Treaty
  • Lifecycle hooks (onRequest, onBeforeHandle, onAfterHandle, onError)
  • Error handling with custom error classes and status()
  • WebSocket with schema validation
  • Testing with bun:test and .handle() or Eden Treaty

Detailed Resources:


<philosophy>

Philosophy

Method chaining IS the type system. Elysia infers the entire route tree through chained calls. Breaking the chain (separate app.get() statements) loses type information for Eden Treaty clients. This is the single most important architectural constraint.

TypeBox over Zod for Bun. While Elysia 1.4+ supports Standard Schema (Zod, Valibot, etc.), TypeBox (t from elysia) uses AOT compilation inside Bun for ~18x faster validation. Use TypeBox as default; use Zod only when sharing schemas with a non-Bun codebase.

Plugins are Elysia instances. Every new Elysia() is a plugin. There is no separate plugin API -- you compose by chaining .use(). The name property deduplicates plugins across the tree.

</philosophy>
<patterns>

Core Patterns

Pattern 1: Route Setup with Method Chaining

Chain route definitions on the Elysia instance. Each .get(), .post(), etc. returns the instance with updated type information.

import { Elysia, t } from "elysia";

const app = new Elysia()
  .get("/", () => "hello")
  .get("/user/:id", ({ params: { id } }) => id, {
    params: t.Object({
      id: t.Number(),
    }),
  })
  .post("/user", ({ body }) => body, {
    body: t.Object({
      name: t.String(),
      email: t.String({ format: "email" }),
    }),
  })
  .listen(3000);

export type App = typeof app;

Why good: method chaining preserves type inference across the entire route tree, export type App enables Eden Treaty, TypeBox validates at runtime with AOT compilation

See examples/core.md for complete route setup with modular plugins.


Pattern 2: Plugin Architecture

Every Elysia instance is a plugin. Use .use() to compose, name to deduplicate.

import { Elysia } from "elysia";

const userPlugin = new Elysia({ name: "user", prefix: "/user" })
  .get("/", () => "list users")
  .get("/:id", ({ params: { id } }) => `user ${id}`);

const app = new Elysia().use(userPlugin).listen(3000);

Why good: name prevents duplicate registration when a plugin is .use()-d multiple times, prefix scopes routes cleanly

See examples/core.md for .derive(), .decorate(), and .macro() patterns.


Pattern 3: Scoping with Guard

Apply validation schemas and lifecycle hooks to groups of routes.

import { Elysia, t } from "elysia";

const app = new Elysia()
  .guard(
    {
      headers: t.Object({
        authorization: t.String(),
      }),
    },
    (app) =>
      app
        .get("/protected", ({ headers }) => headers.authorization)
        .post("/admin", ({ body }) => body, {
          body: t.Object({ action: t.String() }),
        }),
  )
  .get("/public", () => "no auth needed");

Why good: guard encapsulates validation for route groups without repeating schema definitions, public routes outside the guard are unaffected


Pattern 4: Error Handling with status()

Use status() for typed error responses. Register custom error classes with .error().

import { Elysia } from "elysia";

const HTTP_UNAUTHORIZED = 401;
const HTTP_NOT_FOUND = 404;

class NotFoundError extends Error {
  status = HTTP_NOT_FOUND;
  constructor(public resource: string) {
    super(`${resource} not found`);
  }
}

const app = new Elysia()
  .error({ NotFoundError })
  .onError(({ code, error, status }) => {
    if (code === "NotFoundError") {
      return status(HTTP_NOT_FOUND, { error: error.message });
    }
    if (code === "VALIDATION") {
      return status(HTTP_UNAUTHORIZED, { error: "Validation failed" });
    }
  })
  .get("/user/:id", ({ params: { id }, status }) => {
    const user = findUser(id);
    if (!user) throw new NotFoundError("User");
    return user;
  });

Why good: status() provides type narrowing for error responses, custom error classes with .error() enable code-based type narrowing in onError, named constants avoid magic status numbers

See examples/lifecycle-errors.md for all lifecycle hooks and error patterns.


Pattern 5: Eden Treaty Client

Export the app type and use treaty() for a fully type-safe client with no code generation.

// server.ts
import { Elysia, t } from "elysia";

const app = new Elysia()
  .get("/user/:id", ({ params: { id } }) => ({ id, name: "Alice" }), {
    params: t.Object({ id: t.Number() }),
  })
  .post("/user", ({ body }) => body, {
    body: t.Object({ name: t.String() }),
  });

export type App = typeof app;
// client.ts
import { treaty } from "@elysiajs/eden";
import type { App } from "./server";

const api = treaty<App>("localhost:3000");

const { data, error } = await api.user({ id: 1 }).get();
// data is typed as { id: number; name: string } | null
// error is typed based on error responses

Why good: zero code generation, full autocomplete on paths and methods, error/data destructuring with type narrowing

See examples/eden-treaty.md for response handling, file uploads, and WebSocket via Treaty.

</patterns>

<red_flags>

RED FLAGS

High Priority:

  • Separate app.get() / app.post() calls instead of chaining -- breaks Eden Treaty type inference entirely
  • Using deprecated error() function instead of status() -- deprecated since Elysia 1.3
  • Not exporting type App = typeof app -- Eden Treaty client has no type information
  • Using as('plugin') -- removed in 1.3+, use as('scoped') instead

Medium Priority:

  • Importing patterns from other HTTP frameworks -- Elysia has its own routing API and conventions
  • Using t.Object() without named constants for limits/lengths -- magic numbers in validation schemas
  • Not providing name on plugin instances -- causes duplicate registration in complex app trees
  • .derive() or lifecycle hooks without { as: 'scoped' } or { as: 'global' } when parent routes need them -- hooks are local-scoped by default

Gotchas & Edge Cases:

  • params are strings by default -- use t.Number() in the schema to coerce path params to numbers
  • .handle() in tests requires a fully qualified URL (http://localhost/path), NOT a path fragment (/path)
  • .guard() group standalone mode is default in 1.4+ -- guard schemas merge with route schemas instead of overwriting
  • TypeBox t.File() auto-detects multipart/form-data content type -- no need to set headers manually
  • t.Files() (plural) for multiple file uploads, t.File() for single
  • Eden Treaty dynamic path params use function syntax: api.user({ id: 1 }).get() not api.user[1].get()
  • When Eden Treaty response has status >= 300, data is always null and error has the value
  • Lifecycle hooks only apply to routes registered AFTER the hook -- order of .on*() and route definitions matters
  • onError receives a code string, not a status number -- switch on code for type narrowing
  • Cookies parse as JSON automatically if the value looks like JSON (1.3+ behavior)
  • await app.modules is required in tests when using lazy-loaded plugins (import('./plugin'))

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

(You MUST use method chaining on the Elysia instance -- separate .get() calls break type inference for Eden Treaty)

(You MUST use status() for error responses -- error() is deprecated since 1.3, prefer status())

(You MUST export the app type (export type App = typeof app) for Eden Treaty client generation)

Failure to follow these rules will break end-to-end type safety and Eden Treaty client generation.

</critical_reminders>

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

See all skills by agents-inc

Report a problem with this skill