alepha@docs:~/docs/guides/server$
cat 4-openapi.md
3 min read
Last commit:

#OpenAPI & Swagger

You wrote your schemas. You defined your actions. Now comes the boring part: documenting your API so other developers can use it.

Just kidding. Alepha already did it for you.

#Zero-Config Documentation

Every $action you create is automatically documented. No YAML files. No @ApiProperty() decorators. No separate documentation step.

Just navigate to /docs and you'll see a full Swagger UI:

bash
http://localhost:3000/docs

That's it. Your entire API — every endpoint, every parameter, every request body, every response — rendered in an interactive explorer. It updates in real-time as you code.

#What Gets Documented

Everything you put in your schema shows up in the docs:

src/controllers/UserController.ts
 1class UserController { 2  group = "users"; 3  4  list = $action({ 5    group: this.group, 6    path: "/users", 7    description: "List all users with optional filtering and pagination.", 8    schema: { 9      query: t.object({10        page: t.optional(t.integer({ minimum: 1, default: 1 })),11        limit: t.optional(t.integer({ minimum: 1, maximum: 100, default: 20 })),12        role: t.optional(t.enum(["admin", "user", "guest"])),13      }),14      response: t.object({15        users: t.array(t.object({16          id: t.uuid(),17          name: t.text(),18          email: t.email(),19          role: t.enum(["admin", "user", "guest"]),20        })),21        total: t.integer(),22        page: t.integer(),23      }),24    },25    handler: async ({ query }) => {26      // ...27    },28  });29}

In Swagger UI, this becomes:

  • Tag: users (from group)
  • Summary: list (from property name)
  • Description: "List all users with optional filtering and pagination."
  • Query Parameters: page, limit, role — with types, defaults, and constraints
  • Response Schema: Full object structure with all fields typed

#Grouping Endpoints

The group property organizes your endpoints into sections:

src/controllers/OrderController.ts
1class OrderController {2  group = "orders";3 4  list = $action({ group: this.group, /* ... */ });5  create = $action({ group: this.group, /* ... */ });6  get = $action({ group: this.group, /* ... */ });7  cancel = $action({ group: this.group, /* ... */ });8}
src/controllers/PaymentController.ts
1class PaymentController {2  group = "payments";3 4  charge = $action({ group: this.group, /* ... */ });5  refund = $action({ group: this.group, /* ... */ });6}

In Swagger UI, you get collapsible sections: orders (4 endpoints) and payments (2 endpoints). Clean and navigable.

If you don't set a group, it defaults to the class name.

#Adding Descriptions

The description field adds context to your endpoints:

typescript
1createUser = $action({2  description: "Creates a new user account. Sends a welcome email upon success.",3  schema: { /* ... */ },4  handler: async ({ body }) => { /* ... */ },5});

Short and useful. Tell developers what the endpoint does, not how it does it.

#Schema Descriptions

You can add descriptions to individual fields too:

typescript
 1const createUserSchema = t.object({ 2  email: t.email({ description: "Must be unique across all users" }), 3  password: t.text({ 4    minLength: 8, 5    description: "Minimum 8 characters. We recommend using a password manager." 6  }), 7  role: t.optional(t.enum(["user", "admin"], { 8    description: "Defaults to 'user' if not specified" 9  })),10});

These descriptions appear in the Swagger UI schema viewer. Helpful for complex fields.

#Try It Out

Swagger UI isn't just documentation — it's an API playground.

  1. Click on an endpoint
  2. Fill in the parameters
  3. Hit "Try it out"
  4. See the real response from your server

No Postman. No curl. Just click and test. Perfect for quick debugging or demoing to stakeholders.

#Authentication in Swagger

If your API uses authentication (via alepha/server/security), Swagger UI shows a lock icon. Click "Authorize" to add your token, and all subsequent requests include it.

typescript
1import { $issuer } from "alepha/security";2 3const apiIssuer = $issuer({4  name: "api",5  // ...6});

Protected endpoints show which issuer they require. Swagger handles the rest.

#OpenAPI JSON

Need the raw OpenAPI spec? It's available at:

bash
http://localhost:3000/docs/openapi.json

Use it to:

  • Generate client SDKs (OpenAPI Generator)
  • Import into Postman or Insomnia
  • Feed into API gateways
  • Run contract tests

The spec follows OpenAPI 3.0 and includes everything: paths, schemas, security definitions, and more.

#Customizing the Docs

#Custom Title and Version

Configure via environment variables or the Swagger provider:

src/main.server.ts
 1import { Alepha, run } from "alepha"; 2import { SwaggerProvider } from "alepha/server/swagger"; 3  4const app = Alepha.create().with({ 5  provide: SwaggerProvider, 6  config: { 7    title: "My Awesome API", 8    version: "2.1.0", 9    description: "The API that powers everything.",10  },11});12 13run(app);

#Hiding Internal Endpoints

Some endpoints shouldn't appear in public docs. Use disabled or create separate API surfaces:

typescript
1internalHealthCheck = $action({2  group: "internal",3  path: "/internal/health",4  // This still works, just won't be in public docs if you filter by group5  handler: async () => ({ status: "ok" }),6});

#Why This Matters

API documentation is usually an afterthought. It gets outdated. It lies. Developers stop trusting it.

With Alepha, documentation is generated from the same schemas that validate your requests and serialize your responses. It cannot lie. If the docs say a field is required, it's required. If they say the response has three fields, it has three fields.

Your schemas are your documentation. Keep them accurate, and the docs stay accurate automatically.

#Quick Reference

URL Purpose
/docs Interactive Swagger UI
/docs/openapi.json Raw OpenAPI 3.0 specification
Option Where Purpose
group $action Groups endpoints into tags
description $action Endpoint description
description Schema fields Field-level documentation
name $action Override the operation name
On This Page
No headings found...
ready
mainTypeScript
UTF-8guides_server_openapi.md