REST Framework auto-generates an OpenAPI document for every controller, reflecting the
real configuration that drives the API: fields, field_config, validators, enum variants,
association metadata, rich-text and attachment fields, bulk support, and more. The document is
served from the controller’s OPTIONS endpoint — no extra setup, no drift between spec and
behavior.
Every REST Framework controller responds to OPTIONS with its OpenAPI document:
curl -X OPTIONS https://example.com/api/movies
From a browser, the OPTIONS action also has an HTML rendering accessible from the route list
in the browsable API, so you can explore the document interactively.
The fields of the info block and the tag description come from standard controller
attributes:
class Api::MoviesController < ApiController
self.model = Movie
self.title = "Movies API"
self.description = "Read and manage movie records. Supports bulk operations."
self.version = "2026.04"
end
title → info.title and the tag name.description → info.description and the tag description.version → info.version (defaults to empty).If title is unset, it falls back to the controller’s titleized class name, with
RESTFramework.config.inflect_acronyms applied so things like "ID", "API", and "REST"
stay capitalized.
When the controller has a model, the OpenAPI document includes a full schema for that model
under components.schemas.<SchemaName>. The schema name defaults to the controller class name
with "Controller" stripped and "::" replaced with "." — so Api::MoviesController becomes
Api.Movies. Every field becomes a property:
title — set from the label (either field_config[:label] or the auto-generated titleized
name).type — derived from the column or attribute (string, integer, boolean, etc.), or set
to array / object for associations.readOnly — true when the field is marked read-only (primary keys, fields in
read_only_fields, method fields, or fields with field_config[f][:read_only] = true).default — default from the schema or field_config.enum — enum key names, when the column is an ActiveRecord enum.required — fields inferred from null: false or presence validators land in the schema’s
top-level required array.On top of the standard OpenAPI vocabulary, the framework adds several x-rrf-* extensions for
browsable-API clients and tools that want to consume the full field metadata.
x-rrf-* Extensions| Extension | Description |
|---|---|
x-rrf-kind |
"column", "association", "method", "attribute", "rich_text", or "attachment". |
x-rrf-rich_text |
true when the field is an Action Text rich text attribute. |
x-rrf-attachment |
:has_one_attached or :has_many_attached for Active Storage attachments. |
x-rrf-enum_variants |
Full map of enum name → database value (standard enum only lists keys). |
x-rrf-validators |
Hash of validator kind → array of option hashes, for every model-level validator. |
x-rrf-reflection |
Association metadata: class_name, foreign_key, association_foreign_key, association_primary_key, inverse_of, join_table. |
x-rrf-association_pk |
The primary key of the associated class. |
x-rrf-sub_fields |
Sub-fields used for serializing/filtering/ordering the association. |
x-rrf-sub_fields_metadata |
Kind (column vs method) for each sub-field. |
x-rrf-id_field |
The scalar id field (e.g., user_id, tag_ids). |
x-rrf-nested_attributes_options |
accepts_nested_attributes_for options for the association. |
| Extension | Description |
|---|---|
x-rrf-primary_key |
The model’s primary key name. |
x-rrf-callbacks |
The controller’s _process_action_callbacks (for tools that want to show filters). |
x-rrf-bulk-create |
true when the controller has bulk = true — bulk create overloads the collection POST, so this is the only way to indicate it in OpenAPI. |
Any extra metadata you provide in extra_actions / extra_member_actions flows through to the
operation spec:
self.extra_member_actions = {
disable: {
methods: :patch,
metadata: {
label: "Disable Movie",
description: "Marks the movie as disabled without deleting it.",
},
},
}
metadata[:label] → the operation summary (default: the action name).metadata[:description] → the operation description.metadata → attached as x-rrf-metadata on the operation.The framework populates standard OpenAPI responses automatically:
create → 201.destroy → 204.200.400 ($ref: BadRequest) and 404 ($ref: NotFound)
response schemas, plus a shared Error schema for both.POST / PUT / PATCH operations get a requestBody referencing the model’s schema.By default, each controller’s document covers only its own routes. To aggregate child controllers into a single parent document (useful for generating a one-big-document view of a sub-API), set:
class Api::DemoController < ApiController
include RESTFramework::Controller
self.openapi_include_children = true
end
With this, OPTIONS /api/demo returns a merged document containing paths, tags, and schemas
from every child controller reachable from the root.
Because the framework emits a spec-compliant OpenAPI 3.1.1 document, any standard tool works:
OPTIONS URL.