Docs Home | Next: Decorator Basics
This guide walks you through building a small Users API with bun-openapi. By the end you will have a running Bun server with automatic request validation, a generated OpenAPI 3.0 spec, and live Swagger UI.
Prerequisites: Bun >= 1.0 installed.
Install bun-openapi and one schema adapter. This guide uses class-validator, but you can swap it for TypeBox, Zod, or Valibot later.
bun add bun-openapi class-validatorStart by defining the data shapes your API will use. These classes serve double duty — they are TypeScript types for your code and the source for OpenAPI schema definitions.
import {
IsEmail,
IsString,
MinLength,
} from "class-validator";
class CreateUser {
@IsString()
@MinLength(1)
name!: string;
@IsEmail()
email!: string;
}
class User {
id!: string;
name!: string;
email!: string;
}TIP: With
class-validator, you can add validation constraints directly on the DTO fields. These constraints are enforced at runtime and also appear in the generated OpenAPI spec.
Controllers define your routes. The @Route decorator sets the base path, and HTTP-verb decorators (@Get, @Post, ...) register individual endpoints. Parameter decorators like @Body bind and validate incoming data automatically.
import {
Body,
Controller,
Get,
Post,
Returns,
Route,
Summary,
} from "bun-openapi";
@Route("/users")
class UserController extends Controller {
@Get()
@Summary("List users")
@Returns(200, [User], "List of users")
list() {
return [];
}
@Post()
@Summary("Create user")
@Returns(201, User, "Created user")
create(@Body(CreateUser) body: CreateUser) {
this.setStatus(201);
return {
id: crypto.randomUUID(),
name: body.name,
email: body.email,
};
}
}Let's break down what's happening:
@Route("/users")sets/usersas the base path for all methods in this class.@Get()combined with the base route creates aGET /usersendpoint.@Post()createsPOST /users.@Summary(...)becomes the endpoint summary in generated OpenAPI docs.@Returns(201, User, ...)tells bun-openapi what the success response looks like — both for documentation and optional response validation.@Body(CreateUser)validates the incoming JSON body against theCreateUserschema before the method executes.
TIP: If validation fails (e.g. a missing
namefield), the framework automatically returns a 400 error with details. You don't need to write any validation logic yourself.
Wire everything together with createApp() and pass the result to Bun.serve:
import { createApp } from "bun-openapi";
import { classValidator } from "bun-openapi/adapters/class-validator";
const app = createApp({
schema: classValidator(),
controllers: [UserController],
docs: { swagger: true },
openapi: {
service: {
name: "my-api",
version: "1.0.0",
},
},
});
Bun.serve({
port: 3000,
routes: app.routes,
fetch: app.fetch,
});createApp() does several things at startup:
- Reads decorator metadata from your controllers.
- Builds a Bun route map with validation, guards, and interceptors wired in.
- Generates an OpenAPI 3.0 document from the collected metadata.
- Optionally serves Swagger UI and the raw spec.
Start your server and visit:
- Swagger UI: http://localhost:3000/docs/swagger/
- OpenAPI JSON: http://localhost:3000/docs/swagger/openapi.json
You should see your Users endpoints listed with their request/response schemas — all generated from the decorators you wrote. Try sending a POST /users with an invalid body to see validation in action.
Now that you have a running API, explore deeper topics:
- Decorator Basics — understand the full decorator model and how layers compose.
- Request Binding — learn about
@Param,@Query,@Header, and scalar vs. whole-object binding. - Validation and Schemas — swap adapters, add constraints, and understand how validation integrates with OpenAPI.
- Error Handling — customize error responses with
errorFormatterand built-in exceptions. - Dependency Injection — organize code with providers and service classes.
- Guards and Security — add authentication and authorization to your endpoints.
- Descriptions and Metadata — enrich your OpenAPI docs with summaries, descriptions, and operation IDs.
See examples/02_crud/server.ts for a complete working setup.