Skip to content

API Documentation — Scalar & OpenAPI 3.1

An API without discoverable documentation creates friction: frontend developers read source code, QA teams guess request shapes, and onboarding new team members takes days instead of minutes. Static Swagger pages help, but they lack real authentication flows — testers still reach for Postman or curl.

Granit.Http.ApiDocumentation generates versioned OpenAPI 3.1 documents and serves Scalar, a modern interactive UI where developers can authenticate with OAuth2/PKCE and execute requests directly in the browser. One document is generated per API major version, so consumers always find the spec that matches their integration. The result: self-service API exploration from day one, fewer support requests, and documentation that stays in sync with the code automatically.

[DependsOn(typeof(GranitHttpApiDocumentationModule))]
public class AppModule : GranitModule { }
{
"ApiDocumentation": {
"Title": "Clinic API",
"MajorVersions": [1, 2],
"Description": "Patient and appointment management API"
}
}

In Program.cs:

app.UseGranitApiDocumentation(); // Maps /openapi/v1.json, /openapi/v2.json, /scalar

Provide realistic example values for request DTOs without coupling to OpenAPI:

public class AppointmentSchemaExamples : ISchemaExampleProvider
{
public IReadOnlyDictionary<Type, JsonNode> GetExamples() => new Dictionary<Type, JsonNode>
{
[typeof(CreateAppointmentRequest)] = new JsonObject
{
["patientId"] = "d4e5f6a7-1234-5678-9abc-def012345678",
["doctorId"] = "a1b2c3d4-5678-9abc-def0-123456789abc",
["scheduledAt"] = "2026-04-15T09:30:00Z",
["durationMinutes"] = 30
}
};
}

Implementations of ISchemaExampleProvider are auto-discovered at startup.

Exclude inter-service endpoints from public documentation:

app.MapPost("/webhooks/keycloak", HandleKeycloakWebhook)
.WithMetadata(new InternalApiAttribute());

The module registers these OpenAPI transformers automatically:

TransformerPurpose
JwtBearerSecuritySchemeTransformerAdds Bearer security scheme when JWT is configured
OAuth2SecuritySchemeTransformerReplaces Bearer with OAuth2 Authorization Code when configured
SecurityRequirementOperationTransformerAnonymous endpoints override global security
ProblemDetailsSchemaDocumentTransformerAdds RFC 7807 ProblemDetails schema
ProblemDetailsResponseOperationTransformerDocuments 4xx/5xx Problem Details responses
TenantHeaderOperationTransformerDocuments X-Tenant-Id header when enabled
InternalApiDocumentTransformerRemoves [InternalApi] endpoints
WolverineOpenApiOperationTransformerEnhances Wolverine HTTP endpoint documentation
SchemaExampleSchemaTransformerApplies ISchemaExampleProvider examples
PropertyDefaultDescription
Title"API"OpenAPI document and Scalar UI title
MajorVersions[1]Major version numbers to document
DescriptionnullOpenAPI description (Markdown supported)
PartyEmailnullParty email in OpenAPI info
LogoUrlnullLogo URL for Scalar sidebar
FaviconUrlnullFavicon for Scalar page
EnableInProductionfalseExpose docs in Production
EnableTenantHeaderfalseDocument required tenant header
TenantHeaderName"X-Tenant-Id"Tenant header name
AuthorizationPolicynullPolicy for doc endpoints (null = inherit, "" = anonymous)
OAuth2.AuthorizationUrlnullOAuth2 authorization endpoint
OAuth2.TokenUrlnullOAuth2 token endpoint
OAuth2.ClientIdnullPublic OAuth2 client ID (PKCE-capable)
OAuth2.EnablePkcetrueEnable PKCE with S256
OAuth2.Scopes["openid"]OAuth2 scopes to request
CategoryKey typesPackage
ModuleGranitHttpApiDocumentationModule
OptionsApiDocumentationOptions, OAuth2OptionsGranit.Http.ApiDocumentation
Extension pointsISchemaExampleProvider, InternalApiAttributeGranit.Http.ApiDocumentation
ExtensionsAddGranitApiDocumentation(), UseGranitApiDocumentation()Granit.Http.ApiDocumentation