{"id":18910025,"url":"https://github.com/jonasbn/perl-mojolicious-plugin-openapi-tutorial-hello-world","last_synced_at":"2025-07-23T09:06:41.329Z","repository":{"id":43879573,"uuid":"142420620","full_name":"jonasbn/perl-mojolicious-plugin-openapi-tutorial-hello-world","owner":"jonasbn","description":"Tutorial for Mojolicious::Plugin::OpenAPI: Hello World","archived":false,"fork":false,"pushed_at":"2022-11-04T16:06:54.000Z","size":79,"stargazers_count":5,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-14T00:00:36.446Z","etag":null,"topics":["mojolicious","openapi","perl","tutorial"],"latest_commit_sha":null,"homepage":"https://dev.to/jonasbn/tutorial-mojoliciouspluginopenapi-3jgd","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonasbn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-26T09:40:14.000Z","updated_at":"2023-01-17T11:57:24.000Z","dependencies_parsed_at":"2023-01-21T00:45:10.241Z","dependency_job_id":null,"html_url":"https://github.com/jonasbn/perl-mojolicious-plugin-openapi-tutorial-hello-world","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jonasbn/perl-mojolicious-plugin-openapi-tutorial-hello-world","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-hello-world","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-hello-world/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-hello-world/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-hello-world/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonasbn","download_url":"https://codeload.github.com/jonasbn/perl-mojolicious-plugin-openapi-tutorial-hello-world/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-hello-world/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266649176,"owners_count":23962181,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mojolicious","openapi","perl","tutorial"],"created_at":"2024-11-08T09:38:59.838Z","updated_at":"2025-07-23T09:06:41.275Z","avatar_url":"https://github.com/jonasbn.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tutorial on Mojolicious::Plugin::OpenAPI: Hello World\n\n\u003c!-- markdownlint-disable MD014 --\u003e\n\nI have always wanted to get my hands _dirty_ with **Swagger**. I recently fell over [Mojolicious::Plugin::OpenAPI](https://metacpan.org/pod/Mojolicious::Plugin::OpenAPI), which fits into my [_boring stack_](https://hackernoon.com/the-boring-stack-the-best-way-to-build-interesting-things-9f54420f683e) and I decided to do a prototype.\n\nI followed the [tutorial](https://metacpan.org/pod/Mojolicious::Plugin::OpenAPI::Guides::Tutorial) for Mojolicious::Plugin::OpenAPI and found it a bit confusing, so I decided to write up a more simple tutorial.\n\nThis tutorial requires that you have [Mojolicious](https://metacpan.org/pod/Mojolicious) installed and recommends [carton](https://metacpan.org/pod/distribution/Carton/script/carton). The installation of these components is however beyond the scope of this tutorial.\n\n**OpenAPI** comes from **Swagger**, which I have had a look at, much water has run under that bridge, so now it is time to look at **OpenAPI** a specification on how to write RESTful APIs in a standardised format.\n\nHere goes, lets start with a basic `hello world` example, [all files are available on GitHub](https://github.com/jonasbn/perl-mojolicious-plugin-openapi-tutorial-hello-world).\n\n## Hello World\n\nFirst we set up an application, yes we could do a **Mojolicious** lite-app, but I primarily use **Mojolicious** apps, so I think it makes sense to keep stick to this for reference.\n\n```bash\n$ mojo generate app HelloWorld\n```\n\nJump into our newly generated application directory\n\n```bash\n$ cd hello_world\n```\n\nWe then install the plugin we need to enable **OpenAPI** in our **Mojolicious** application\n\nUsing **CPAN** shell:\n\n```bash\n$ perl -MCPAN -e shell install Mojolicious::Plugin::OpenAPI\n```\n\nUsing `cpanm`:\n\n```bash\n$ cpanm Mojolicious::Plugin::OpenAPI\n```\n\nIf you need help installing please refer to [the CPAN installation guide](https://www.cpan.org/modules/INSTALL.html).\n\nCreate a definition JSON file based on **OpenAPI** to support an Hello World implementation based on the **OpenAPI** specification:\n\n```bash\n$ touch openapi.conf\n```\n\nThe exact name of this file is insignifcant, I just prefer to have clear and understandable filenames for easy identification.\n\nOpen `openapi.conf` and insert the following _snippet_:\n\n```json\n{\n    \"swagger\": \"2.0\",\n    \"info\": { \"version\": \"1.0\", \"title\": \"Hello World example\" },\n    \"basePath\": \"/api\",\n    \"paths\": {\n      \"/hello_world\": {\n        \"get\": {\n          \"operationId\": \"helloWorld\",\n          \"x-mojo-name\": \"hello_world\",\n          \"x-mojo-to\": \"example#hello_world\",\n          \"summary\": \"Example app returning hello world\",\n          \"responses\": {\n            \"200\": {\n              \"description\": \"Returning string 'hello world'\",\n              \"schema\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"greeting\": {\n                        \"type\": \"string\"\n                    }\n                }\n              }\n            },\n            \"default\": {\n              \"description\": \"Unexpected error\",\n              \"schema\": {}\n            }\n          }\n        }\n      }\n    }\n}\n```\n\nNow lets go over our definiton.\n\n- `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.\n\n- `paths`: here we define our first API path, so our Hello World application can be accessed at: `/api/hello_world`\n\n- `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 the **Mojolicious** application\n\n- `x-mojo-name`: this is the name used to identify our operation in the **Mojolicious** application\n\n- `x-mojo-to`: this is the specification for the route to be used for our operation in the **Mojolicious** application, more on this later\n\n- `responses`: here we define the type we want to handle, for now we settle for `200`. The response definition outline our response, this could be boiled down to a `string` instead of an `object`, with properties, but the example would be come _too simple_ and in my opinion we work primarily with objects over basic types, so this extended example makes for a better reference.\n\nNext step is to enable the [MetaCPAN: Mojolicious::Plugin::OpenAPI](https://metacpan.org/pod/Mojolicious::Plugin::OpenAPI) plugin in the application\n\nOpen the file: `lib/HelloWorld.pm` and add the following snippet:\n\n```perl\n$self-\u003eplugin(\"OpenAPI\" =\u003e {url =\u003e $self-\u003ehome-\u003erel_file(\"openapi.json\")});\n```\n\nNote the pointer to our previously created file: `openapi.json`.\n\nThe complete file should look like the following:\n\n```perl\npackage HelloWorld;\nuse Mojo::Base 'Mojolicious';\n\n# This method will run once at server start\nsub startup {\n  my $self = shift;\n\n  $self-\u003eplugin('OpenAPI' =\u003e {url =\u003e $self-\u003ehome-\u003erel_file('openapi.json')});\n\n  # Load configuration from hash returned by \"my_app.conf\"\n  my $config = $self-\u003eplugin('Config');\n\n  # Documentation browser under \"/perldoc\"\n  $self-\u003eplugin('PODRenderer') if $config-\u003e{perldoc};\n\n  # Router\n  my $r = $self-\u003eroutes;\n\n  # Normal route to controller\n  $r-\u003eget('/')-\u003eto('example#welcome');\n}\n\n1;\n```\n\nThen we add the actual operation, open the file: `lib/HelloWorld/Controller/Example.pm` and add the following snippet:\n\n```perl\nsub hello_world {\n    my $c = shift-\u003eopenapi-\u003evalid_input or return;\n\n    my $output = { greeting =\u003e 'Hello World' };\n    $c-\u003erender(openapi =\u003e $output);\n}\n```\n\nNote that this maps to the definition in our API definition: `openapi.conf`\n\n```json\n\"x-mojo-to\": \"example#hello_world\",\n```\n\nThe complete file should look like the following:\n\n```perl\npackage HelloWorld::Controller::Example;\nuse Mojo::Base 'Mojolicious::Controller';\n\n# This action will render a template\nsub welcome {\n  my $self = shift;\n\n  # Render template \"example/welcome.html.ep\" with message\n  $self-\u003erender(msg =\u003e 'Welcome to the Mojolicious real-time web framework!');\n}\n\nsub hello_world {\n    my $c = shift-\u003eopenapi-\u003evalid_input or return;\n\n    my $output = { greeting =\u003e 'Hello World' };\n    $c-\u003erender(openapi =\u003e $output);\n}\n\n1;\n```\n\nI 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.\n\nNow start the application\n\n```bash\n$ morbo script/hello_world\n```\n\nAnd finally - lets call the API, do note you do not need `jq` and your could use `curl` or `httpie`, so this is just for sticking to the already available tools, `jq` being the exception.\n\n```bash\n$ mojo get --verbose http://localhost:3000/api/hello_world | jq\nGET /api/hello_world HTTP/1.1\nHost: localhost:3000\nUser-Agent: Mojolicious (Perl)\nContent-Length: 0\nAccept-Encoding: gzip\n\nHTTP/1.1 200 OK\nServer: Mojolicious (Perl)\nContent-Length: 26\nDate: Fri, 27 Jul 2018 08:47:33 GMT\nContent-Type: application/json;charset=UTF-8\n\n{\n  \"greeting\": \"Hello World\"\n}\n```\n\nYay! and our first **Mojolicious** **OpenAPI** implementation works!\n\nIn addition to the operation, you can obtain the specification by calling the following URL: `/api`\n\n```bash\n$ mojo get http://localhost:3000/api/\n```\n\nAnd as mentioned earlier our existing operations and parts of the application still works as expected, try calling the URL: `/`\n\n```bash\n$ mojo get http://localhost:3000/\n```\n\nThat is it for now, good luck with experimenting with **Mojolicious** **OpenAPI** integration and **OpenAPI**. Thanks to Jan Henning Thorsen ([@jhthorsen](https://twitter.com/jhthorsen)) for the implementation of Mojolicious::Plugin::OpenAPI.\n\n## References\n\n- [MetaCPAN: Mojolicious::Plugin::OpenAPI](https://metacpan.org/pod/Mojolicious::Plugin::OpenAPI)\n- [MetaCPAN: Mojolicious::Plugin::OpenAPI Tutorial](https://metacpan.org/pod/Mojolicious::Plugin::OpenAPI::Guides::Tutorial)\n- [OpenAPI Website](https://www.openapis.org/)\n- [GitHub repository for tutorial](https://github.com/jonasbn/perl-mojolicious-plugin-openapi-tutorial-hello-world)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-hello-world","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-hello-world","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-hello-world/lists"}