NAME

PAGI::FastAPI::Context - Request and Response Lifecycle Context for PAGI::FastAPI

VERSION

Version v1.7.2

SYNOPSIS

# Inspecting Request Parameters
my $user_id = $c->path_param('id');
my $limit   = $c->query_param('limit');
my $name    = $c->body('name');

# Generic Parameter Fallback (Path -> Query -> Body)
my $token   = $c->param('token');

# Headers & Scope
my $ua      = $c->header('User-Agent');
my $scope   = $c->scope;

# Stash Storage
$c->stash->{user} = { id => 42, role => 'admin' };

# Modifying Response State
$c->status(201);
$c->set_header('X-Custom-Header' => 'value');

DESCRIPTION

PAGI::FastAPI::Context encapsulates the request environment, parsed parameters, payload data, and response state for an individual HTTP exchange processed by PAGI::FastAPI.

An instance of this context is passed as the primary argument to route handlers, middleware functions, and dependency blocks.

METHODS

new(%args)

Constructor called internally by PAGI::FastAPI. Accepts named arguments:

  • scope - PAGI environment HashRef.

  • query_params - HashRef of validated query parameters.

  • path_params - HashRef of route path variables.

  • body - Decoded payload body (HashRef, ArrayRef, or Scalar).

  • form_data - HashRef of parsed form fields, for a request whose body was application/x-www-form-urlencoded or multipart/form-data. undef for a JSON body.

  • uploaded_files - HashRef of uploaded file parts from a multipart/form-data body, keyed by field name.

  • status - Initial HTTP response code (default: 200).

  • res_headers - Initial response headers ArrayRef (default: []).

  • stash - Context-bound storage HashRef (default: {}).

scope()

Returns the raw PAGI scope HashRef for the current request.

sleep($seconds)

await $c->sleep(1);

Asynchronously pauses execution for the given number of seconds without blocking the event loop.

Uses Future::IO under the hood to ensure non-blocking sleep operations.

  • $seconds

    Number of seconds to sleep (fractional seconds like 0.5 are supported).

Returns a Future that completes when the specified sleep duration has elapsed.

status([ $code ])

Gets or sets the HTTP status code for the response.

$c->status(403);
my $code = $c->status; # 403

pagi_context

my $pagi_ctx = $c->pagi_context;

Returns the underlying low-level PAGI::Context instance associated with the current HTTP request. Useful for low-level protocol inspection, raw environment access, or invoking protocol-specific extension methods.

csrf_token

my $token = $c->csrf_token;

Retrieves the active Anti-CSRF token for the current request.

This method transparently attempts to resolve the token from three potential locations in order of precedence:

1. Direct Scope Environment: $scope->{'pagi.csrf_token'} (set directly by PAGI::Middleware::CSRF).
2. PAGI Context Environment: $pagi_context->env->{'pagi.csrf_token'}.
3. Session Storage Fallback: $scope->{'pagi.session'}{'csrf_token'} (set by session management middleware).

Returns the scalar token string if found, or undef if no token is available or if CSRF/Session middleware is not active for the request.

Example Usage (Embedding in HTML forms):

$app->get('/form', handler => async sub ($c) {
    my $token = $c->csrf_token // '';
    return $c->html(qq{
        <form method="POST" action="/submit">
            <input type="hidden" name="csrf_token" value="$token">
            <button type="submit">Submit</button>
        </form>
    });
});

csrf_verify($token)

my $is_valid = $c->csrf_verify($submitted_token);

Explicitly validates the given $token against the current request's CSRF state by delegating to the underlying low-level PAGI::Context instance.

Accepts a scalar token string $token. Returns a true value if the token signature and expiration are valid; returns false otherwise.

Dies with "PAGI context is not set" if invoked when no low-level PAGI::Context instance is associated with $c.

Example Usage (Manual Verification):

$app->post('/api/action', handler => async sub ($c) {
    my $token = $c->body('csrf_token');

    unless ($c->csrf_verify($token)) {
        $c->status(403);
        return { error => 'Invalid or missing CSRF token' };
    }

    return { status => 'success' };
});

