{"id":18910020,"url":"https://github.com/jonasbn/perl-mojolicious-plugin-openapi-tutorial-parameters","last_synced_at":"2026-05-02T10:40:36.036Z","repository":{"id":136289443,"uuid":"142474187","full_name":"jonasbn/perl-mojolicious-plugin-openapi-tutorial-parameters","owner":"jonasbn","description":"Yet another tutorial for Mojolicious::Plugin::OpenAPI: Parameters","archived":false,"fork":false,"pushed_at":"2023-06-19T15:14:41.000Z","size":19,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-15T05:27:21.157Z","etag":null,"topics":["mojolicious","openapi","perl","tutorial"],"latest_commit_sha":null,"homepage":"","language":"Perl","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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-26T17:41:54.000Z","updated_at":"2023-06-19T15:14:43.000Z","dependencies_parsed_at":"2024-12-31T12:49:09.479Z","dependency_job_id":"3b20f4e5-a357-415d-8294-c2c248d190b0","html_url":"https://github.com/jonasbn/perl-mojolicious-plugin-openapi-tutorial-parameters","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-parameters","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-parameters/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-parameters/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-parameters/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-parameters/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239898462,"owners_count":19715212,"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","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:58.498Z","updated_at":"2026-03-07T13:30:18.762Z","avatar_url":"https://github.com/jonasbn.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tutorial on Mojolicious::Plugin::OpenAPI: Parameters\n\n\u003c!-- markdownlint-disable MD014 --\u003e\n\nThis is a follow up to [my tutorial](https://github.com/jonasbn/perl-mojolicious-plugin-openapi-tutorial-hello-world/blob/master/README.md) on Mojolicious::Plugin::OpenAPI, demonstrating the basic implementation of a Hello World example.\n\nThis tutorial with extend on the topic and will touch on parameter use.\n\n## The Application\n\nThe application implements and demonstrates two approaches to parameter handling:\n\n1. Query parameter based, like: `/api/user?id=alice`\n1. URL parameter based, like: `/api/user/bob`\n\nFirst lets generate our application:\n\n```bash\n$ mojo generate app Parameters\n```\n\nJump into our newly generated application directory\n\n```bash\n$ cd parameters\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\nOpen `openapi.conf` and insert the following _snippet_:\n\n```json\n{\n    \"swagger\": \"2.0\",\n    \"info\": { \"version\": \"1.0\", \"title\": \"Demo of API with parameters\" },\n    \"basePath\": \"/api\",\n    \"paths\": {\n      \"/users\": {\n        \"get\": {\n          \"operationId\": \"getUsers\",\n          \"x-mojo-name\": \"get_users\",\n          \"x-mojo-to\": \"user#list\",\n          \"summary\": \"Lists users\",\n          \"responses\": {\n            \"200\": {\n              \"description\": \"Users response\",\n              \"schema\": {\n                \"type\": \"object\",\n                \"properties\": {\n                  \"users\": {\n                    \"type\": \"array\",\n                    \"items\": { \"type\": \"object\" }\n                  }\n                }\n              }\n            }\n          }\n        }\n      },\n      \"/user/#id\": {\n        \"get\": {\n          \"operationId\": \"getUserByUrl\",\n          \"x-mojo-name\": \"get_user_by_url\",\n          \"x-mojo-to\": \"user#get_by_url\",\n          \"summary\": \"User response\",\n          \"responses\": {\n            \"200\": {\n              \"description\": \"User response\",\n              \"schema\": {\n                \"type\": \"object\",\n                \"properties\": {\n                  \"user\": {\n                    \"type\": \"object\"\n                  }\n                }\n              }\n            },\n            \"default\": {\n              \"description\": \"Unexpected error\",\n              \"schema\": {}\n            }\n          }\n        }\n      },\n      \"/user\": {\n            \"get\": {\n              \"operationId\": \"getUserByParameter\",\n              \"x-mojo-name\": \"get_user_by_parameter\",\n              \"x-mojo-to\": \"user#get_by_parameter\",\n              \"summary\": \"User response\",\n              \"parameters\": [\n                {\"in\": \"query\", \"name\": \"id\", \"type\": \"string\"}\n              ],\n              \"responses\": {\n                \"200\": {\n                  \"description\": \"User response\",\n                  \"schema\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                      \"user\": {\n                        \"type\": \"object\"\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. We will not cover the basics as such, please refer to the Hello World tutorial, instead we will focus on the aspects related to parameters.\n\nWe have introduced 3 end-points, to simulate a more complete RESTful API.\n\nWe have defined the following paths:\n\n- `/api/users`, returning an array of users. This is basically a variation of the Hello World example, which just returned an object where this end-point return an array of objects.\n\n- `/api/user?id=«id»`, returning a user object if one can be found, this is based on a query parameter. Mojolicious::Plugin::OpenAPI validates this parameter based on the defintion in our `openapi.json`\n\n- `/api/user/«id»`, returning a user object if one can be found, this is based on the URL like a proper RESTful interface, Mojolicious::Plugin::OpenAPI does **NOT** validate but the URL is somewhat validated based on the defintion in our `openapi.json`\n\nCreate a new file: `User.pm` in `lib/Parameters/Controller/`\n\nFirst we add the method for handling `/api/users`:\n\n```perl\npackage Parameters::Controller::User;\nuse Mojo::Base 'Mojolicious::Controller';\n\nmy %users = (\n    'bob'  =\u003e { name =\u003e 'Bob', email =\u003e 'bob@eksempel.dk' },\n    'alice'=\u003e { name =\u003e 'Alice', email =\u003e 'alice@eksempel.dk' },\n);\n\nsub list {\n\n    # Do not continue on invalid input and render a default 400\n    # error document.\n    my $c = shift-\u003eopenapi-\u003evalid_input or return;\n\n    my @user_list = ();\n\n    foreach my $user_id (keys %users) {\n        my $user = $users{$user_id};\n        $user-\u003e{userid} = $user_id;\n        push @user_list, $user;\n    }\n\n    # $output will be validated by the OpenAPI spec before rendered\n    my $output = { users =\u003e \\@user_list };\n    $c-\u003erender(openapi =\u003e $output);\n}\n```\n\nWe are simulating a model in our application, so the data are just placed in a internal data structure named: `%users`, this could of course be any sort of model, a database, filesystem or another service.\n\nNow start the application:\n\n```bash\n$ morbo script/parameters\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 http://localhost:3000/api/users | jq\n```\n\nA we get a complete list of our users, currently consisting of Bob and Alice:\n\n```json\n{\n  \"users\": [\n    {\n      \"email\": \"bob@eksempel.dk\",\n      \"name\": \"Bob\",\n      \"userid\": \"bob\"\n    },\n    {\n      \"email\": \"alice@eksempel.dk\",\n      \"name\": \"Alice\",\n      \"userid\": \"alice\"\n    }\n  ]\n}\n```\n\nNow lets add the method for handling a single user object via a query parameter:\n\n```perl\nsub get_by_parameter {\n\n    # Do not continue on invalid input and render a default 400\n    # error document.\n    my $c = shift-\u003eopenapi-\u003evalid_input or return;\n\n    # $c-\u003eopenapi-\u003evalid_input copies valid data to validation object,\n    # and the normal Mojolicious api works as well.\n    my $input = $c-\u003evalidation-\u003eoutput;\n\n    my $id = $input-\u003e{'id'};\n\n    my $user = $users{$id};\n\n    if ($user) {\n        $user-\u003e{userid} = $id;\n        # $output will be validated by the OpenAPI spec before rendered\n        my $output = { user =\u003e $user };\n        $c-\u003erender(openapi =\u003e $output);\n    } else {\n        $c-\u003erespond_to(\n            any =\u003e { status =\u003e 404, json =\u003e { message =\u003e 'Not found' }}\n        );\n    }\n}\n```\n\nNow lets call the API:\n\n```bash\n$ mojo get http://localhost:3000/api/user?id=alice | jq\n```\n\nAnd the result for Alice:\n\n```json\n{\n  \"user\": {\n    \"email\": \"alice@eksempel.dk\",\n    \"name\": \"Alice\",\n    \"userid\": \"alice\"\n  }\n}\n```\n\nAnd finally lets add the method for handling a single user object via the URL:\n\n```perl\nsub get_by_url {\n\n    # Do not continue on invalid input and render a default 400\n    # error document.\n    my $c = shift-\u003eopenapi-\u003evalid_input or return;\n\n    my $id = $c-\u003estash('id');\n\n    my $user = $users{$id};\n\n    if ($user) {\n        $user-\u003e{userid} = $id;\n        # $output will be validated by the OpenAPI spec before rendered\n        my $output = { user =\u003e $user };\n        $c-\u003erender(openapi =\u003e $output);\n    } else {\n        $c-\u003erespond_to(\n            any =\u003e { status =\u003e 404, json =\u003e { message =\u003e 'Not found' }}\n        );\n    }\n}\n```\n\nLets call the API again using the URL:\n\n```bash\n$ mojo get http://localhost:3000/api/user/bob | jq\n```\n\nAnd we get the result for Bob:\n\n```json\n{\n  \"user\": {\n    \"email\": \"bob@eksempel.dk\",\n    \"name\": \"Bob\",\n    \"userid\": \"bob\"\n  }\n}\n```\n\nYay! our **Mojolicious** **OpenAPI** implementation works and we can even support different URL schemas.\n\nSince we take parameters, we need to do one last thing. And that is sanitizing our input. This part is not essential for get going with **Mojolicious** **OpenAPI** integration and **Mojolicious::Plugin::OpenAPI**.\n\nThis next part is not required for understanding handling parameters, but if you want get into validation, which is quite essential please read along.\n\nLets add validation to our two end-points processing data. The Mojolicious::Plugin::OpenAPI already has a hook for validation, but we need to extend this.\n\nLets add a method to our `Parameters::Controller::User`. Our `id` no matter how we receive it has to adhere to the same validation so we add this basic validation method, which overwrites the existing validation for our controller.\n\n```perl\nsub _validate_id {\n    my ($c, $id) = @_;\n\n    my $validator = $c-\u003evalidation-\u003evalidator;\n    my $validation = $validator-\u003evalidation;\n    $validation-\u003einput({id =\u003e $id});\n    $validation-\u003erequired('id')-\u003elike(qr/^[A-Z]/i);\n    $c-\u003evalidation-\u003evalidator($validation);\n\n    return $c;\n}\n```\n\nAs you can see our two end-points fetching users are pretty similar, the only difference is how the `id` parameter is received, so lets generalise this with the following method:\n\n```perl\nsub _proces_request {\n    my ($c, $id) = @_;\n\n    $c-\u003e_validate_id($id);\n\n    if (not $c-\u003evalidation-\u003evalidator-\u003ehas_error) {\n\n        my $input = $c-\u003evalidation-\u003evalidator-\u003eoutput;\n\n        my $id = $input-\u003e{'id'};\n        my $user = $users{$id};\n\n        if ($user) {\n            $user-\u003e{userid} = $id;\n            # $output will be validated by the OpenAPI spec before rendered\n            my $output = { user =\u003e $user };\n            $c-\u003erender(openapi =\u003e $output);\n        } else {\n            $c-\u003erespond_to(\n                any =\u003e { status =\u003e 404, json =\u003e { message =\u003e 'Not found' }}\n            );\n        }\n    } else {\n        $c-\u003erespond_to(\n            any =\u003e { status =\u003e 400, json =\u003e { message =\u003e 'Bad request' }}\n        );\n    }\n\n    return $c;\n}\n```\n\nWe let this method call our validation method, so all we need to do is let the two end-points extract the `id` parameter and call the `_proces_request` method:\n\nFirst: `get_by_parameter`:\n\n```perl\nsub get_by_parameter {\n\n    # Do not continue on invalid input and render a default 400\n    # error document.\n    my $c = shift-\u003eopenapi-\u003evalid_input or return;\n\n    # $c-\u003eopenapi-\u003evalid_input copies valid data to validation object,\n    # and the normal Mojolicious api works as well.\n\n    $c-\u003e_proces_request($c-\u003eparam('id'));\n}\n```\n\nSecondly: `get_by_parameter`:\n\n```perl\nsub get_by_url {\n\n    # Do not continue on invalid input and render a default 400\n    # error document.\n    my $c = shift-\u003eopenapi-\u003evalid_input or return;\n\n    # $c-\u003eopenapi-\u003evalid_input copies valid data to validation object,\n    # and the normal Mojolicious api works as well.\n\n    $c-\u003e_proces_request($c-\u003estash('id'));\n}\n```\n\nAnd we should be good to go. The complete controller component should look like the following:\n\n```perl\npackage Parameters::Controller::User;\nuse Mojo::Base 'Mojolicious::Controller';\n\nmy %users = (\n    'bob'  =\u003e { name =\u003e 'Bob', email =\u003e 'bob@eksempel.dk' },\n    'alice'=\u003e { name =\u003e 'Alice', email =\u003e 'alice@eksempel.dk' },\n);\n\nsub list {\n\n    # Do not continue on invalid input and render a default 400\n    # error document.\n    my $c = shift-\u003eopenapi-\u003evalid_input or return;\n\n    my @user_list = ();\n\n    foreach my $user_id (keys %users) {\n        my $user = $users{$user_id};\n        $user-\u003e{userid} = $user_id;\n        push @user_list, $user;\n    }\n\n    # $output will be validated by the OpenAPI spec before rendered\n    my $output = { users =\u003e \\@user_list };\n    $c-\u003erender(openapi =\u003e $output);\n}\n\nsub get_by_parameter {\n\n    # Do not continue on invalid input and render a default 400\n    # error document.\n    my $c = shift-\u003eopenapi-\u003evalid_input or return;\n\n    # $c-\u003eopenapi-\u003evalid_input copies valid data to validation object,\n    # and the normal Mojolicious api works as well.\n\n    $c-\u003e_proces_request($c-\u003eparam('id'));\n}\n\nsub get_by_url {\n\n    # Do not continue on invalid input and render a default 400\n    # error document.\n    my $c = shift-\u003eopenapi-\u003evalid_input or return;\n\n    # $c-\u003eopenapi-\u003evalid_input copies valid data to validation object,\n    # and the normal Mojolicious api works as well.\n\n    $c-\u003e_proces_request($c-\u003estash('id'));\n}\n\nsub _proces_request {\n    my ($c, $id) = @_;\n\n    $c-\u003e_validate_id($id);\n\n    if (not $c-\u003evalidation-\u003evalidator-\u003ehas_error) {\n\n        my $input = $c-\u003evalidation-\u003evalidator-\u003eoutput;\n\n        my $id = $input-\u003e{'id'};\n        my $user = $users{$id};\n\n        if ($user) {\n            $user-\u003e{userid} = $id;\n            # $output will be validated by the OpenAPI spec before rendered\n            my $output = { user =\u003e $user };\n            $c-\u003erender(openapi =\u003e $output);\n        } else {\n            $c-\u003erespond_to(\n                any =\u003e { status =\u003e 404, json =\u003e { message =\u003e 'Not found' }}\n            );\n        }\n    } else {\n        $c-\u003erespond_to(\n            any =\u003e { status =\u003e 400, json =\u003e { message =\u003e 'Bad request' }}\n        );\n    }\n\n    return $c;\n}\n\nsub _validate_id {\n    my ($c, $id) = @_;\n\n    my $validator = $c-\u003evalidation-\u003evalidator;\n    my $validation = $validator-\u003evalidation;\n    $validation-\u003einput({id =\u003e $id});\n    $validation-\u003erequired('id')-\u003elike(qr/^[A-Z]/i);\n    $c-\u003evalidation-\u003evalidator($validation);\n\n    return $c;\n}\n\n1;\n```\n\nTo checkout our new validations try different variations. As we implemented we only allow lettes for user-id and hence we regard the request as a _bad request_\n\n\n```bash\n$ mojo get --verbose http://localhost:3000/api/user/123 | jq\nGET /api/user/123 HTTP/1.1\nHost: localhost:3000\nAccept-Encoding: gzip\nUser-Agent: Mojolicious (Perl)\nContent-Length: 0\n\nHTTP/1.1 400 Bad Request\nDate: Fri, 27 Jul 2018 08:25:40 GMT\nContent-Type: application/json;charset=UTF-8\nServer: Mojolicious (Perl)\nContent-Length: 25\n\n{\n  \"message\": \"Bad request\"\n}\n```\n\nAnd of the other end-point also a _bad request_:\n\n```bash\n$ mojo get --verbose http://localhost:3000/api/user?id=123 | jq\nGET /api/user?id=123 HTTP/1.1\nHost: localhost:3000\nUser-Agent: Mojolicious (Perl)\nAccept-Encoding: gzip\nContent-Length: 0\n\nHTTP/1.1 400 Bad Request\nDate: Fri, 27 Jul 2018 08:27:24 GMT\nServer: Mojolicious (Perl)\nContent-Type: application/json;charset=UTF-8\nContent-Length: 25\n\n{\n  \"message\": \"Bad request\"\n}\n```\n\nThat concludes this part. Have fun experimenting with **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-parameters)\n- [Mojolicious.org: Mojolicious::Validator](https://mojolicious.org/perldoc/Mojolicious/Validator)\n- [Mojolicious.org: Mojolicious::Validator::Validation](https://mojolicious.org/perldoc/Mojolicious/Validator/Validation)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-parameters","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-parameters","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonasbn%2Fperl-mojolicious-plugin-openapi-tutorial-parameters/lists"}