Introducing PAGI: Async Web Development for Perl
TL;DR: PAGI (Perl Asynchronous Gateway Interface) is a new specification for async Perl web applications, inspired by Python's ASGI. It supports HTTP, WebSockets, and Server-Sent Events natively, and can wrap existing PSGI applications for backward compatibility.
The Problem
Modern web applications need more than traditional request-response cycles. Real-time features like live notifications, collaborative editing, and streaming data require persistent connections. This means:
- WebSockets for bidirectional communication
- Server-Sent Events for efficient server push
- Streaming responses for large payloads
- Connection lifecycle management for resource pooling
PSGI, Perl's venerable web server interface, assumes a synchronous world. While frameworks like Mojolicious have built async capabilities on top, there's no shared standard that allows different async frameworks and servers to interoperate.
PAGI aims to fill that gap.
What is PAGI?
PAGI defines a standard interface between async-capable Perl web servers and applications. If you're familiar with Python's ecosystem, think of it as Perl's answer to ASGI.
A PAGI application is an async coderef with three parameters:
use Future::AsyncAwait;
use experimental 'signatures';
async sub app ($scope, $receive, $send) {
# $scope - connection metadata (type, headers, path, etc.)
# $receive - async coderef to get events from the client
# $send - async coderef to send events to the client
}
The $scope->{type} tells you what kind of connection you're handling:
| Type | Description |
|---|---|
http |
Standard HTTP request/response |
websocket |
Persistent WebSocket connection |
sse |
Server-Sent Events stream |
lifespan |
Application startup/shutdown lifecycle |
A Simple HTTP Example
Here's "Hello World" in raw PAGI:
use Future::AsyncAwait;
async sub app ($scope, $receive, $send) {
die "Unsupported: $scope->{type}" if $scope->{type} ne 'http';
await $send->({
type => 'http.response.start',
status => 200,
headers => [['content-type', 'text/plain']],
});
await $send->({
type => 'http.response.body',
body => 'Hello from PAGI!',
more => 0,
});
}
Run it:
pagi-server --app app.pl --port 5000
curl http://localhost:5000/
# => Hello from PAGI!
The response is split into http.response.start (headers) and http.response.body (content). This separation enables streaming—send multiple body chunks with more => 1 before the final more => 0.
WebSocket Support
WebSockets are first-class citizens in PAGI:
async sub app ($scope, $receive, $send) {
if ($scope->{type} eq 'websocket') {
await $send->({ type => 'websocket.accept' });
while (1) {
my $event = await $receive->();
if ($event->{type} eq 'websocket.receive') {
my $msg = $event->{text} // $event->{bytes};
await $send->({
type => 'websocket.send',
text => "Echo: $msg",
});
}
elsif ($event->{type} eq 'websocket.disconnect') {
last;
}
}
}
else {
die "Unsupported: $scope->{type}";
}
}
The event loop pattern is consistent across all connection types: await events from $receive, send responses via $send.
PSGI Compatibility
One of PAGI's key features is backward compatibility with PSGI. The PAGI::App::WrapPSGI adapter lets you run existing PSGI applications on a PAGI server:
use PAGI::App::WrapPSGI;
# Your existing Catalyst/Dancer/Plack app
my $psgi_app = MyApp->psgi_app;
my $wrapper = PAGI::App::WrapPSGI->new(psgi_app => $psgi_app);
$wrapper->to_app;
The wrapper handles all the translation: building %env from PAGI scope, collecting request bodies, and converting responses back to PAGI events.
This means you can:
- Run legacy applications on a PAGI server
- Add WebSocket endpoints alongside existing routes
- Migrate incrementally from PSGI to PAGI
- Share connection pools between old and new code
PAGI::Simple Micro-Framework
For rapid development, PAGI ships with a micro-framework inspired by Express.js:
use PAGI::Simple;
my $app = PAGI::Simple->new(name => 'My API');
$app->get('/' => sub ($c) {
$c->text('Hello, World!');
});
$app->get('/users/:id' => sub ($c) {
my $id = $c->path_params->{id};
$c->json({ user_id => $id });
});
$app->post('/api/data' => sub ($c) {
my $data = $c->json_body;
$c->json({ received => $data, status => 'ok' });
});
$app->to_app;
WebSockets are equally clean:
$app->websocket('/chat' => sub ($ws) {
$ws->on(message => sub ($data) {
$ws->broadcast("Someone said: $data");
});
});
PAGI::Simple includes:
- Express-style routing with path parameters
- JSON request/response helpers
- Session management
- Middleware support (CORS, logging, rate limiting, etc.)
- Static file serving
- WebSocket rooms and broadcasting
- SSE channels with pub/sub
Current Status
PAGI is currently in early beta. The test suite passes, the examples work, but it hasn't been battle-tested in production.
What exists today:
- Complete PAGI specification
- Reference server implementation (
PAGI::Server) - PAGI::Simple micro-framework
- 13 example applications
- PSGI compatibility layer
- 483 passing tests
What it needs:
- Developers willing to experiment and provide feedback
- Real-world testing
- Framework authors interested in building on PAGI
- Performance profiling and optimization
Getting Started
git clone https://github.com/jjn1056/pagi.git
cd pagi
cpanm --installdeps .
prove -l t/
# Try the examples
pagi-server --app examples/01-hello-http/app.pl --port 5000
pagi-server --app examples/simple-01-hello/app.pl --port 5000
Why This Matters
Perl has excellent async primitives (IO::Async, Future::AsyncAwait), but no shared specification for async web applications. Each framework implements its own approach, which limits interoperability.
PAGI provides that shared foundation. By standardizing on a common interface:
- Servers can focus on performance and protocol handling
- Frameworks can focus on developer experience
- Middleware becomes portable across implementations
- The ecosystem can grow together rather than in isolation
If you're interested in the future of async Perl web development, I'd love your feedback. Check out the repository, try the examples, and let me know what you think.
Repository: github.com/jjn1056/pagi
PAGI is not yet on CPAN. It's experimental software—please don't use it in production unless you really know what you're doing.
Top comments (0)