ELPOD / DOCS FOUNDATIONS

foundations / reference

Feature Pods and Modular Elysia Architecture

A pod is an Elpod feature boundary grouping a native Elysia controller, URL prefix, providers, and explicit imports and exports.

The idea

In Elpod, a pod is an in-process feature composition boundary for a Bun and Elysia application. It groups one controller, a route prefix, local providers, and optional imports and exports. Use pods when the growing application needs named ownership of routes and services. The project structure guide shows how to lay out files, while provider boundaries explain cross-feature dependencies.

Imagine a busy food market. “Users”, “billing”, and “reports” are different stalls. Each stall has its own menu, tools, and counter, but the market still has one entrance and one set of safety rules. A feature is one stall. A pod is its signboard and setup sheet.

A feature is a business boundary: users, billing, reports, or another cohesive capability. A pod is the pod({...}) value that describes that feature to Elpod. It supplies a lowercase name, URL prefix, controller, local providers, and optional dependency boundaries.

How Elpod provides it

import { pod } from "@elpod/core";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";

export const users = pod({
  name: "users",
  prefix: "/users",
  controller: UsersController,
  providers: [UsersService],
});

During bootstrap Elpod creates a feature scope, resolves the controller, creates an Elysia group at prefix, and calls controller.routes(group). The controller must return the same native Elysia instance.

Prefixes and routes: the stall’s address

Names are single lowercase words such as users or billing. Prefixes start with / and cannot contain a query, fragment, control character, or //. Application prefixes must be unique. A / prefix is allowed for a feature that intentionally owns root routes.

routes(app: ElpodElysia) {
  return app
    .get("/", () => ({ ok: true }))
    .get("/:id", ({ params }) => this.find(params.id));
}

One controller per feature: one front counter

One controller is the pod entrypoint and keeps route ownership obvious. It can register many native Elysia routes. If a domain grows, split internal services or create a second feature with its own prefix rather than hiding multiple unrelated controllers behind magic discovery. There is no mysterious “find every controller” spell to debug later.

Common mistakes

  • Using a URL prefix to imply authorization. A prefix is routing, not a security boundary.
  • Registering the same prefix twice; composition rejects duplicate prefixes.
  • Returning undefined from routes() instead of the Elysia instance.
  • Treating uses as a provider registration; it only declares consumption.

Production notes

Pods give you composition and DI boundaries, not process isolation or tenant isolation. A feature can still make unsafe queries or leak data. Keep authorization near domain actions and use policies. Use explicit feature imports and exports when one feature consumes another feature’s public provider.

Next: Dependency injection and Providers, uses, exports, and imports.