res_headers()

Returns the current list of outgoing response header pairs as an ArrayRef of tuple pairs [ [$name, $val], ... ].

set_header($key, $val)

Sets an outgoing HTTP header, case-insensitively replacing any existing header of the same name (last write wins). Use this for headers that should only ever have one value.

$c->set_header('X-Frame-Options' => 'DENY');
$c->set_header('Content-Type'    => 'text/plain'); # replaces any prior Content-Type

add_header($key, $val)

Appends an additional outgoing HTTP header pair without touching any existing header of the same name. Use this only for headers that are legitimately allowed to appear more than once (e.g. Set-Cookie, Vary, Link); for everything else prefer set_header.

$c->add_header('Set-Cookie' => 'session=abc123; Path=/');
$c->add_header('Set-Cookie' => 'theme=dark; Path=/');

header($name)

Case-insensitively searches incoming request headers (from $c->scope->{headers}) and returns its scalar value, or undef if missing.

my $auth = $c->header('Authorization');

file($filepath, %options)

$app->get('/assets/app.js', handler => async sub ($c) {
    return $c->file('/var/www/static/app.js');
});

$app->get('/reports/annual.pdf', handler => async sub ($c) {
    return $c->file(
        '/var/www/reports/2026.pdf',
        as_attachment => 1,
        filename      => 'annual-report-2026.pdf',
    );
});

Constructs and returns a PAGI::FastAPI::Response::File instance to stream a file from disk to the client.

Delegates directly to "file_response" in PAGI::FastAPI::Response::File, with a context-aware default of as_attachment => 0 to ensure assets (such as CSS, JavaScript, or images) display inline in the browser rather than triggering a download prompt.

Accepts the following named options:

  • as_attachment (Boolean, optional)

    Controls the Content-Disposition response header. Defaults to 0 (inline) when called via $c->file. Set to 1 to force the browser to download the file as an attachment.

  • content_type (String, optional)

    Explicitly overrides the MIME type sent in the Content-Type header. When omitted, the content type is guessed automatically from the file extension of $filepath.

  • filename (String, optional)

    Sets a custom filename string for the Content-Disposition header. Useful when serving files stored under internal hashes or non-descriptive paths on disk.

  • status (Integer, optional)

    The HTTP status code to return with the file response. Defaults to 200.

Returns an instance of PAGI::FastAPI::Response::File.

html($content, %options)

$app->get('/about', handler => async sub ($c) {
    return $c->html('<h1>About Us</h1>');
});

Returns an HTTP response with the Content-Type header automatically set to text/html; charset=utf-8.

Accepts the HTML content string as the first parameter, followed by optional named parameters:

  • status (Optional)

    Integer HTTP status code. Defaults to 200.

  • headers (Optional)

    ArrayRef of additional header key-value pairs.

Returns a PAGI::FastAPI::Response object.

sse($code)

$app->get('/api/v1/metrics', handler => async sub ($c) {
    return $c->sse(async sub ($stream) {
        while (1) {
            await $stream->send_json({ cpu => 42 });
            await $c->sleep(1);
        }
    });
});

Creates and returns a Server-Sent Events (SSE) response object.

Accepts an async generator coderef that receives an SSE stream handler as its first argument.

  • $code

    An async sub coderef that defines the event streaming loop. The callback receives an instance of PAGI::SSE, offering methods such as send_event(), send_json(), send(), keepalive(), and close().

Returns an instance of PAGI::FastAPI::Response::SSE.

path_params()

Returns the HashRef containing all parsed path parameters.

path_param($key)

Returns a specific parsed path parameter by name, or undef if absent.

query_params()

Returns the HashRef containing all parsed query parameters.

query_param($key)

Returns a specific parsed query parameter by name, or undef if absent.

body([ $key ])

If called without parameters, returns the full raw/decoded request body.

If called with a $key parameter and the body is a HashRef, returns the value for that key, or undef if missing or if the body is not a HashRef.

