NAME

Mojolicious::Plugin::OpenAPI::Guides::Tutorial - Mojolicious <3 Open API (Swagger)

OVERVIEW

This guide will give you an introduction to how to use Mojolicious::Plugin::OpenAPI.

You can also have a look at http://thorsen.pm/perl/programming/2015/07/05/mojolicious-swagger2.html, which includes reasons for why you want to use Open API - also known as Swagger.

TUTORIAL

Specification

This plugin reads an OpenAPI specification and generate routes and input/output rules from it. See JSON::Validator for supported schema file formats.

{
  "basePath": "/api",
  "paths": {
    "/pets": {
      "get": {
        "x-mojo-to": "pet#list",
        "summary": "Finds pets in the system",
        "parameters": [
          {"in": "body", "name": "body", "schema": {"type": "object"}},
          {"in": "query", "name": "age", "type": "integer"}}
        ],
        "responses": {
          "200": {
            "description": "Pet response",
            "schema": { "type": "array", "items": { "type": "object" } }
          },
          "default": {
            "description": "Unexpected error",
            "schema": { "$ref": "http://git.io/vcKD4#" }
          }
        }
      }
    }
  }
}

The complete HTTP request for getting the "pet list" will be GET /api/pets The first part of the path ("/api") comes from basePath, the second part comes from the key under paths, and the HTTP method comes from the key under /pets.

parameters and responses will be used to define rules for input and output. Continue reading for explanation about "x-mojo-to".

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 flatten. 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}]
$route->to("pet#list", {foo => 123});

x-mojo-name

"x-mojo-name" is also a non-standard key, which will either find an existing route (useful for Mojolicious::Lite apps) or name the route which is generated. The default value used is "operationId" (see the specification), unless "x-mojo-name" is specified.

Application

package Myapp;
use Mojolicious;

sub startup {
  my $app = shift;
  $app->plugin("OpenAPI" => {url => $app->home->rel_file("myapi.json")});
}

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;

sub list {
  my $c = shift;

  # Do not continue on invalid input and render a default 400
  # error document.
  return if $c->openapi->invalid_input;

  # You might want to introspect the specification for the current route
  my $spec = $c->openapi->spec;
  unless ($spec->{'x-opening-hour'} == (localtime)[2]) {
    return $c->reply->openapi([], 498);
  }

  # $c->openapi->invalid_input copies valid data to validation object,
  # and the normal Mojolicious api works as well.
  my $input = $c->validation->output;
  my $age   = $c->param("age"); # same as $input->{age}
  my $body  = $c->req->json;    # same as $input->{body}

  # $output will be validated by the OpenAPI spec before rendered
  my $output = {pets => [{name => "kit-e-cat"}]};
  $c->reply->openapi(200 => $output);
}

The input will be validated using "openapi.invalid_input" in Mojolicious::Plugin::OpenAPI while the output is validated through "reply.openapi" in Mojolicious::Plugin::OpenAPI.

Default error document

The default error document rendered on invalid input and output looks like this:

{
  "errors": [
    {"path": "/some/json/path", "message": "Some error message"},
    {"path": "/age", "message": "Expected integer - got 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.

The HTTP status code on invalid input is 400, and 500 for invalid output

SEE ALSO

Mojolicious::Plugin::OpenAPI, https://openapis.org/specification.