perf(server): return 204 from post_message (LC-228) #292

Merged
nrupard merged 2 commits from feat/lc-228-post-message-no-content into main 2026-06-01 16:18:53 +02:00
Owner

Summary

The composer form posts with hx-swap="none" (templates/room/composer.html:11), so any body returned by POST /room/{id}/messages is discarded by htmx. The handler was rendering a ~5 KB ComposerFragment template + serializing it on every send for nothing. This PR returns 204 No Content instead, skipping the render + serialization. The slash-dispatch branch still returns its own fragment (commands like /me render through the route), and the link-filter Block branch also flips to 204 since its old behaviour was the same wasted-render pattern.

The user-visible effects (textarea clear, send-button enable) are driven by event.detail.successful on hx-on::after-request, which fires for any 2xx response including 204. The rendered message itself is fanned out to every connected client by finalize_message_send via the WS hub - nothing on the wire to the sender is needed.

Test plan

  • just check clean (clippy + fmt for both standalone and saas)
  • just test passes (50 integration binaries)
  • just test-saas passes (modulo the known-flaky routes_uploads under concurrent-binary load - documented in CLAUDE.md)
  • Test-contract drift swept (category 4 per CLAUDE.md): every test asserting StatusCode::OK after a POST to /room/{id}/messages updated to assert StatusCode::NO_CONTENT. PATCH /messages/{id} (edit) is unchanged.
  • Manual: send a message in dev, verify textarea clears and the message appears via the WS broadcast (no flicker, no double-render).
## Summary The composer form posts with `hx-swap="none"` (`templates/room/composer.html:11`), so any body returned by `POST /room/{id}/messages` is discarded by htmx. The handler was rendering a ~5 KB `ComposerFragment` template + serializing it on every send for nothing. This PR returns `204 No Content` instead, skipping the render + serialization. The slash-dispatch branch still returns its own fragment (commands like `/me` render through the route), and the link-filter Block branch also flips to 204 since its old behaviour was the same wasted-render pattern. The user-visible effects (textarea clear, send-button enable) are driven by `event.detail.successful` on `hx-on::after-request`, which fires for any 2xx response including 204. The rendered message itself is fanned out to every connected client by `finalize_message_send` via the WS hub - nothing on the wire to the sender is needed. ## Test plan - [x] `just check` clean (clippy + fmt for both standalone and saas) - [x] `just test` passes (50 integration binaries) - [x] `just test-saas` passes (modulo the known-flaky `routes_uploads` under concurrent-binary load - documented in CLAUDE.md) - [x] Test-contract drift swept (category 4 per CLAUDE.md): every test asserting `StatusCode::OK` after a POST to `/room/{id}/messages` updated to assert `StatusCode::NO_CONTENT`. PATCH /messages/{id} (edit) is unchanged. - [ ] Manual: send a message in dev, verify textarea clears and the message appears via the WS broadcast (no flicker, no double-render).
perf(server): return 204 from post_message instead of unused composer fragment (LC-228)
All checks were successful
check-secrets / Nosey parker (push) Successful in 3s
check-secrets / Kingfisher (push) Successful in 4s
check-secrets / TruffleHog (push) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 3s
check-secrets / TruffleHog (pull_request) Successful in 4s
check-secrets / Kingfisher (pull_request) Successful in 5s
Check / clippy + fmt + tests (pull_request) Successful in 2m11s
e48269c57f
The composer form posts with `hx-swap="none"` (composer.html:11), so any body returned by `POST /room/{id}/messages` is discarded by htmx. The handler was rendering a ~5 KB `ComposerFragment` template and serializing it on every send for nothing. Switch the success return (and the link-filter Block branch) to `StatusCode::NO_CONTENT` and drop the unused `ComposerFragment` import. The slash-dispatch arm continues to return its own fragment via `into_response()` so commands like `/me` still render through the route. The user-visible effects (textarea clear, send-button enable) are driven by `event.detail.successful` on `hx-on::after-request`, which fires for any 2xx including 204; the rendered message itself is fanned out via the WS hub by `finalize_message_send`.

Test contract: this is a category-4 (test-contract drift) change per CLAUDE.md. Updated every integration test that POSTs to `/room/{id}/messages` and asserts `StatusCode::OK` to assert `StatusCode::NO_CONTENT` instead. `PATCH /messages/{id}` (edit) is unchanged and still returns 200 with the rendered fragment.
perf(server): return 204 from slash dispatch too (LC-228)
All checks were successful
check-secrets / TruffleHog (pull_request) Successful in 3s
check-secrets / Nosey parker (push) Successful in 3s
check-secrets / Nosey parker (pull_request) Successful in 4s
check-secrets / Kingfisher (pull_request) Successful in 5s
check-secrets / TruffleHog (push) Successful in 5s
check-secrets / Kingfisher (push) Successful in 7s
Check / clippy + fmt + tests (pull_request) Successful in 2m9s
Create release / Create release from merged PR (pull_request) Has been skipped
5e586e0b45
The slash-command dispatch in `routes/slash.rs` had the same waste pattern as the LC-228 fix in `post_message`: `/me`, `/shrug`, `/poll`, `/remind`, and custom commands all rendered a ~5 KB `ComposerFragment` after posting their message, even though the composer form posts with `hx-swap="none"` so htmx discards the body. Flipping these to 204 saves the render on every slash send.

Signature: `try_dispatch` now returns `Result<Option<Response>, AppError>` instead of `Result<Option<Html>, AppError>` so it can mix the 204 path with the `/help` render path (which legitimately returns HTML for the ephemeral OOB result panel). The room.rs caller is simplified to return `resp` directly (no `.into_response()` wrap needed on a Response).

Tests: routes_slash.rs `/me` and `/remind` now assert NO_CONTENT; `/help` stays OK (legit render). routes_polls.rs `slash_command_posts_poll` flipped to NO_CONTENT (POST /room/{id}/messages -> slash dispatch -> /poll).
nrupard deleted branch feat/lc-228-post-message-no-content 2026-06-01 16:18:53 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!292
No description provided.