Skip to content

OpenAPI Emission

Generate an OpenAPI 3.0 JSON spec alongside your TypeScript output. Your C# types and endpoints become a standards-compliant API spec — useful for documentation tools, API gateways, and external consumers.

Command

bash
dotnet rivet --project path/to/Api.csproj --output ./generated --openapi

This produces everything the normal forward pipeline does (types, client, validators) plus an openapi.json file.

Security schemes

Add a security scheme to the spec with --security:

bash
# Bearer token
dotnet rivet --project Api.csproj -o dir --openapi --security bearer

# Bearer JWT
dotnet rivet --project Api.csproj -o dir --openapi --security bearer:jwt

# Cookie-based
dotnet rivet --project Api.csproj -o dir --openapi --security cookie:sid

# API key in header
dotnet rivet --project Api.csproj -o dir --openapi --security apikey:header:X-Api-Key

Per-endpoint security

On contract endpoints, use .Anonymous() and .Secure(scheme) to control per-endpoint security:

csharp
public static readonly RouteDefinition<List<MemberDto>> List =
    Define.Get<List<MemberDto>>("/api/members")
        .Summary("List all team members")
        .Anonymous();  // no auth required

public static readonly RouteDefinition<InviteMemberRequest, InviteMemberResponse> Invite =
    Define.Post<InviteMemberRequest, InviteMemberResponse>("/api/members")
        .Summary("Invite a new team member")
        .Description("Creates a new member invitation and sends an email notification")
        .Secure("admin");  // requires admin scheme

These are emitted as security: [] (anonymous) or security: [{ admin: [] }] on the corresponding OpenAPI operation.

What it produces

The generated openapi.json includes:

  • Paths — one operation per endpoint, with parameters, request bodies, and response schemas
  • Schemas — all C# types marked with [RivetType] or referenced by endpoints, under #/components/schemas
  • Security schemes — if --security is specified
  • Summary / Description.Summary() emits to the operation summary field, .Description() to description
  • Property metadata[RivetDescription], [RivetConstraints], [RivetDefault], [RivetExample], [RivetReadOnly], [RivetWriteOnly] attributes are emitted on property schemas
  • Type descriptions[RivetDescription] on records emits description on the schema object
  • Endpoint/content examples — request and response examples on the endpoint model emit to requestBody.content[*] and responses[*].content[*]

Type representation

  • Generic types are monomorphised: PagedResult<TaskDto> becomes PagedResult_TaskDto in the schema, with an x-rivet-generic extension that allows the importer to reconstruct the generic template
  • Branded value objects (single-property records) are emitted as component schemas with an x-rivet-brand extension (e.g., Email becomes { "type": "string", "x-rivet-brand": "Email" }), and references use $ref
  • File upload records with IFormFile properties produce multipart/form-data schemas using $ref to the component schema, with x-rivet-file markers on file properties
  • Enums are type: string with enum: [...]
  • Nullable types use nullable: true

The x-rivet-* extensions are ignored by non-Rivet consumers (valid OpenAPI 3.0). They enable lossless round-trips when the spec is imported back into Rivet.

Endpoint/content example emission

Rivet emits endpoint-level examples from either contract builder calls or controller example attributes.

  • One unnamed inline example on a media type emits as singular example.
  • Named examples, multiple examples, and ref-backed examples emit under examples.
  • Ref-backed examples emit $ref entries that point at #/components/examples/{id} when Rivet has both the component id and resolved JSON payload.
  • If Rivet cannot safely emit a valid ref, it inlines the example content instead of producing a broken $ref.

Example:

csharp
public static readonly RouteDefinition<CreateWidgetRequest, WidgetDto> Create =
    Define.Post<CreateWidgetRequest, WidgetDto>("/api/widgets")
        .RequestExampleJson("{\"name\":\"starter-widget\"}")
        .Returns<ProblemDto>(422)
        .ResponseExampleRef(
            422,
            "widget-validation-problem",
            "{\"title\":\"Validation failed\"}",
            name: "validationProblem");

This feature covers request-body and response-body examples. Property-level examples still come from [RivetExample] on DTO properties and emit on schemas, not on operation content.

Viewing the spec

The generated openapi.json works with any OpenAPI 3.0 tool:

  • Swagger Editor — paste or upload the JSON
  • Redoclynpx @redocly/cli preview-docs openapi.json
  • API gateways (Kong, AWS API Gateway, Azure APIM) — import directly

Consistency with import

The OpenAPI emitter and the OpenAPI importer use consistent type mappings. What the emitter outputs, the importer can consume — enabling a round-trip workflow where you emit a spec, hand it to another team, and they import it back.

The emitter annotates the spec with x-rivet-* vendor extensions so that brands, generic types, and file upload record names survive the round-trip without loss. See OpenAPI Round-Trips for details.