- Notifications
You must be signed in to change notification settings - Fork7
mojolicious plugin openapi_tutorial
I have always wanted to get my handsdirty withSwagger. I recently fell overMojolicious::Plugin::OpenAPI, which fits into myboring stack and I decided to do a prototype.
I followed thetutorial for Mojolicious::Plugin::OpenAPI and found it a bit confusing, so I decided to write up a more simple tutorial.
This tutorial requires that you haveMojolicious installed and recommendscarton. The installation of these components is however beyond the scope of this tutorial.
OpenAPI comes fromSwagger, which I have had a look at, much water has run under that bridge, so now it is time to look atOpenAPI a specification on how to write RESTful APIs in a standardised format.
Here goes, lets start with a basichello world
example,all files are available on GitHub.
First we set up an application, yes we could do aMojolicious lite-app, but I primarily useMojolicious apps, so I think it makes sense to keep stick to this for reference.
$ mojo generate app HelloWorld
Jump into our newly generated application directory
$cd hello_world
We then install the plugin we need to enableOpenAPI in ourMojolicious application
UsingCPAN shell:
$ perl -MCPAN -e shell install Mojolicious::Plugin::OpenAPI
Usingcpanm
:
$ cpanm Mojolicious::Plugin::OpenAPI
If you need help installing please refer tothe CPAN installation guide.
Create a definition JSON file based onOpenAPI to support an Hello World implementation based on theOpenAPI specification:
$ touch openapi.conf
The exact name of this file is insignifcant, I just prefer to have clear and understandable filenames for easy identification.
Openopenapi.conf
and insert the followingsnippet:
{"swagger":"2.0","info": {"version":"1.0","title":"Hello World example" },"basePath":"/api","paths": {"/hello_world": {"get": {"operationId":"helloWorld","x-mojo-name":"hello_world","x-mojo-to":"example#hello_world","summary":"Example app returning hello world","responses": {"200": {"description":"Returning string 'hello world'","schema": {"type":"object","properties": {"greeting": {"type":"string" } } } },"default": {"description":"Unexpected error","schema": {} } } } } }}
Now lets go over our definiton.
basePath
: defines the root of our URL, so we would be able to access our application at/api
, recommendations on versioning APIs using this part is do exist, but for our example application, this is out of scope.paths
: here we define our first API path, so our Hello World application can be accessed at:/api/hello_world
operationId
: the is an operation identifier, it is important for the OpenAPI part, whereas the two following definitions are mappings of the same operation identifier towards theMojolicious applicationx-mojo-name
: this is the name used to identify our operation in theMojolicious applicationx-mojo-to
: this is the specification for the route to be used for our operation in theMojolicious application, more on this laterresponses
: here we define the type we want to handle, for now we settle for200
. The response definition outline our response, this could be boiled down to astring
instead of anobject
, with properties, but the example would be cometoo simple and in my opinion we work primarily with objects over basic types, so this extended example makes for a better reference.
Next step is to enable theMetaCPAN: Mojolicious::Plugin::OpenAPI plugin in the application
Open the file:lib/HelloWorld.pm
and add the following snippet:
$self->plugin("OpenAPI"=> {url=>$self->home->rel_file("openapi.json")});
Note the pointer to our previously created file:openapi.json
.
The complete file should look like the following:
packageHelloWorld;use Mojo::Base'Mojolicious';# This method will run once at server startsubstartup {my$self =shift;$self->plugin('OpenAPI'=> {url=>$self->home->rel_file('openapi.json')});# Load configuration from hash returned by "my_app.conf"my$config =$self->plugin('Config');# Documentation browser under "/perldoc"$self->plugin('PODRenderer')if$config->{perldoc};# Routermy$r =$self->routes;# Normal route to controller$r->get('/')->to('example#welcome');}1;
Then we add the acual operation, open the file:lib/HelloWorld/Controller/Example.pm
and add the following snippet:
subhello_world {my$c =shift->openapi->valid_inputorreturn;my$output = {greeting=>'Hello World' };$c->render(openapi=>$output);}
Note that this maps to the definition in our API definition:openapi.conf
"x-mojo-to":"example#hello_world",
The complete file should look like the following:
packageHelloWorld::Controller::Example;use Mojo::Base'Mojolicious::Controller';# This action will render a templatesubwelcome {my$self =shift;# Render template "example/welcome.html.ep" with message$self->render(msg=>'Welcome to the Mojolicious real-time web framework!');}subhello_world {my$c =shift->openapi->valid_inputorreturn;my$output = {greeting=>'Hello World' };$c->render(openapi=>$output);}1;
I decided to implement the tutorial in a scaffolded application, you could create your own controller, but changing an existing controller this way demonstrates how our newly added OpenAPI API end-point, can live in unison with existing and additional end-points.
Now start the application
$ morbo script/hello_world
And finally - lets call the API
$ http http://localhost:3000/api/hello_world
We should now get the result
HTTP/1.1 200 OKContent-Length: 26Content-Type: application/json;charset=UTF-8Date: Thu, 26 Jul 2018 08:20:59 GMTServer: Mojolicious (Perl){"greeting":"Hello World"}
Yay! and our firstMojoliciousOpenAPI implementation works!
In addition to the operation, you can obtain the specification by calling the following URL:/api
$ http http://localhost:3000/api/
And as mentioned earlier our existing operations and parts of the application still works as expected, try calling the URL:/
$ http http://localhost:3000/
That is it for now, good luck with experimenting withMojoliciousOpenAPI integration andOpenAPI. Thanks to Jan Henning Thorsen (@jhthorsen) for the implementation of Mojolicious::Plugin::OpenAPI.
Today I Learned/TIL
This wiki is primarily for my notes and stuff, please visitmy page if you want to get in touch