FastAPI-inspired asynchronous micro-framework for Perl built on the PAGI protocol with Type::Tiny validation and automatic OpenAPI 3.1 / Swagger UI documentation.
use v5.36;
use PAGI::FastAPI;
use PAGI::FastAPI::Depends qw(Depends);
use Types::Standard qw(Int Str);
use Future::AsyncAwait;
my $app = PAGI::FastAPI->new(
title => 'Store Microservice',
version => '1.0.0',
);
# 1. Add CORS Support
$app->add_cors(
allow_origins => ['https://example.com'],
allow_methods => ['GET', 'POST'],
);
# 2. Add Authentication Middleware Hook
# (hand-rolled here for illustration only, for ready-made schemes,
# including proper 401 challenges, see PAGI::FastAPI::Security)
$app->add_middleware(async sub ($c, $next) {
my $auth = $c->header('Authorization') // '';
if ($auth ne 'Bearer secret_token') {
$c->status(401);
return { detail => 'Unauthorized' };
}
$c->stash->{user_id} = 42;
return await $next->($c);
});
# 3. Register Lifespan Handlers
$app->on_startup(async sub {
warn "Connecting to database connection pool...\n";
});
$app->on_shutdown(async sub {
warn "Closing database connections...\n";
});
# 4. Declare Async Dependencies
my $get_db = async sub ($c) {
return { db_name => 'production_db' };
};
my $get_current_user = async sub ($c) {
my $token = $c->header('Authorization') // '';
unless ($token eq 'Bearer secret_token') {
$c->status(401);
return { detail => 'Invalid credentials' };
}
return { user_id => 42, role => 'admin' };
};
# 5. Route using HashRef Dependency Map
$app->get('/profile',
dependencies => {
db => $get_db,
user => $get_current_user,
},
handler => async sub ($c) {
my $db = $c->stash->{db};
my $user = $c->stash->{user};
return { user => $user, db => $db->{db_name} };
}
);
# 6. Route using Depends() Array Spec
$app->get('/admin',
dependencies => [
Depends($get_current_user, key => 'user'),
async sub ($c) {
if ($c->stash->{user}{role} ne 'admin') {
$c->status(403);
return { detail => 'Admin privileges required' };
}
}
],
handler => async sub ($c) {
return { message => 'Welcome to admin panel' };
}
);
# 7. Non-blocking GET route with path parameter & query validation
$app->get('/items/{id}',
query => { limit => Int },
handler => async sub ($c) {
return {
item_id => $c->param('id'),
limit => $c->param('limit'),
status => 'active',
};
}
);
# 8. Non-blocking POST route with JSON payload validation
$app->post('/items',
body => { name => Str, price => Int },
handler => async sub ($c) {
return {
created => 1,
name => $c->body('name'),
price => $c->body('price'),
};
}
);
my $pagi_app = $app->to_app;
# 9. Authentication via the companion PAGI::FastAPI::Security distribution
# (extraction only, you supply the verification logic)
#
# use PAGI::FastAPI::Security::HTTPBearer;
# my $bearer = PAGI::FastAPI::Security::HTTPBearer->new;
# $app->get('/secure',
# dependencies => [ $bearer->depends(key => 'token') ],
# handler => async sub ($c) {
# return { token => $c->stash->{token} };
# }
# );
#
# See PAGI::FastAPI::Security for HTTP Basic, API Key
# (header/query/cookie), and OAuth2 password-bearer schemes.
PAGI::FastAPI has no authentication built in by design, auth needs vary too
much between applications to standardise.
Instead you get two general-purpose building blocks:
- Middleware (
add_middleware), runs for every request. - Dependencies (the
dependenciesroute option), runs per-route.
A dependency signals failure by calling $c->status($code) with a code >= 400 and returning a body HashRef (not by dieing, dependency execution isn't wrapped in eval).
For ready-made schemes instead of hand-rolling this every time, see the companion distribution PAGI::FastAPI::Security:
PAGI::FastAPI::Security::HTTPBearer:Authorization: Bearer <token>, with a401+WWW-Authenticate: Bearerchallenge.PAGI::FastAPI::Security::HTTPBasic:Authorization: Basic <base64>, with a401+WWW-Authenticate: Basic realm="..."challenge.PAGI::FastAPI::Security::APIKey: header, query string, or cookie, with a403on failure.PAGI::FastAPI::Security::OAuth2::PasswordBearer: OAuth2 bearer-token extraction plustoken_url/scopesmetadata.
Each scheme only extracts the credential, verification (JWT signature checking, password hashing, database/cache lookups) is left to you, so you aren't locked into one library. All schemes accept auto_error => 0 for optional-auth routes. See PAGI::FastAPI::Security's own docs for a full JWT-verification example.
Copyright (C) 2026 Mohammad Sajid Anwar.
This program is free software; you can redistribute it and/or modify it under the terms of the Artistic License (2.0). You may obtain a copy of the full license at: