Added
- Transport-conditional API returns —
api_response()convention +@event_handler(expose_api=True, serialize=...)override (v0.5.1 P2 follow-up to ADR-008) — Handlers serving both WebSocket and HTTP API callers often have split needs: WS only wants state mutation (VDOM renders the UI), HTTP wants actual data in the response. Serializing query results on every WS keystroke is wasteful. Resolved with three-tier resolution on the HTTP path (zero overhead on WS): (1) per-handler@event_handler(expose_api=True, serialize=<callable-or-str>)wins when set; (2) otherwise the view'sapi_response(self)method is called (the DRY convention — one method, many handlers); (3) otherwise the handler's return value passes through unchanged.serialize=accepts a callable (arity-detected:fn()/fn(view)/fn(view, result)) or a method-name string resolved against the view at dispatch time. Async serializers and asyncapi_response()are both awaited.serialize=withoutexpose_api=TrueraisesTypeErrorat decoration. Missing method or serializer exception → 500serialize_error(details logged server-side only);PermissionDeniedraised from either path surfaces as 403 (not 500). Theself._api_request = Trueflag is set by dispatch beforemount()runs so mount can branch on transport; it is retained as an escape hatch for code that needs transport awareness without the decorator plumbing. 28 tests inpython/djust/tests/test_api_response.pycover unit-level resolution (passthrough, convention, per-handler override, arity detection, async paths, MRO-provided api_response, shadowed non-callable api_response, invalid spec types, staticmethod-via-string, callable class instances) and end-to-end dispatch integration (including PermissionDenied surfacing as 403 and the mount-time flag availability). Full guide indocs/website/guides/http-api.mdunder "Transport-conditional returns". (python/djust/decorators.py,python/djust/api/dispatch.py)