Revision history for PAGI::FastAPI 1.7.0 2026-08-29 [FEATURE] - File uploads: a request body with Content-Type multipart/form-data is now parsed (per RFC 7578) instead of unconditionally failing JSON decode and hard-rejecting with 422 before any handler or dependency ran. New PAGI::FastAPI::Context methods: form_data(), uploaded_files(), uploaded_file(). - Background tasks: new $c->background(async sub {...}) on PAGI::FastAPI::Context for fire-and-forget work that keeps running after the response has been sent. Retention is owned by the application (not the request), a failing task is logged rather than crashing anything, and any still-pending tasks are drained on PAGI Lifespan shutdown before on_shutdown callbacks run. - Per-route OpenAPI metadata: get/post/put/patch/delete now accept tags, summary, description, deprecated, and a responses => {...} map (merged over, not replacing, the existing default 200/422 entries), threaded into the generated OpenAPI document for that route. summary still defaults to "$method $path" when omitted. - PAGI::FastAPI::Context now includes a file() method for returning file responses with configurable MIME types and attachment headers. - Swagger UI rendering now accepts an injectable $swagger_ui_parameters configuration hash during app instantiation. - Added $swagger_ui_css_url and $swagger_ui_bundle_url constructor attributes, allowing developers to override CDN links or serve Swagger UI assets locally. [BUGFIX] - PAGI::FastAPI->new's default 'version' (when not explicitly passed) was the blessed version object itself rather than a plain string, which crashed encode_json on /openapi.json for any app that didn't pass an explicit version. Found via testing the OpenAPI metadata feature above; unrelated to it otherwise. - Resolved random visual shuffling of endpoints in the /docs UI by explicitly defaulting operationsSorter to 'method' and tagsSorter to 'alpha'. [TESTING] - Added the following unit tests to cover new features added: - t/37-multipart_file_uploads.t - t/38-background_tasks.t - t/39-openapi_route_options.t [EXAMPLES] - Added the following applications to demo new features: - eg/background_demo.pl - eg/docs_demo.pl - eg/file_uploads_demo.pl 1.6.0 2026-08-25 [SECURITY FIXES] - Added 'trust_proxies' configuration parameter to PAGI::FastAPI::Middleware::BotProtection, defaults to 0. - Added pow() reader method to PAGI::FastAPI::Middleware::BotProtection to expose the underlying PAGI::FastAPI::BotProtection::ProofOfWork instance for inspection and testing. - Fixed IP spoofing security vulnerability by ignoring untrusted 'X-Forwarded-For' headers unless 'trust_proxies' is explicitly enabled. - Properly extract the original client IP from comma-separated 'X-Forwarded-For' proxy chains when 'trust_proxies' is enabled. [TESTING] - Added unit test: t/36-middleware_bot_protection_trust_proxies.t 1.5.0 2026-08-25 [SECURITY FIXES] - CWE-1188: Removed default fallback string 'change_me_in_production' from $secret in PAGI::FastAPI::BotProtection::ProofOfWork and PAGI::FastAPI::Middleware::BotProtection. - Enforced mandatory check for defined $secret; throws an exception when unconfigured. [POD] - Updated BotProtection SYNOPSIS and POD documentation to reflect required secret initialisation. [TESTING] - Added unit test: t/35-security_cwe_1188_secret_default.t 1.4.0 2026-08-24 [SECURITY FIXES] - Fix silent dropping of object-based middleware in to_app() (CWE-306, CWE-352). Object middleware (implementing 'wrap' or 'call', such as CSRF and Basic Auth) registered via 'add_middleware' or 'enable_csrf' were ignored when deployed using to_app(), bypassing authentication and forgery protection. Unified to_app() and to_pagi() execution pipelines. Reported by CPANSec. [TESTING] - Add t/34-security_cwe_306_object_middleware_execution.t to verify object middleware pipeline execution in to_app(). 1.3.0 2026-08-23 [SECURITY FIXES] - Fix rate-limit key resolution using untrusted HTTP request headers (CWE-807). Default key_cb now strictly identifies clients by their TCP socket address ($c->scope->{client}[0]) rather than prioritising client-controlled headers. Header-derived identity can be explicitly enabled by setting 'trust_proxies => 1'. Reported by CPANSec. - Fix unbounded memory growth in PAGI::FastAPI::RateLimit::Driver::Memory (CWE-770). Enforce a maximum active key limit (`max_keys`, defaults to 10000) with key eviction, and add periodic probabilistic cache sweeps to purge stale entries. Reported by CPANSec. [ENHANCEMENTS] - Add trust_proxies parameter to PAGI::FastAPI::Middleware::RateLimit. - Add max_keys parameter and count() introspection method to PAGI::FastAPI::RateLimit::Driver::Memory. [TESTING] - Add t/32-security_cwe_807_key_spoofing.t to test header-spoofing resilience. - Add t/33-security_cwe_770_memory_cap.t to test driver memory bounds. 1.2.6 2026-08-23 [DEMO] - Added demo app for Webhook: eg/webhook_demo.pl 1.2.5 2026-08-21 [IMPROVEMENT] - Updated demo app and capture job_id when adding task. eg/memory_queue_demo.pl 1.2.4 2026-08-20 [IMPROVEMENT] - Updated constant_time_eq() in the API Gateway demo app. 1.2.3 2026-08-20 [IMPROVEMENT] - Fixed API Gateway demo app w.r.t setting header, thanks John Napiorkowski. 1.2.2 2026-08-20 [IMPROVEMENT] - Fixed API Gateway demo app, thanks John Napiorkowski. 1.2.1 2026-08-20 [POD] - Removed redundant link to CPAN Ratings, thanks @davorg. 1.2.0 2026-08-20 [FEATURE] - Added PAGI::FastAPI::TypedPath, providing a Depends()-compatible TypedPath($param_name, $type) helper that validates (and, for a coercing Type::Tiny type, converts) a path parameter the same way query/body validation already works, short-circuiting with HTTP 422 for a non-matching value before the handler runs. Path parameters have no coercion hook at the routing layer itself; this is built entirely on the existing per-route dependencies extension point. - Added PAGI::FastAPI::ResponseModel's with_response_model($schema, $handler) wrapper, which validates a handler's return value against a declared Type::Tiny schema and, for a HashRef-of-fields schema, filters the output to just the declared fields (dropping e.g. an accidental password_hash column from an ORM row). A schema mismatch is treated as HTTP 500 (a server bug), mirroring Python FastAPI's ResponseValidationError, rather than leaking the validation failure or the invalid data to the client. - Added PAGI::FastAPI::Middleware::ExceptionHandler for typed exception-to-handler dispatch (Python FastAPI's @app.exception_handler equivalent): registers a coderef per exception class, dispatched via blessed()/isa(), with a configurable default_handler fallback and re-throw if nothing matches. For the case of a real Perl exception (die) escaping a handler or dependency, complementing (not replacing) the existing $c->status(...)-and-return convention. - Added PAGI::FastAPI::Response::Redirect, a PAGI::FastAPI::Response subclass for HTTP redirects, plus a redirect_to($location, %opts) convenience function defaulting to 302. - Added PAGI::FastAPI::Response::File, a PAGI::FastAPI::Response subclass for file-download responses, plus a file_response($path, %opts) convenience function with content-type guessing by extension and automatic Content-Disposition handling. Reads the whole file into memory; not intended for very large files. - Added PAGI::FastAPI::Cookies, providing parse_cookies($raw_header) and cookie($c, $name) for parsing the request Cookie header. Pure parsing on top of the existing $c->header('Cookie') accessor. [DOCUMENTATION] - Updated PAGI::FastAPI's POD: Key Features now documents all six modules above; ERROR HANDLING cross-references PAGI::FastAPI::Middleware::ExceptionHandler for the uncaught- exception case it doesn't otherwise cover; SEE ALSO links all six. - Added a "RESPONSE HELPERS, VALIDATION & ERROR HANDLING" section to README.md covering the same six modules, plus a new SYNOPSIS step demonstrating TypedPath + with_response_model together and the Redirect/File responses. [TESTS] - Added t/26-response_redirect.t. - Added t/27-response_file.t. - Added t/28-cookies.t. - Added t/29-response_model.t. - Added t/30-typed_path.t (includes a skip-gracefully integration subtest exercising TypedPath through a real Depends() + full app dispatch, alongside direct unit tests of the validation/coercion logic itself). - Added t/31-middleware_exception_handler.t. [DEMO] - Added demo app for API Gateway linked to two microservices: - eg/api_gateway.pl - eg/user_microservice.pl - eg/order_microservice.pl 1.1.0 2026-08-18 [FEATURE] - We have included PAGI::FastAPI::Queue which is an async-first message queue facade with a topic-based push(), pop(), and size() operations. We have also added dep() helper for use in conjunction with PAGI::FastAPI::Depends and the support for the choice of the storage driver by merely the short name or fully-qualified class name provided by the driver constructor option. - PAGI::FastAPI::Queue::Driver has been added, which is an abstract async base class used for all queue storage drivers (be it built-in or 3rd party). - We have added a new built-in storage driver called PAGI::FastAPI::Queue::Driver::Memory. [TESTS] - The file t/22-queue_driver.t was added to contain contract tests of the abstract PAGI::FastAPI::Queue::Driver base class which returns failed Future errors for push, pop, and size methods in case they are not overruled. - The file t/23-queue_driver_memory.t was introduced to hold unit tests for the PAGI::FastAPI::Queue::Driver::Memory subclass which conducts FIFO operation tests. - The file t/24-queue.t was created to run unit tests for the functionalities of the PAGI::FastAPI::Queue interface, which verifies its working of the driver name in its three possible forms and the ability to handle possible errors in the absence of the object in the class. - The file t/25-rate_limit_stacked.t was created to test app-level rate liimt and per route rate limit stacked together. - All the newly added modules were included in t/00-load.t so that they can be made use of to call are OK. 1.0.0 2026-08-12 [FEATURE] - Refactored core framework to modern Perl syntax: use experimental 'class'. - Added PAGI::FastAPI::RateLimit::Driver abstract async base class for pluggable rate-limiting storage drivers. - Added PAGI::FastAPI::RateLimit::Driver::Memory as the built-in, default in-memory storage driver. - Added PAGI::FastAPI::Middleware::RateLimit to support app-level and route-level rate limiting using a fixed time-window counter. - Added PAGI::FastAPI::BotProtection::ProofOfWork for stateless cryptographic bot mitigation. - Added PAGI::FastAPI::Middleware::BotProtection middleware to automatically enforce challenge/response flow on unauthenticated requests. - Added $app->add_bot_protection() helper method to PAGI::FastAPI. - Added PAGI::FastAPI::Response::SSE to support production-grade Server-Sent Events (SSE) streaming via PAGI::SSE. - Added $c->sse() context helper method for simplified SSE stream creation with support for keepalives, custom headers, and auto-JSON serialisation. - Added $c->sse() helper method to PAGI::FastAPI::Context for Server-Sent Events support. - Added $c->sleep() non-blocking sleep utility method using Future::IO. - Added PAGI::FastAPI::Response base class to standardise HTTP response handling across HTML, SSE, and JSON handlers. - Improved route response dispatcher in PAGI::FastAPI to correctly route streaming responses (can('dispatch')) and response objects without triggering JSON serialisation errors. - Added stringification overload fallback for response classes. - Added enable_csrf(), to_pagi() to PAGI::FastAPI. - Added csrf_token(), csrf_verify(), pagi_context() to PAGI::FastAPI::Context. [DOCUMENTATION] - Updated POD for core classes and added comprehensive POD for: PAGI::FastAPI::RateLimit::Driver, PAGI::FastAPI::RateLimit::Driver::Memory, PAGI::FastAPI::Middleware::RateLimit, PAGI::FastAPI::BotProtection, PAGI::FastAPI::BotProtection::ProofOfWork, PAGI::FastAPI::Middleware::BotProtection, PAGI::FastAPI::Response, PAGI::FastAPI::Response::HTML and PAGI::FastAPI::Response::SSE. [TESTS] - Added integration tests for rate limiting: t/12-rate_limit.t. - Added comprehensive unit test suite: t/13-bot_protection.t. - Added test suite for PAGI::SSE: t/14-sse_streaming.t - Added test for HTML response: t/15-html_response.t - Added test for CSRF: t/16-middleware_csrf. - Added t/17-enable_csrf_app_secret.t covering the app-level secret fallback fix, the call-level override, and the "no secret anywhere" error path. - Added t/18-context_extras.t covering PAGI::FastAPI::Context's param(), csrf_token(), csrf_verify(), html(), sse(), and sleep(), none of which had any prior test coverage. - Added t/19-ratelimit_driver_memory.t: direct unit tests for PAGI::FastAPI::RateLimit::Driver::Memory, including a regression test for the increment_async() two-value contract. - Added t/20-bot_protection_ipv6.t: regression tests for the IPv6 delimiter fix and the difficulty=0 edge case. - Added t/21-depends.t covering PAGI::FastAPI::Depends, including the previously-untested ADJUST non-CODE-reference guard. [EXAMPLES] - Added working example of SSE: eg/sse_demo.pl - Added working example of CSRF: eg/csrf_demo.pl [PACKAGING] - Corrected MIN_PERL_VERSION in Makefile.PL from 5.036 to 5.038000. The `class`/`field`/`method`/`ADJUST` keywords used throughout lib/ (via `use experimental 'class'`) were only added to the Perl interpreter in 5.38.0; they do not exist in 5.36, regardless of the `use experimental` pragma. Every lib/*.pm now consistently declares `use v5.38;` to match. - Added TEST_REQUIRES entries: Test::Fatal and PAGI::Test::Client, both of which the test suite already depended on without declaring. 0.1.0 2026-08-10 [BREAKING] - The add_cors function now utilizes PAGI::Middleware::CORS instead of implementing its own version, based on the recommendation of the author of the PAGI specifications. The reason to make this switch is the established use of PAGI::Tools in other components of the package. The previous names of the options have been changed, as follows: allow_origins and allow_methods were replaced with origins and methods, respectively, while the new default value for max_age changed from 600 to 86,400. The value of max_age is the default one used by PAGI::Middleware::CORS. There is also a new value called expose_headers. - CORS is added as the outermost level in to_app() and will be applied after mount()-ed sub-applications and static files are created. Thus, CORS will be applied throughout the application and not only to get/post/other functions like before. - New requirement: PAGI::Middleware::CORS >= 0.002002 (comes with the same PAGI::Tools package as PAGI::WebSocket, already in the list of prerequisites. Also, presented here explicitly as being directly utilised). [DOCUMENTATION] - The previous event-loop section has been revised (renamed from "MIXING WITH OTHER EVENT LOOPS" to "EVENT LOOPS: FUTURE::IO IS THE GOAL, IO::ASYNC IS AN IMPLEMENTATION DETAIL"), at the request of the author of the PAGI specification: previously, it stated that "PAGI::FastAPI works entirely on IO::Async," although this makes it sound like a load-bearing requirement instead of simply an implementation detail. It now insists on Future::IO being the recommended way of writing your own event-driven code (timers, delays) and provides a working example following the heartbeat pattern presented in the chat-server example. The IO::Async::Loop::EV/IO_ASYNC_LOOP=EV technology has been preserved, but it is defined as simply a backup solution used in very specific cases involving the use of Mojo::IOLoop-based libraries that appeared prior to Future::IO. 0.0.9 2026-08-08 [BREAKING] - Removed PAGI::FastAPI::WebSocket entirely. - websocket() handlers now receive a plain PAGI::WebSocket instance directly. For anyone who wasn't relying on the class name itself (isa checks, `use PAGI::FastAPI::WebSocket` directly), this is a no-op - every method, return type, and calling convention is unchanged from 0.0.10, since that version was already a strict, override-free subclass. - _handle_websocket now sets $scope->{path_params} directly and calls PAGI::WebSocket->new($scope, $receive, $send) - the same glue that used to live in the now-removed class's constructor. - Dropped the now-unused JSON::MaybeXS prereq (it was only ever needed by the removed class) and the PAGI::FastAPI::WebSocket entry from t/00-load.t and the provides map. 0.0.8 2026-08-08 [DOCUMENTATION] - There is a new "MIXING WITH OTHER EVENT LOOPS" section which describes the interoperability issue between IO::Async (on which PAGI::FastAPI is based) and Mojo::IOLoop-based libraries like Mojo::Pg. The paragraph in question explains that IO::Async allows calls to the non-blocking API to hang unless both libraries utilise the same reactor. The other method of calling a library in a blocking manner locks every other connection in the process. The solution is detailed (IO::Async::Loop::EV + IO_ASYNC_LOOP=EV, along with wrapping the callback API in a Future). 0.0.7 2026-08-08 [DOCUMENTATION] - Reworded PAGI::FastAPI::WebSocket's receive_text()/receive_bytes() POD, which previously said they "block asynchronously" - a self-contradictory phrase that read as "this blocks the server" to at least one reader. Now states plainly that they suspend only the current connection's coroutine and never block the event loop or other connections. Added the same clarification to the module DESCRIPTION. No code changes; the implementation was already correctly non-blocking (verified against PAGI::Server::Connection's per-connection dispatch, which detaches each handler via adopt_future rather than awaiting it inline). 0.0.6 2026-08-08 [ENHANCEMENTS] - Full support for WebSocket routing has been added and is available by creating a $app->websocket() endpoint. The implementation is completed with async frame handling and parameter extraction capabilities. - A new class called PAGI::FastAPI::WebSocket has been introduced. It allows for the non-blocking implementation of the handshake and message methods: accept(), close(), receive_text(), receive_json(), receive_bytes(), send_text(), send_json(), send_bytes(). - The mount() method has been implemented, allowing PAGI sub-applications, static files, and routers to be mounted via PAGI::App::URLMap. - The add_middleware() method has been implemented for registering custom async middleware. - The WebSocket routes were excluded from the generated OpenAPI documentation version 3.1. [BUG FIXES & ERROR HANDLING] - The unmatched WebSocket routes already reject the connections with a close code 4004 before the acceptance of handshake. - Unhandled exceptions in the WebSocket handlers lead to open connections termination with a close code 1011 automatically. - Dialing execution errors on the WebSocket endpoints lead to the closure of the connections automatically with the status code 1008. [DOCUMENTATION & EXAMPLE] - Chat server application was added to showcase HTML/JavaScript and WebSocket functionality. - Updated module documentation and SYNOPSIS for PAGI::FastAPI and PAGI::FastAPI::WebSocket. [TESTING] - Created t/11-websocket.t that includes handshakes, echo flows, path parameters, JSON payload handling and status codes of close. - Updated t/00-load.t to incorporate PAGI::FastAPI::WebSocket. 0.0.5 2026-08-06 [ENHANCEMENT] - The POST/PUT/PATCH request bodies can now be in the application/x-www-form-urlencoded format instead of being restricted to JSON. The body parser examines the Content-Type header; therefore, the charset suffix (such as ";charset=utf-8") will be ignored. Furthermore, the body parser will process url-encoded data in the same manner as query string in which the body is decoded using percent-/plus-decode algorithm. However, the same Type::Tiny validation can be applied in either format and the handler will receive a HashRef object as a result of a processed request regardless of the sent format. - It is backward compatible, i.e., if the Content-Type header is not set or not recognised, the body will be processed in the same way as before. In particular, the default behavior of parsing requests is JSON parsing. - Also the new feature was tested in t/10-form_urlencoded_body.t. - The plugin is documented in the `body` configuration option as well as in the ERROR HANDLING section. The decision to implement this feature was influenced by the example provided on a PAGI::FastAPI::Security page. 0.0.4 2026-08-05 [DOCUMENTATION] - Included an AUTHENTICATION AND SECURITY part in the POD to highlight the dependencies and components of middleware and provided information about the new companion distribution called PAGI::FastAPI::Security for the ready-to-use schemes related to HTTP Bearer, HTTP Basic, API Key, and OAuth2 password-bearer. - Added a Pluggable Authentication item in Key Features List. - Added PAGI::FastAPI::Security and DBIx::Class::Async to SEE ALSO section. - Mentioned in the SYNOPSIS the introduction about the functionality of hand-rolled Bearer-token checks in this context and referring to PAGI::FastAPI::Security for production use. - Added a similar entry in README.md as well. [EXAMPLE] - Updated the example to demo authentication using PAGI::FastAPI::Security: eg/dbic_async_integration.pl 0.0.3 2026-08-04 [EXAMPLE] - Added integration demo script: eg/integration_demo.pl 0.0.2 2026-08-04 [BUGFIX] - Routes no longer consider metacharacters in their static paths (like ".", "+", "?") as wildcards. Only placeholders {param} are transformed into capture groups. Previously, it would allow the route '/items.json/{id}' to match paths like '/itemsXjson/{id}'. - The values of path parameters and query string parameters are now appropriately decoded: '%XX' and '+' decoding is now done as it should be according to the application/x-www-form-urlencoded rules. Until now strings like 'John%20Doe' were sent to handlers unchanged. - The request body reader no longer goes into an infinite loop when the PAGI server sends a non-'http.request' event (for example 'http.disconnect') while transmitting POST/PUT/PATCH requests. - When registering routes, the process now fails with a clear message instead of just ignoring misconfiguration. For instance, absence of a 'handler' property, 'dependencies' property not being a HashRef or an ArrayRef is rejected immediately during route registration. [TESTING] - A new test was added in t/08-input_handling_and_registration.t covering the above-mentioned aspects. - A new test was added in t/09-external_async_resource_integration.t to cover DBIx::Class::Async integration. [EXAMPLE] - Added additional DBIx::Class::Async integrated application: eg/dbic_async_integration.pl 0.0.1 2026-08-03 - Initial CPAN release. - Asynchronous routing engine built on top of PAGI ($scope, $receive, $send). - Type-safe parameter & POST JSON body validation via Type::Tiny. - Async Middleware pipeline support with built-in CORS helper (add_cors). - FastAPI-style Dependency Injection system via PAGI::FastAPI::Depends. - Integrated OpenAPI 3.1 schema (/openapi.json) and interactive Swagger UI (/docs).