Skip to main content
BunShip uses Elysia, a TypeScript-first web framework built for Bun. Routes live in apps/api/src/routes/ and are registered in the main application entry point.

Elysia Route Anatomy

Every route in BunShip follows the same structure: an HTTP method, a path, a handler function, and an options object for validation and documentation.

Handler Context

Elysia injects context into every handler. The available properties depend on which middleware is applied:

Adding a New Resource

This walkthrough creates a complete CRUD resource: widgets scoped to an organization. By the end, you will have list, create, get, update, and delete endpoints.

Step 1: Create Validation Schemas

Define your request and response schemas in a separate file. BunShip uses TypeBox (Elysia’s built-in validation) for compile-time type safety and automatic OpenAPI documentation.

Step 2: Create the Route File

Step 3: Register the Route

Add your route module to the main application in apps/api/src/index.ts:
Your new endpoints are now live at:
  • GET /api/v1/organizations/:orgId/widgets
  • POST /api/v1/organizations/:orgId/widgets
  • GET /api/v1/organizations/:orgId/widgets/:widgetId
  • PATCH /api/v1/organizations/:orgId/widgets/:widgetId
  • DELETE /api/v1/organizations/:orgId/widgets/:widgetId

Route Groups and Prefixes

Elysia supports nesting routes with prefixes. BunShip uses this pattern for organization-scoped resources.

Top-Level Routes

Routes that do not belong to an organization (like auth or user profile):

Organization-Scoped Routes

Resources that belong to an organization use the :orgId parameter:

Nested Sub-Routes

Mount related routes as sub-routes using .use():

Middleware

BunShip provides three middleware layers that you apply to routes as needed.

Authentication Middleware

Verifies the JWT access token and injects user into the handler context:

Organization Middleware

Loads the organization from the :orgId parameter and verifies the user is a member. Injects organization and membership into context:

Permission Middleware

Checks that the user’s role grants a specific permission:
Middleware order matters. Always apply authMiddleware before organizationMiddleware, and organizationMiddleware before permission checks.

OpenAPI Documentation

Elysia generates OpenAPI (Swagger) documentation automatically from your route definitions. BunShip serves this at /docs by default.

Adding Documentation to Routes

Use the detail property to describe your endpoints:

Tags

Tags group related endpoints in the Swagger UI. Use consistent tag names across your route files:

TypeBox Validation Schemas

Elysia uses TypeBox for request validation and OpenAPI type generation. Here are the patterns used throughout BunShip.

Common Schema Patterns

Adding Examples for API Docs

TypeBox examples show up in the Swagger UI as sample values:

Updating the Eden Client

After adding new routes, regenerate the type-safe API client so your frontend picks up the new endpoints:
Then use the new endpoints with full type inference:

Next Steps

Database Schema

Add the database table your new routes need

Configuration

Add permissions and feature flags for your resource