NAME

Hypersonic::Response - Fluent response builder for Hypersonic

SYNOPSIS

use Hypersonic;
use Hypersonic::Response 'res';

my $server = Hypersonic->new();

# Using the res() shortcut
$server->get('/users/:id' => sub {
    my ($req) = @_;
    my $id = $req->param('id');
    
    return res->json({ id => $id, name => "User $id" });
});

# Full fluent API
$server->post('/users' => sub {
    my ($req) = @_;
    my $data = $req->json;
    
    return res
        ->status(201)
        ->header('X-Request-Id', 'abc123')
        ->json({ created => $data->{name} })
        ->cookie('session', 'token123', httponly => 1, secure => 1);
}, { parse_json => 1 });

# Error responses
$server->get('/admin/:id' => sub {
    my ($req) = @_;
    my $token = $req->header('Authorization');
    
    return res->unauthorized('Invalid token') unless $token;
    return res->not_found('User not found')   unless $user;
    return res->json($user);
});

# Redirect
$server->get('/old-path' => sub {
    return res->redirect('/new-path', 301);
}, { dynamic => 1 });

# Explicit finalize() (usually not needed)
$server->get('/explicit' => sub {
    my ($req) = @_;
    return res->json({ ok => 1 })->finalize;
}, { dynamic => 1 });

DESCRIPTION

Hypersonic::Response provides a fluent (chainable) API for building HTTP responses. All methods return $self to enable chaining.

The response object uses JIT-compiled array-based storage for maximum performance, similar to Hypersonic::Request.

EXPORTS

res

use Hypersonic::Response 'res';

return res->json({ data => 'value' });

Shortcut constructor for cleaner handler code. Equivalent to Hypersonic::Response->new().

CONSTRUCTOR

new

my $res = Hypersonic::Response->new();
my $res = Hypersonic::Response->new(
    status  => 201,
    headers => { 'X-Custom' => 'value' },
    body    => 'Hello',
);

Create a new Response object. All options are optional:

status

Initial HTTP status code (default: 200)

headers

Initial headers hashref

body

Initial response body

cache_dir

Directory for JIT compilation cache

METHODS

All setter methods return $self for chaining.

status

$res->status(201);
$res->status(404);

Set the HTTP status code.

$res->header('Content-Type', 'application/json');
$res->header('X-Request-Id', $id);

Set a single response header.

headers

$res->headers(
    'X-Request-Id' => $id,
    'X-Custom'     => 'value',
);

Set multiple headers at once.

body

$res->body('Hello, World!');
$res->body($html_content);

Set the response body.

json

$res->json({ status => 'ok', data => $data });
$res->json([ 1, 2, 3 ]);

Set JSON response. Automatically:

  • Sets Content-Type to application/json

  • Encodes the data structure to JSON

Requires JSON::XS.

text

$res->text('Plain text content');

Set plain text response (Content-Type: text/plain).

html

$res->html('<h1>Hello</h1>');

Set HTML response (Content-Type: text/html).

xml

$res->xml('<root><item/></root>');

Set XML response (Content-Type: application/xml).

content_type

$res->content_type('image/png');

Set the Content-Type header directly.

redirect

$res->redirect('/new-location');          # 302 Found
$res->redirect('/new-location', 301);     # 301 Moved Permanently
$res->redirect('/new-location', 307);     # 307 Temporary Redirect

Set redirect response with Location header.

$res->cookie('session', $token);
$res->cookie('session', $token,
    path     => '/',
    domain   => '.example.com',
    max_age  => 3600,           # Seconds
    expires  => $http_date,     # HTTP date string
    httponly => 1,              # Not accessible via JavaScript
    secure   => 1,              # HTTPS only
    samesite => 'Strict',       # Strict, Lax, or None
);

Set a cookie with optional attributes.

$res->clear_cookie('session');
$res->clear_cookie('session', path => '/');

Clear a cookie by setting it to expire immediately.

cache

$res->cache('public, max-age=3600');

Set the Cache-Control header.

no_cache

$res->no_cache;

Disable caching with appropriate headers.

etag

$res->etag($checksum);

Set the ETag header.

last_modified

$res->last_modified($timestamp);   # Unix timestamp
$res->last_modified(time());

Set the Last-Modified header (auto-formats to HTTP date).

attachment

$res->attachment('report.pdf');

Set Content-Disposition for file download.

CONVENIENCE METHODS

These set both status code and body for common responses:

created

$res->created('/users/42');   # 201 with Location header

no_content

$res->no_content;             # 204 No Content

bad_request

$res->bad_request;                     # 400
$res->bad_request('Invalid input');    # 400 with message

unauthorized

$res->unauthorized;                    # 401
$res->unauthorized('Invalid token');   # 401 with message

forbidden

$res->forbidden;                       # 403
$res->forbidden('Access denied');      # 403 with message

not_found

$res->not_found;                       # 404
$res->not_found('User not found');     # 404 with message

conflict

$res->conflict;                        # 409
$res->conflict('Already exists');      # 409 with message

unprocessable

$res->unprocessable;                   # 422
$res->unprocessable('Validation failed');

too_many_requests

$res->too_many_requests;               # 429
$res->too_many_requests(60);           # With Retry-After header

server_error

$res->server_error;                    # 500
$res->server_error('Database error');  # 500 with message

unavailable

$res->unavailable;                     # 503
$res->unavailable(300);                # With Retry-After header

FINALIZATION

finalize

my $hashref = $res->finalize;

Convert the Response object to a hashref for returning from handlers. Returns:

{
    status  => 200,
    headers => { 'Content-Type' => 'application/json', ... },
    body    => '{"data":"value"}',
}

Note: Hypersonic automatically calls finalize() when a Response object is returned, so explicit finalization is usually not needed.

INTERNAL STRUCTURE

The response uses array-based storage:

use Hypersonic::Response qw(SLOT_STATUS SLOT_HEADERS SLOT_BODY SLOT_COOKIES);

# Direct slot access (advanced)
$res->[SLOT_STATUS] = 200;
$res->[SLOT_BODY]   = 'content';

EXAMPLES

REST API Response

$server->post('/api/users' => sub {
    my ($req) = @_;
    my $data = $req->json;
    
    # Validation
    return res->bad_request('Name required')
        unless $data->{name};
    
    # Create user
    my $user = create_user($data);
    
    return res
        ->status(201)
        ->header('Location', "/api/users/$user->{id}")
        ->json($user);
}, { parse_json => 1 });

Conditional Response

$server->get('/api/resource/:id' => sub {
    my ($req) = @_;
    my $resource = get_resource($req->param('id'));
    
    return res->not_found unless $resource;
    
    return res
        ->etag($resource->{version})
        ->cache('private, max-age=60')
        ->json($resource);
});

File Download

$server->get('/download/:file' => sub {
    my ($req) = @_;
    my $file = $req->param('file');
    my $content = read_file($file);
    
    return res
        ->content_type('application/octet-stream')
        ->attachment($file)
        ->body($content);
});

SEE ALSO

Hypersonic - Main HTTP server module

Hypersonic::Request - JIT-compiled request object

JSON::XS - Required for JSON responses

AUTHOR

LNATION <email@lnation.org>

LICENSE

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.