BulletTrain::Api
API capabilities for apps built with Bullet Train framework.
Quick Start
Installation
Add this to your Gemfile:
gem "bullet_train-api"
Then, run bundle
or install it manually using gem install bullet_train-api
.
Contents
API
BulletTrain::Api defines basic REST actions for every model generated with super-scaffolding.
Accessing
BulletTrain::Api uses Bearer token as a default authentication method with the help of Doorkeeper gem. It uses the idea that in order to access the API, there should be a Platform Application object, which can have access to different parts of the application. In Bullet Train, each Team may have several Platform Applications (created in Developers > API menu). When a Platform Application is created, it automatically generates an Bearer Token needed to access the API, controlled by this Platform Application.
Versioning
Versions are being set automatically based on the location of the controller.
Current version can also be checked with
BulletTrain::Api.current_version
Views
All API response templates are located in app/views/api/<version>/
and are written using standard jbuilder syntax.
Documentation
This gem automatically generates OpenAPI 3.1 compatible documentation at:
/api/v1/openapi.yaml
Index File
app/views/api/v1/open_api/index.yaml.erb
The index file is the central point for the API documentation. This consists of a number of sections, some that get pulled in and bundled at build time.
The file is in YAML format, but includes erb code which generates additional YAML with the help of jbuilder-schema
gem.
Automatic Components
There are several helper methods available in Index file.
One of them is automatic_components_for
, which generates two schemas of a model, Attributes and Parameters, based on it's Jbuilder show file.
Parameters are used in requests and Attributes are used in responses.
For example this code:
components:
schemas:
<%= automatic_components_for User %>
looks for the file app/views/api/v1/users/_user.json.jbuilder
.
Let's say it has this contents:
json.extract! user,
:id,
:email,
:name
then the produced component will be:
components:
schemas:
UserAttributes:
type: object
title: Users
description: Users
required:
- id
- email
properties:
id:
type: integer
description: Team ID
email:
type: string
description: Email Address
name:
type:
- string
- 'null'
description: Name
example:
id: 42
email: generic-user-1@example.com
name: Example Name
UserParameters:
type: object
title: Users
description: Users
required:
- email
properties:
email:
type: string
description: Email Address
name:
type:
- string
- 'null'
description: Name
example:
email: generic-user-1@example.com
name: Example First Name
As you can see, it automatically sets required fields based on presence validators of the model,
field types based on the value found in Jbuilder file, descriptions and examples.
More on how it works and how you can customize the output in jbuilder-schema
documentation.
Manually Extending Component Schemas
While automatic_components_for
automatically adds parameters and attributes from your application, sometimes it is
necessary to manually specify parameters and attributes in addition to the automatic ones, due to custom code in
your app.
automatic_components_for
allows to you add or remove parameters and attributes. You can also specify that
parameters that are only available for create or update methods.
Adding or Removing Specific Attributes for a Component
To add or remove specific attributes for a component:
automatic_components_for User,
parameters: {
add: {
tag_ids: {
type: :array,
items: {type: :integer},
description: "Array of Tag IDs for the User."
}
}
},
attributes: {
remove: [:email, :time_zone],
add: {
url: {
type: :string,
description: "The URL of the User's image."
}
}
}
Specifying Parameters for Create or Update Methods
To specify parameters that only exist for the create or update methods, use the following format:
automatic_components_for Enrollment,
parameters: {
create: {
add: {
user_id: {
type: :integer,
description: "ID of the User who enrolled.",
example: 42
}
}
},
update: {
remove: [:time_zone]
}
}
Automatic Paths
Method automatic_paths_for
generates basic REST paths. It accepts model as it's argument.
To generate paths for nested models, pass parent model as a second argument. It also accepts except
as a third argument,
where you can list actions which paths you don't want to be generated.
If the methods defined in the automatic_paths_for
for the endpoints support
a write action (i.e. create or update), then doc generation uses the strong_parameters
defined in the corresponding controller to generate the Parameters section in the schema.
If your endpoint accepts different parameters for the create and update actions, if you define <model>_update_params
in the
corresponding controller to define the update parameters, these will be used to generate the Parameter for the
update method in the schema.
Automatic paths are generated for basic REST actions. You can customize those paths or add your own by creating a
file at app/views/api/<version>/open_api/<Model.underscore.plural>/_paths.yaml.erb
. For REST paths there's no need to
duplicate all the schema, you can specify only what differs from auto-generated code.
External Markdown files
External text files with Markdown markup can be added with external_doc
method.
It assumes that the file with .md
extension can be found in app/views/api/<version>/open_api/docs
.
You can also use description_for
method with a model, then there should be file app/views/api/<version>/open_api/docs/<Model.name.underscore>_description.md
This allows including external markdown files in OpenAPI schema like in this example:
openapi: 3.1.0
info:
...
description: <%= external_doc "info_description" %>
...
tags:
- name: Team
description: <%= description_for Team %>
- name: Addresses::Country
description: <%= description_for Addresses::Country %>
...
supposing the following files exist:
app/views/api/v1/open_api/docs/info_description.md
app/views/api/v1/open_api/docs/team_description.md
app/views/api/v1/open_api/docs/addresses/country_description.md
Examples
In order to generate example requests and responses for the documentation in the
automatic_components_for
calls, the bullet_train-api gem contains ExampleBot
which uses FactoryBot to build an in-memory representation of the model,
then generates the relevant OpenAPI schema for that model.
ExampleBot will attempt to create an instance of the given model called <model>_example
.
For namespaced models, <plural_namespaces>_<model>_example
For example, for the Order model, use order_example
factory.
For Orders::Invoices::LineItem, use orders_invoices_line_item_example
factory.
When writing the factory, the example factory should usually inherit from the existing factory, but in some cases (usually if the existing factory uses callbacks or creates associations that you may not want), you may wish to not inherit from the existing one.
Example IDs
Since we only want to use in-memory instances, we need to ensure that all examples
have an id
specified, along with created_at
and updated_at
, otherwise they
will show as null
in the examples.
You may wish to use sequence
for the id in the examples, but you need to be careful
not to create circular references (see Associations section below)
Associations
You need to be careful when specifying associated examples since it is easy to get
into a recursive loop (see Example IDs section above). Also, ensure that you only
create associations using FactoryBot.example()
and not create
, otherwise it will
create records in your database.
Localization
The documentation requires that "#{model.name.underscore.pluralize}.label"
localisation value is defined,
which will be used to set model title and description.
Rake Tasks
Bump version
Bump the current version of application's API:
rake bullet_train:api:bump_version
Export OpenAPI document in file
Export the OpenAPI schema for the application to tmp/openapi
directory:
rake bullet_train:api:export_openapi_schema
Push OpenAPI document to Redocly
Needs REDOCLY_ORGANIZATION_ID
and REDOCLY_API_KEY
to be set:
rake bullet_train:api:push_to_redocly
Create separate translations for API
Starting in 1.6.28, Bullet Train Scaffolding generates separate translations for API documentation: api_title
and api_description
.
This rake task will add those translations for the existing fields, based on their heading
value:
rake bullet_train:api:create_translations
It only needs to be run once after upgrade.
Contributing
Contributions are welcome! Report bugs and submit pull requests on GitHub.
License
This gem is open source under the MIT License.