OpenAPI
api mounts an OpenAPI 3.1 document as a first-class citizen: each
operation dispatches to the controller method named after its
operationId, request validation runs before your code does, and the
document's security schemes become ordinary Punk guards - all resolved
at boot.
my $api = api 'openapi.json';
An operation like
{
"paths": {
"/pets/{petId}": {
"get": {
"operationId": "getPet",
"tags": ["pets"],
"parameters": [
{ "name": "petId", "in": "path", "required": true,
"schema": { "type": "integer" } }
]
}
}
}
}
dispatches to MyApp::Controller::Pets::getPet (controllers resolve per
tag), with petId already validated and coerced to an integer:
sub getPet {
my ($c) = @_;
my $id = $c->param('petId'); # validated params win the lookup
...
}
$c->openapi holds the whole validated parameter set - path, query,
header and body - when you want it all at once.
Security as guards
my $api = api 'openapi.json' => {
security => {
key => sub {
my ($c, $value) = @_;
return $c->json({ error => 'nope' }, 401)
unless valid_key($value);
return;
},
},
};
Each security scheme in the document maps to a checker; a reference
return short-circuits exactly like an under guard. Basic and Bearer
credentials are extracted for you.
Scopes
Under a scope, an API mount inherits the scope's prefix and guards:
my $v1 = under '/v1' => $guard;
my $api = $v1->api('openapi.json');
The reference UI
docs '/api-docs';
Serves the generated documentation UI
(Open::API::UI) for a mounted
spec. With one api mount the mount is implied; name it when several
are mounted. A docs path the spec already declares croaks at boot.
Starting from the spec
punk new MyApp --api ./openapi.json
generates the application with the spec mounted and a controller of operation stubs per tag. When the spec grows,
punk api sync
writes stubs for the operations you have not implemented yet.
Under the hood
Validation runs on Open::API's C ABI - schema checks compiled at boot, no per-request schema walk. A mounted operation serves at about 176,000 requests a second with validation on.