my $full_body = $c->body;
my $user_name = $c->body('username');

form_data([ $key ])

$app->post('/upload', handler => async sub ($c) {
    my $caption = $c->form_data('caption');
    ...
});

Returns the parsed form fields for a request body that was either application/x-www-form-urlencoded or multipart/form-data, with the same calling convention as body: no arguments returns the full HashRef, a $key argument returns that one field's value (or undef).

For a multipart/form-data body, this returns only the plain text fields, file parts are available separately via uploaded_files. undef for a JSON body (JSON is not a form).

Note that for both application/x-www-form-urlencoded and multipart/form-data bodies, body and form_data return the same HashRef, form_data exists as a clearer-named alias for form-sourced data specifically, not as a separate parse.

uploaded_files([ $field_name ])

my $all_files   = $c->uploaded_files;           # HashRef, keyed by field name
my $avatar_list = $c->uploaded_files('avatar'); # ArrayRef for one field

Returns uploaded file parts from a multipart/form-data request body.

With no arguments, returns a HashRef keyed by field name. With a $field_name argument, returns just that field's files (or an empty ArrayRef if none were uploaded under that name).

Each file is always represented as an ArrayRef of HashRefs, even when only one file was uploaded for that field, so a single-file <input type="file"> and a multi-file <input type="file" multiple> have the same shape to consume. Each file HashRef has:

  • filename - The original filename supplied by the client.

  • content_type - The Content-Type declared for that part, or application/octet-stream if none was given.

  • content - The raw file bytes.

  • size - Byte length of content.

Note: file contents are currently held fully in memory for the duration of the request; there is no streaming or on-disk spooling for large uploads.

uploaded_file($field_name)

my $avatar = $c->uploaded_file('avatar');
if ($avatar) {
    say "Got $avatar->{filename}, $avatar->{size} bytes";
}

Convenience accessor for the common single-file-per-field case. Returns the first uploaded file HashRef for $field_name (see uploaded_files above for its shape), or undef if no file was uploaded under that name.

param($key)

Convenience parameter accessor that checks parameter stores in priority order:

1. Path parameters (path_param) 2. Query parameters (query_param) 3. JSON/Body fields (body($key))

Returns the first matching non-undef value, or undef if the key is not present in any store.

stash()

Returns a HashRef tied to the lifecycle of this context. Useful for sharing data between middleware, dependency injection blocks, and final route handlers.

$c->stash->{db_session} = $db;

background($code)

$app->post('/signup', handler => async sub ($c) {
    my $email = $c->body('email');

    $c->background(async sub {
        await send_welcome_email($email);   # runs after the response is sent
    });

    return { status => 'account created' };
});

Schedules an async sub to keep running after the response has already been sent, without making the caller wait for it, for fire-and-forget work like sending an email, writing an audit log entry, or warming a cache.

$code must be an async sub (or any coderef that, when called with no arguments, returns a Future); background calls it immediately and retains the resulting Future for you, so it won't be garbage-collected or silently dropped before it completes, the same way the framework already owns the request/response lifecycle. A task that dies is logged (via warn) rather than crashing the request that scheduled it, or any other in-flight request.

Retention is owned by the application, not the request: scheduled tasks outlive the $c that scheduled them, and any still-pending tasks are awaited during PAGI Lifespan shutdown (before your own on_shutdown callbacks run), so a task doesn't get killed mid-flight by process exit under normal shutdown.

Dies immediately, before scheduling anything, if $code isn't a coderef, or if calling it didn't return a Future (the most common cause of the latter: forgetting the async keyword).

AUTHOR

Mohammad Sajid Anwar, <mohammad.anwar at yahoo.com>

REPOSITORY

https://github.com/manwar/PAGI-FastAPI

BUGS

Please report any bugs or feature requests through the web interface at https://github.com/manwar/PAGI-FastAPI/issues. I will be notified and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc PAGI::FastAPI::Context

You can also look for information at:

LICENSE AND COPYRIGHT

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).