-
Notifications
You must be signed in to change notification settings - Fork 25
OpenAPI Guide
Use OpenAPI Playground to see what documentation Rage::OpenAPI generates without having to set up a project. Experiment with various tags and serializer combinations to understand how Rage::OpenAPI works.
Rage::OpenAPI is a lightweight solution that allows you to create and customize an OpenAPI specification for your application. To enable OpenAPI specification for an application, update your config.ru file to mount the Rage::OpenAPI component at the URL of your choice. For example:
map "/publicapi" do
run Rage::OpenAPI.application
endAlternatively, Rage::OpenAPI can also be mounted in config/routes.rb:
Rage.routes.draw do
mount Rage::OpenAPI.application, at: "/publicapi"
endConsider we have the following route definitions:
Rage.routes.draw do
namespace :api do
namespace :v1 do
resources :users, only: %i[index show create]
resources :photos, only: :index
end
namespace :v2 do
resources :users, only: :show
end
end
endLaunch the server using the rage s command and visit localhost:3000/publicapi to see the specification:
Currently, our specification is not very useful. All we do is display a list of endpoints the application exposes. Sometimes, this is what we are looking for, but most of the time, what we also want is to add some information to our specification and turn a faceless list of API endpoints into actual documentation.
One of the main concepts of Rage::OpenAPI is limited scope. We believe that complex DSLs that attempt to implement the whole OpenAPI specification make no sense - at some point it becomes easier to manually create an OpenAPI document instead of learning a DSL, which will then create the OpenAPI document for you.
Instead, Rage::OpenAPI aims to enhance the developer experience by focusing on specific tasks that API developers face. With Rage::OpenAPI, customizations to your API specification can be made using YARD-style tags placed directly on action definitions inside controllers. Not only is this a very simple and natural way to document APIs, but it also allows you to simultaneously document your code, too.
There are two types of tags. Some tags, like @description, can only be attached to an action within a controller. Others, like @deprecated, can also be attached to a class. This way, all actions in the class together with all actions in child classes will become deprecated.
The simplest customization you can make is to add a summary - comment on your action to add a one-line summary to the specification.
class Api::V1::UsersController < ApplicationController
# Returns the list of all active non-admin users.
def index
end
endReload the page to see the updated specification:
@description allows you to add a longer description for documentation purposes. The tag supports markdown, and, similar to other text tags, can be multi-line.
class Api::V1::UsersController < ApplicationController
# Returns the list of all active non-admin users.
# @description The API endpoint provides access to a list of
# registered users within the system. Only non-admin
# users are returned by this endpoint.
def show
end
endThe @version and @title tags can only be used once per API specification and allow setting the title and version properties on the info object.
The @internal tag allows you to leave comments for other developers. Rage::OpenAPI ignores this tag and doesn't put its contents into the specification:
class Api::V1::UsersController < ApplicationController
# @internal All changes to this action must first be approved by a principal engineer.
def create
end
endWith @deprecated you can mark an action as deprecated:
class Api::V1::UsersController < ApplicationController
def index
end
# @deprecated
def create
end
endAdditionally, you can mark the whole class as deprecated. In such a case, make sure to place the tag inside the class without attaching it to an action:
class Api::V1::UsersController < ApplicationController
# @deprecated
def index
end
def create
end
endUsually, Ruby developers place authentication logic in the before_action callbacks. With Rage::OpenAPI, you can specify the name of the before_action used for authentication using the @auth tag. For instance:
class ApplicationController
before_action :authenticate_by_token
# @auth authenticate_by_token
private
def authenticate_by_token
...
end
endIn this example, Rage::OpenAPI will follow all applications of the authenticate_by_token callback, take into account the skip_before_action calls, and apply the corresponding security scheme to all necessary controller actions. By default, the scheme that corresponds with the authenticate_with_http_token method is used:
type: http
scheme: bearerYou can customize the security scheme definition by inlining a corresponding YAML entry:
class ApplicationController
before_action :authenticate_by_token
# @auth authenticate_by_token
# type: apiKey
# in: header
# name: X-API-Key
private
def authenticate_by_token
...
end
endIt is also possible to have multiple security schemes:
class ApplicationController
before_action :authenticate_by_user_token
before_action :authenticate_by_service_token
# @auth authenticate_by_user_token
# @auth authenticate_by_service_token
endThe optional second argument can be used to change the name of the security scheme:
class ApplicationController
before_action :authenticate_by_token
# @auth authenticate_by_token UserAuth
endWith Rage::OpenAPI, there are three ways to specify the response schema.
For simple use cases, you can inline a YAML definition of the response into the tag:
class Api::V1::UsersController < ApplicationController
# @response { id: Integer, full_name: String }
def show
end
endFor the maximum flexibility, you can use shared references. Finally, Rage::OpenAPI can automatically build the response schema based on ActiveRecord models or Alba resources.
For example, let's say we have the following Alba resource:
class UserResource
include Alba::Resource
root_key :user
attributes :id, :name, :email
endPass the resource to the @response tag to have Rage::OpenAPI build the response schema:
class Api::V1::UsersController < ApplicationController
# @response UserResource
def show
end
endThe response schema Rage::OpenAPI builds will look like this:
schema:
type: object
properties:
user:
type: object
properties:
id:
type: string
name:
type: string
email:
type: stringMost Alba features, including associations, key transformations, inheritance, and typed attributes, are supported. Use the [] or Array<> to specify that the endpoint returns a collection:
class Api::V1::UsersController < ApplicationController
# @response Array<UserResource>
def index
end
endRage::OpenAPI also takes namespaces into account, so in case you have the Api::V1::UserResource and Api::V2::UserResource classes, referencing UserResource from Api::V2::UsersController will pick Api::V2::UserResource.
Additionally, in case the endpoint can have different responses, you can optionally pass the response code in the first argument:
class Api::V1::UsersController < ApplicationController
# @response 200 UserResource
# @response 404 { error_code: String }
def show
end
endResponse tags can also be global, in which case they will be applied to all actions of all child controllers:
class Api::V1::BaseController < ApplicationController
# @response 404 { status: "NOT_FOUND" }
# @response 500 { status: "ERROR", message: String }
endThe @request tag is pretty similar to the @response tag - it can accept an inline YAML, a shared reference, or an ActiveRecord model. In the latter case, Rage::OpenAPI will strip all internal attributes from the request, e.g. id, created_at, or type.
class Api::V1::UsersController < ApplicationController
# @request User
def create
end
endThe @param tag can be used to describe query parameters:
class Api::V1::UsersController < ApplicationController
# @param account_id The account the records are attached to
def index
end
endBy default, parameters are required. To mark a parameter as optional, use a ? after the parameter name:
class Api::V1::UsersController < ApplicationController
# @param created_at? Filter records by creation date
def index
end
endYou can also specify the type of the parameter by enclosing it in {} and providing it as a second argument:
class Api::V1::UsersController < ApplicationController
# @param is_active {Boolean} Filter records by active status
def index
end
endRage::OpenAPI allows you to have a file with shared OpenAPI definitions that you can reference from the tags like @response, @request, or @param. The file can either be in the YAML or JSON format and should have the root components key.
For example, create the config/openapi_components.yml file with the following content:
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: stringThen, reference the component inside the controller:
class Api::V1::UsersController < ApplicationController
# @response #/components/schemas/User
def show
end
endThere are two options you can use to limit the visibility of specific endpoints. The first is the @private tag. You can use @private to either hide single actions:
class Api::V1::UsersController < ApplicationController
def show
end
# @private
def create
end
endor the entire class:
class Api::V1::UsersController < ApplicationController
# @private
def show
end
def create
end
endAdditionally, you can limit Rage::OpenAPI to a specific namespace by passing it into the Rage::OpenAPI.application call:
map "/publicapi" do
run Rage::OpenAPI.application(namespace: "Api::V2")
endWith this option, you can even have different specifications for different use cases:
map "/publicapi" do
run Rage::OpenAPI.application(namespace: "Api::Public")
end
map "/internalapi" do
use Rack::Auth::Basic
run Rage::OpenAPI.application(namespace: "Api::Internal")
endRage::OpenAPI groups all endpoints by tags that it derives from controller names. For example, all actions from the Api::V1::UsersController controller will be grouped under the v1/Users tag.
You can customize this behavior with a custom tag resolver:
Rage.configure do
config.openapi.tag_resolver = proc do |controller, action, default_tag|
# ...
end
endA custom tag resolver is a proc that is passed to the config.openapi.tag_resolver configuration. It is invoked for every endpoint Rage::OpenAPI processes and accepts three arguments:
- the controller class;
- the action symbol;
- the original tag generated by
Rage::OpenAPI;
The resolver should return a tag (or an array of tags) that will be assigned to the endpoint.