HaloStack Docs
Open App
Packages

Elysia

The shared Elysia app — routes, auth mount, OpenAPI, telemetry — plus the type-safe Eden Treaty client the web app consumes.

@halostack/elysia is the heart of the API. It holds the composed Elysia app — the routes, the mounted auth handler, OpenAPI, and telemetry — together with the type-safe Eden Treaty client the web app uses to call it. The deployable apps/api is just a thin runtime shell around this package, which is where all the actual API surface lives.

Importing

The package exposes the server instance and the browser-safe client separately:

The split matters for bundle safety: client.ts imports App as a type-only import, so it carries no server runtime and is safe to ship in the browser bundle while still being fully type-synced to the server.

Module structure

Routes are organized per feature under src/modules/<feature>/, and each module is composed onto the app in src/app.ts with .use(...). Every feature follows the same internal split, which keeps validation, routing, and logic cleanly separated:

  • *-model.ts — drizzle-typebox request/response schemas, which double as both validation and OpenAPI definitions.
  • *-routes.ts — the Elysia route group.
  • *-service.ts / *-queries.ts — the business logic and database access.

The current modules are auth, health, notifications, storage, and todos, plus a shared module for cross-cutting concerns like pagination and filters. Beyond the feature modules, app.ts also mounts better-auth (via auth.handler), CORS (credentialed and pinned to WEB_URL), and OpenAPI.

Conventions

Adding an endpoint means adding or extending a module here — and because the Eden client is derived from the app's types, it updates for free with no separate client code to maintain.

Validate inputs and outputs with drizzle-typebox models. When a TypeBox override is unavoidable, apply a single override at the model boundary rather than scattering casts — and never reach for as any. The package is strict TypeScript throughout, and tests are co-located as *.test.ts beside the routes, run with bun test src/.

For exploring the API, the @elysia/openapi plugin serves a live, interactive reference (Scalar) at /openapi, with the raw JSON at /openapi/json. It's reflected straight from the routes and models, so there's no build step and no committed spec to keep in sync.