PAGI
view release on metacpan or search on metacpan
lib/PAGI/Cookbook.pod view on Meta::CPAN
my ($scope, $receive, $send) = @_;
my $ws = PAGI::WebSocket->new($scope, $receive, $send);
await $ws->accept;
await $ws->each_text(async sub {
my ($text) = @_;
await $ws->send_text("Echo: $text");
});
});
# WebSocket with path parameters
$router->websocket('/ws/chat/:room' => async sub {
my ($scope, $receive, $send) = @_;
my $ws = PAGI::WebSocket->new($scope, $receive, $send);
# Access room parameter
my $room = $scope->{path_params}{room};
await $ws->accept;
await $ws->send_text("Joined room: $room");
# ... chat logic ...
});
$router->to_app;
=head3 SSE Routes
Route Server-Sent Events by path:
use strict;
use warnings;
use Future::AsyncAwait;
use PAGI::App::Router;
use PAGI::SSE;
my $router = PAGI::App::Router->new;
# SSE stream
$router->sse('/events' => async sub {
my ($scope, $receive, $send) = @_;
my $sse = PAGI::SSE->new($scope, $receive, $send);
await $sse->keepalive(30); # Prevent proxy timeouts
await $sse->send_event("Connected at: " . time);
await $sse->run; # Wait for disconnect
});
# SSE with path parameters
$router->sse('/events/:channel' => async sub {
my ($scope, $receive, $send) = @_;
my $sse = PAGI::SSE->new($scope, $receive, $send);
# Access channel parameter
my $channel = $scope->{path_params}{channel};
await $sse->start;
await $sse->send_event("Subscribed to: $channel");
# ... streaming logic ...
});
$router->to_app;
=head2 Route-Level Middleware
Apply middleware to specific routes by passing an arrayref:
use strict;
use warnings;
use Future::AsyncAwait;
use PAGI::App::Router;
use PAGI::Request;
use PAGI::Response;
my $router = PAGI::App::Router->new;
# Define middleware
my $auth_mw = async sub {
my ($scope, $receive, $send, $next) = @_;
# Check authorization
my $token = '';
for my $h (@{$scope->{headers}}) {
if (lc($h->[0]) eq 'authorization') {
$token = $h->[1];
last;
}
}
unless ($token eq 'Bearer secret123') {
my $res = PAGI::Response->new($scope, $send);
await $res->status(401)->json({ error => 'Unauthorized' });
return; # Don't call $next->()
}
# Authorized - continue to handler
await $next->();
};
# Apply middleware to specific routes
$router->get('/admin' => [$auth_mw] => async sub {
my ($scope, $receive, $send) = @_;
my $res = PAGI::Response->new($scope, $send);
await $res->text('Admin panel');
});
# Multiple middleware
my $log_mw = async sub {
my ($scope, $receive, $send, $next) = @_;
warn "Request: $scope->{method} $scope->{path}\n";
await $next->();
};
$router->post('/admin/users' => [$log_mw, $auth_mw] => async sub {
my ($scope, $receive, $send) = @_;
# ... handler ...
});
$router->to_app;
lib/PAGI/Cookbook.pod view on Meta::CPAN
});
}
# WebSocket with path parameters
async sub handle_chat {
my ($self, $ws) = @_;
# Access path parameter via $ws->path_param()
my $room = $ws->path_param('room');
await $ws->accept;
await $ws->send_text("Joined room: $room");
# ... chat logic ...
}
1;
=head3 SSE Handlers
SSE handlers receive C<($self, $sse)>:
package MyApp;
use parent 'PAGI::Endpoint::Router';
use strict;
use warnings;
use Future::AsyncAwait;
sub routes {
my ($self, $r) = @_;
$r->get('/' => 'home');
$r->sse('/events' => 'handle_events');
$r->sse('/events/:channel' => 'handle_channel');
}
async sub home {
my ($self, $req, $res) = @_;
await $res->html('<h1>Home</h1>');
}
# SSE handler receives ($self, $sse)
async sub handle_events {
my ($self, $sse) = @_;
await $sse->keepalive(30);
await $sse->send_event("Connected at: " . time);
await $sse->run;
}
# SSE with path parameters
async sub handle_channel {
my ($self, $sse) = @_;
# Access path parameter
my $channel = $sse->scope->{'pagi.params'}{channel};
await $sse->start;
await $sse->send_event("Channel: $channel");
# ... streaming logic ...
}
1;
=head3 Lifecycle Hooks
Override C<on_startup> and C<on_shutdown> for application lifecycle management:
package MyApp;
use parent 'PAGI::Endpoint::Router';
use strict;
use warnings;
use Future::AsyncAwait;
# Called when application starts
async sub on_startup {
my ($self) = @_;
# Initialize resources
$self->state->{db} = DBI->connect('dbi:SQLite:dbname=app.db');
$self->state->{started_at} = time();
warn "Application started\n";
}
# Called when application shuts down
async sub on_shutdown {
my ($self) = @_;
# Clean up resources
$self->state->{db}->disconnect if $self->state->{db};
warn "Application shut down\n";
}
sub routes {
my ($self, $r) = @_;
$r->get('/' => 'home');
}
async sub home {
my ($self, $req, $res) = @_;
# Access state initialized in on_startup
my $db = $self->state->{db};
my $uptime = time() - $self->state->{started_at};
await $res->json({
uptime => $uptime,
database => defined($db) ? 'connected' : 'disconnected',
});
}
1;
Note: C<$self-E<gt>state> is per-worker in multi-worker mode. For shared state, use an external store (Redis, database, etc.).
=head3 Route-Level Middleware with Method Names
( run in 0.582 second using v1.01-cache-2.11-cpan-140bd7fdf52 )