NAME
Mojolicious::Plugin::OpenAPI::Guides::OpenAPIv3 - Mojolicious <3 OpenAPI v3
OVERVIEW
This guide will give you an introduction on how to use Mojolicious::Plugin::OpenAPI with OpenAPI version v3.x.
TUTORIAL
Specification
This plugin reads an OpenAPI specification <https://openapis.org/specification> and generates routes and input/output rules from it. See JSON::Validator for supported schema file formats.
{"openapi": "3.0.2","info": {"version": "1.0","title": "Some awesome API"},"paths": {"/pets": {"get": {"operationId": "getPets","x-mojo-name": "get_pets","x-mojo-to": "pet#list","summary": "Finds pets in the system","parameters": [{"in": "query","name": "age","schema": {"type": "integer"}}],"requestBody": {"content": {"application/json": {"schema": {"type": "object"}}}},"responses": {"200": {"description": "Pet response","content": {"application/json": {"schema": {"type": "object","properties": {"pets": {"type": "array","items": {"type": "object"}}}}}}}}}}},"servers": [{"url": "/api"}]}
The complete HTTP request for getting the "pet list" will be "GET /api/pets" The first part of the path ("/api") comes from "servers", the second part comes from the keys under "paths", and the HTTP method comes from the keys under "/pets".
The different parts of the specification can also be retrieved as JSON using the "OPTIONS" HTTP method. Example:
OPTIONS /api/petsOPTIONS /api/pets?method=get
Note that the use of "OPTIONS" is EXPERIMENTAL, and subject to change.
Here are some more details about the different keys:
- •
-
openapi, info and paths
These three sections are required to make the specification valid. Check out <https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md> for a complete reference to the specification.
- •
-
parameters, requestBody and responses
"parameters","requestBody"and"responses"will be used to define input and output validation rules, which is used by "openapi.input" in Mojolicious::Plugin::OpenAPI and when rendering the response back to the client, using"render(openapi => ...)".Here OpenAPIv3 input differs from the v2 spec, where
"parameters"is used for input in the path or query of the request. The"requestBody"is used for input passed in the body.Have a look at "RENDERER" in Mojolicious::Plugin::OpenAPI for more details about output rendering.
- •
-
operationId and x-mojo-name
See "Route names".
- •
-
x-mojo-placeholder
"x-mojo-placeholder"can be used inside a parameter definition to instruct Mojolicious to parse a path part in a certain way. Example:"parameters": [{"x-mojo-placeholder": "#","in": "path","name": "email","type": "string"}]See Mojolicious::Guides::Routing for more information about "standard", "relaxed" and "wildcard" placeholders. The default is to use the "standard" ("/:foo") placeholder.
- •
-
x-mojo-to
The non-standard part in the spec above is "x-mojo-to". The "x-mojo-to" key can be either a plain string, object (hash) or an array. The string and hash will be passed directly to "to" in Mojolicious::Routes::Route, while the array ref will be flattened. Examples:
"x-mojo-to": "pet#list"$route->to("pet#list");"x-mojo-to": {"controller": "pet", "action": "list", "foo": 123}$route->to({controller => "pet", action => "list", foo => 123);"x-mojo-to": ["pet#list", {"foo": 123}, ["format": ["json"]]]$route->to("pet#list", {foo => 123});$route->pattern->constraints->{format} = ["json"];
- •
-
security and securitySchemes
The securityScheme is added under components, where one way is to have the client place an apiKey in the header of the request
{..."components": {"securitySchemes": {"apiKey": {"name": "X-Api-Key","in": "header","type": "apiKey"}}}}It is then referenced under the path object as security like this
{..."paths": {"/pets": {"get": {"operationId": "getPets",..."security": [{"apiKey": []}]}}}}You can then utilize security, by adding a security callback when loading the plugin
$self->plugin(OpenAPI => {spec => $self->static->file("openapi.json")->path,security => {apiKey => sub {my ($c, $definition, $scopes, $cb) = @_;if (my $key = $c->tx->req->content->headers->header('X-Api-Key')) {if (got_valid_api_key()) {return $c->$cb();}else {return $c->$cb('Api Key not valid');}}else {return $c->$cb('Api Key header not present');}}}});
References with files
Only a file reference like
"$ref": "my-other-cool-component.json#/components/schemas/inputSchema"
Is supported, though a valid path must be used for both the reference and in the referenced file, in order to produce a valid spec output.
See "File references" in Known Issues for unsupported file references
Application
package Myapp;use Mojo::Base "Mojolicious";sub startup {my $app = shift;$app->plugin("OpenAPI" => {url => $app->home->rel_file("myapi.json")});}1;
The first thing in your code that you need to do is to load this plugin and the "Specification". See "register" in Mojolicious::Plugin::OpenAPI for information about what the plugin config can be.
See also "SYNOPSIS" in Mojolicious::Plugin::OpenAPI for example Mojolicious::Lite application.
Controller
package Myapp::Controller::Pet;use Mojo::Base "Mojolicious::Controller";sub list {# Do not continue on invalid input and render a default 400# error document.my $c = shift->openapi->valid_input or return;# You might want to introspect the specification for the current routemy $spec = $c->openapi->spec;unless ($spec->{'x-opening-hour'} == (localtime)[2]) {return $c->render(openapi => [], status => 498);}my $age = $c->param("age");my $body = $c->req->json;# $output will be validated by the OpenAPI spec before renderedmy $output = {pets => [{name => "kit-e-cat"}]};$c->render(openapi => $output);}1;
The input will be validated using "openapi.valid_input" in Mojolicious::Plugin::OpenAPI while the output is validated through then openapi handler.
Route names
Routes will get its name from either "x-mojo-name" or from "operationId" if defined in the specification.
The route name can also be used the other way around, to find already defined routes. This is especially useful for Mojolicious::Lite apps.
Note that if spec_route_name is used then all the route names will have that value as prefix:
spec_route_name = "my_cool_api"operationId or x-mojo-name = "Foo"Route name = "my_cool_api.Foo"
You can also set "x-mojo-name" in the spec, instead of passing spec_route_name to plugin():
{"openapi": "3.0.2","info": { "version": "1.0", "title": "Some awesome API" },"x-mojo-name": "my_cool_api"}
Default response schema
A default response definition will be added to the API spec, unless it's already defined. This schema will at least be used for invalid input (400 - Bad Request) and invalid output (500 - Internal Server Error), but can also be used in other cases.
See "default_response_codes" in Mojolicious::Plugin::OpenAPI and "default_response_name" in Mojolicious::Plugin::OpenAPI for more details on how to configure these settings.
The response schema will be added to your spec like this, unless already defined:
{..."components": {..."schemas": {..."DefaultResponse": {"type": "object","required": ["errors"],"properties": {"errors": {"type": "array","items": {"type": "object","required": ["message"],"properties": {"message": {"type": "string"}, "path": {"type": "string"}}}}}}}}}
The "errors" key will contain one element for all the invalid data, and not just the first one. The useful part for a client is mostly the "path", while the "message" is just to add some human readable debug information for why this request/response failed.
Rendering binary data
Rendering assets and binary data should be accomplished by using the standard Mojolicious tools:
sub get_image {my $c = shift->openapi->valid_input or return;my $asset = Mojo::Asset::File->new(path => "image.jpeg");$c->res->headers->content_type("image/jpeg");$c->reply->asset($asset);}
OpenAPIv2 to OpenAPIv3 conversion
Both online and offline tools are available. One example is of this is <https://github.com/mermade/openapi-webconverter>
Known issues
File references
Relative file references like the following
"$ref": "my-cool-component.json#""$ref": "my-cool-component.json"
Will also be placed under '#/definitions/...', again producing a spec output which will not pass validation.
SEE ALSO
Mojolicious::Plugin::OpenAPI, <https://openapis.org/specification>.