Skip to content

feat: add PATCH /api/chats endpoint to update chat topic#191

Merged
sweetmantech merged 6 commits intotestfrom
sweetmantech/myc-4111-api-migrate-recoup-chatappapiroomupdateroutets-to-patch
Feb 3, 2026
Merged

feat: add PATCH /api/chats endpoint to update chat topic#191
sweetmantech merged 6 commits intotestfrom
sweetmantech/myc-4111-api-migrate-recoup-chatappapiroomupdateroutets-to-patch

Conversation

@sweetmantech
Copy link
Contributor

@sweetmantech sweetmantech commented Feb 3, 2026

Summary

  • Migrate Recoup-Chat/app/api/room/update/route.ts to PATCH /api/chats following the new API docs
  • Add PATCH method handler with proper authentication, validation, and access control
  • Create supporting files following Recoup-API patterns (validation, handler, Supabase function)

Changes

  • app/api/chats/route.ts - Add PATCH export
  • lib/chats/updateChatHandler.ts - Business logic handler
  • lib/chats/validateUpdateChatBody.ts - Zod schema validation
  • lib/supabase/rooms/updateRoom.ts - Database update function

API Specification

PATCH /api/chats

Request body:

  • chatId (string, UUID): Required identifier for the chat room
  • topic (string, 3-50 characters): Required display name for the chat

Response (200):

{
  "status": "success",
  "chat": {
    "id": "uuid",
    "account_id": "uuid",
    "topic": "string",
    "updated_at": "timestamp",
    "artist_id": "uuid"
  }
}

Error responses: 400 (validation), 401 (auth), 403 (access denied), 404 (not found)

Test plan

  • Test with valid chatId and topic (3-50 chars) returns 200
  • Test with invalid UUID returns 400
  • Test with topic < 3 chars returns 400
  • Test with topic > 50 chars returns 400
  • Test without auth returns 401
  • Test with API key for different account returns 403
  • Test with non-existent chatId returns 404

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Users can now update chat room topics through the chat update functionality. Changes include request validation ensuring topic length between 3-50 characters, role-based access control to restrict modifications to authorized users, and comprehensive error responses for validation failures and unauthorized access attempts.

Migrate Recoup-Chat/app/api/room/update/route.ts to PATCH /api/chats
following the new API docs at developers.recoupable.com/api-reference/chat/update

- Add PATCH method to app/api/chats/route.ts
- Add lib/chats/updateChatHandler.ts for business logic
- Add lib/chats/validateUpdateChatBody.ts for Zod validation
- Add lib/supabase/rooms/updateRoom.ts for database operations

The endpoint:
- Accepts chatId (UUID) and topic (3-50 chars) in request body
- Supports both x-api-key and Authorization Bearer token auth
- Validates access using buildGetChatsParams (same as GET /api/chats)
- Returns 404 if chat not found, 403 if no access, 200 on success

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link
Contributor

vercel bot commented Feb 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recoup-api Ready Ready Preview Feb 3, 2026 2:37am

@coderabbitai
Copy link

coderabbitai bot commented Feb 3, 2026

Warning

Rate limit exceeded

@sweetmantech has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 39 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

This PR introduces a new PATCH /api/chats endpoint that enables updating a chat room's topic. The implementation includes request validation via Zod schema, access control enforcement with admin bypass, and database persistence through Supabase. Error handling covers validation failures, access denial, and update errors with consistent CORS headers and structured JSON responses.

Changes

Cohort / File(s) Summary
API Route and Handler
app/api/chats/route.ts, lib/chats/updateChatHandler.ts
Adds PATCH handler to route and implements core logic that validates request, checks access control (admins bypass), updates room topic via Supabase, and returns updated chat data with proper error handling and CORS headers.
Request Validation
lib/chats/validateUpdateChatBody.ts
Introduces Zod schema validation for chatId (UUID) and topic (3-50 chars), authenticates user context, verifies room existence, and returns validated data structure for downstream processing with 400/404 error responses.
Database Operations
lib/supabase/rooms/updateRoom.ts
Adds utility function to update room fields by ID in Supabase, executing the update query and returning the updated room data or null on failure.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Route as PATCH Handler
    participant Validator as validateUpdateChatBody
    participant Auth as validateAuthContext
    participant Handler as updateChatHandler
    participant DB as updateRoom (Supabase)

    Client->>Route: PATCH /api/chats
    Route->>Validator: validate request body
    Validator->>Auth: authenticate user
    Auth-->>Validator: user context
    Validator->>DB: select room by chatId
    DB-->>Validator: room data
    Validator-->>Route: validated data
    Route->>Handler: delegate to handler
    Handler->>Handler: check access control<br/>(admin or account_id match)
    Handler->>DB: updateRoom with topic
    DB-->>Handler: updated room
    Handler-->>Route: JSON response
    Route-->>Client: 200 + updated chat data<br/>or 403/500 error
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • Test #105: Both PRs extend the chats API surface by adding route handlers and follow the same validate-handler architectural pattern.

Poem

🎨 A topic update flows with grace,
Validated, authorized, in its place,
From request to database it takes flight,
Clean architecture shining bright! ✨

🚥 Pre-merge checks | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Solid & Clean Code ⚠️ Warning validateUpdateChatBody violates SOLID principles with five distinct responsibilities (parsing, validation, auth, lookup, error handling) versus focused validateCreateChatBody, imports 7 dependencies versus 3, and getCorsHeaders duplicated 8 times across codebase. Refactor validateUpdateChatBody to handle only schema validation (<20 lines), extract auth and room operations into separate functions, create shared createErrorResponse utility, and replace silent JSON failures with explicit errors.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sweetmantech/myc-4111-api-migrate-recoup-chatappapiroomupdateroutets-to-patch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Feb 3, 2026

Braintrust eval report

Catalog Opportunity Analysis Evaluation (HEAD-1770086204)

Score Average Improvements Regressions
Catalog_availability 3.2% (-17pp) 2 🟢 2 🔴
Llm_calls 0 (+0) - -
Tool_calls 0 (+0) - -
Errors 0 (+0) - -
Llm_errors 0 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 0tok (+0tok) - -
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 0tok (+0tok) - -
Completion_reasoning_tokens 0tok (+0tok) - -
Total_tokens 0tok (+0tok) - -
Duration 38.29s (+2.27s) 2 🟢 3 🔴

Catalog Songs Count Evaluation (HEAD-1770086204)

Score Average Improvements Regressions
Llm_calls 4 (+0) - -
Tool_calls 0 (+0) - -
Errors 3 (+0) - -
Llm_errors 1 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 0tok (+0tok) - -
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 0tok (+0tok) - -
Completion_reasoning_tokens 0tok (+0tok) - -
Total_tokens 0tok (+0tok) - -
Duration 13.24s (+0.9s) 1 🟢 2 🔴

First Week Album Sales Evaluation (HEAD-1770086204)

Score Average Improvements Regressions
Llm_calls 1 (+0) - -
Tool_calls 0 (+0) - -
Errors 1 (+0) - -
Llm_errors 0 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 0tok (+0tok) - -
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 0tok (+0tok) - -
Completion_reasoning_tokens 0tok (+0tok) - -
Total_tokens 0tok (+0tok) - -
Duration 11.25s (-2.32s) 4 🟢 -

Memory & Storage Tools Evaluation (HEAD-1770086204)

Score Average Improvements Regressions
Tools_called 0% (+0pp) - -
Llm_calls 0 (+0) - -
Tool_calls 0 (+0) - -
Errors 0 (+0) - -
Llm_errors 0 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 0tok (+0tok) - -
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 0tok (+0tok) - -
Completion_reasoning_tokens 0tok (+0tok) - -
Total_tokens 0tok (+0tok) - -
Duration 14.02s (-1.49s) 1 🟢 -

Monthly Listeners Tracking Evaluation (HEAD-1770086204)

Score Average Improvements Regressions
Llm_calls 2 (+0) - -
Tool_calls 0 (+0) - -
Errors 2 (+0) - -
Llm_errors 1 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 0tok (+0tok) - -
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 0tok (+0tok) - -
Completion_reasoning_tokens 0tok (+0tok) - -
Total_tokens 0tok (+0tok) - -
Duration 9.8s (-3.9s) 5 🟢 -

Search Web Tool Evaluation (HEAD-1770086204)

Score Average Improvements Regressions
Llm_calls 3 (+0) - -
Tool_calls 0 (+0) - -
Errors 2 (+0) - -
Llm_errors 1 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 0tok (+0tok) - -
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 0tok (+0tok) - -
Completion_reasoning_tokens 0tok (+0tok) - -
Total_tokens 0tok (+0tok) - -
Duration 22.66s (+0.3s) 5 🟢 6 🔴

Social Scraping Evaluation (HEAD-1770086204)

Score Average Improvements Regressions
Tools_called 0% (+0pp) - -
Llm_calls 0 (+0) - -
Tool_calls 0 (+0) - -
Errors 0 (+0) - -
Llm_errors 0 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 0tok (+0tok) - -
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 0tok (+0tok) - -
Completion_reasoning_tokens 0tok (+0tok) - -
Total_tokens 0tok (+0tok) - -
Duration 21.52s (+0.1s) 2 🟢 4 🔴

Spotify Followers Evaluation (HEAD-1770086204)

Score Average Improvements Regressions
Llm_calls 3 (+0) - -
Tool_calls 0 (+0) - -
Errors 2 (+0) - -
Llm_errors 1 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 0tok (+0tok) - -
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 0tok (+0tok) - -
Completion_reasoning_tokens 0tok (+0tok) - -
Total_tokens 0tok (+0tok) - -
Duration 11.3s (-2.9s) 4 🟢 1 🔴

Spotify Tools Evaluation (HEAD-1770086204)

Score Average Improvements Regressions
Tools_called 0% (+0pp) - -
Llm_calls 0 (+0) - -
Tool_calls 0 (+0) - -
Errors 0 (+0) - -
Llm_errors 0 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 0tok (+0tok) - -
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 0tok (+0tok) - -
Completion_reasoning_tokens 0tok (+0tok) - -
Total_tokens 0tok (+0tok) - -
Duration 30.11s (+2.77s) 1 🟢 1 🔴

TikTok Analytics Questions Evaluation (HEAD-1770086204)

Score Average Improvements Regressions
Question_answered 10% (+10pp) 1 🟢 -
Llm_calls 0 (+0) - -
Tool_calls 0 (+0) - -
Errors 0 (+0) - -
Llm_errors 0 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 0tok (+0tok) - -
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 0tok (+0tok) - -
Completion_reasoning_tokens 0tok (+0tok) - -
Total_tokens 0tok (+0tok) - -
Duration 14.32s (+1.3s) - 2 🔴

Bug fix:
- Remove target_account_id from buildGetChatsParams call which was causing
  "Personal API keys cannot filter by account_id" error for users updating
  their own chats
- Access control now correctly checks if room's account_id is in the
  user's allowed account_ids

Tests:
- Add comprehensive test suite for updateChatHandler covering:
  - Successful updates (personal key, org key)
  - Validation errors (invalid UUID, topic length)
  - Auth errors (401)
  - Not found errors (404)
  - Access denied errors (403)

Documentation:
- Add TDD section to CLAUDE.md requiring tests before implementation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- validateUpdateChatBody now takes NextRequest instead of unknown body
- Handles JSON parsing internally with try/catch
- Remove safeParseJson import from updateChatHandler
- Add comprehensive tests for validateUpdateChatBody (10 tests)
- Update handler tests to mock validateUpdateChatBody instead of safeParseJson

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- validateUpdateChatBody now handles:
  - JSON parsing
  - Body schema validation
  - Authentication via validateAuthContext
  - Room existence check via selectRoom
- Returns ValidatedUpdateChat with chatId, topic, room, accountId, orgId
- Handler now only handles access control and update logic
- Updated tests for both validation (13 tests) and handler (7 tests)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@lib/chats/updateChatHandler.ts`:
- Around line 29-38: The access check currently skips rooms with falsy
room.account_id which can grant unintended access; update the logic in the
handler around params.account_ids and room.account_id so that when
params.account_ids is present you explicitly treat null/undefined account IDs as
unauthorized: inside the branch that checks params.account_ids, first check if
room.account_id == null and return the same 403 NextResponse.json(...) (or a 500
if you want to assert the invariant), otherwise continue to check
!params.account_ids.includes(room.account_id) and return 403; reference the
existing symbols params.account_ids, room.account_id, NextResponse.json and
getCorsHeaders when making this change.

In `@lib/chats/validateUpdateChatBody.ts`:
- Around line 44-49: The try/catch around request.json() in
validateUpdateChatBody.ts currently swallows JSON parse errors and sets body =
{}, causing misleading validation errors; update the catch to detect JSON parse
failures from request.json() and immediately return/throw a 400 Bad Request with
a clear message like "Invalid JSON body" (including the parse error message for
debugging) instead of falling back to an empty object, keeping the rest of the
validation flow (the code that inspects body and checks chatId, title, etc.)
unchanged.

Comment on lines 29 to 38
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential access control gap when room.account_id is null.

The condition if (params.account_ids && room.account_id) short-circuits when room.account_id is falsy. If a room can legitimately have a null account_id (e.g., orphaned data, system-owned rooms), this would grant access to any authenticated user.

If account_id should always be present on rooms, consider adding a defensive check or asserting this invariant.

🛡️ Proposed defensive fix
     // Check if the room's account_id is in the allowed account_ids
     // If params.account_ids is undefined, it means admin access (all records)
-    if (params.account_ids && room.account_id) {
-      if (!params.account_ids.includes(room.account_id)) {
+    if (params.account_ids) {
+      // Deny access if room has no account_id (orphaned) or account not in allowed list
+      if (!room.account_id || !params.account_ids.includes(room.account_id)) {
         return NextResponse.json(
           { status: "error", error: "Access denied to this chat" },
           { status: 403, headers: getCorsHeaders() },
         );
       }
     }

Please verify whether room.account_id can legitimately be null in your data model:

#!/bin/bash
# Check if rooms table allows null account_id and find any existing null values
rg -n "account_id" --type=ts -C2 | grep -i "rooms\|null"
🤖 Prompt for AI Agents
In `@lib/chats/updateChatHandler.ts` around lines 29 - 38, The access check
currently skips rooms with falsy room.account_id which can grant unintended
access; update the logic in the handler around params.account_ids and
room.account_id so that when params.account_ids is present you explicitly treat
null/undefined account IDs as unauthorized: inside the branch that checks
params.account_ids, first check if room.account_id == null and return the same
403 NextResponse.json(...) (or a 500 if you want to assert the invariant),
otherwise continue to check !params.account_ids.includes(room.account_id) and
return 403; reference the existing symbols params.account_ids, room.account_id,
NextResponse.json and getCorsHeaders when making this change.

Comment on lines +44 to +49
let body: unknown;
try {
body = await request.json();
} catch {
body = {};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

JSON parse failure is silently swallowed, masking the root cause.

When request.json() throws (e.g., malformed JSON), the catch block silently converts it to an empty object. The user receives a generic validation error like "chatId is required" instead of a more helpful "Invalid JSON body" message.

Consider returning a specific 400 error for JSON parse failures to help API consumers debug issues faster.

🛠️ Proposed fix
   // Parse JSON body
   let body: unknown;
   try {
     body = await request.json();
   } catch {
-    body = {};
+    return NextResponse.json(
+      { status: "error", error: "Invalid JSON body" },
+      { status: 400, headers: getCorsHeaders() },
+    );
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let body: unknown;
try {
body = await request.json();
} catch {
body = {};
}
let body: unknown;
try {
body = await request.json();
} catch {
return NextResponse.json(
{ status: "error", error: "Invalid JSON body" },
{ status: 400, headers: getCorsHeaders() },
);
}
🤖 Prompt for AI Agents
In `@lib/chats/validateUpdateChatBody.ts` around lines 44 - 49, The try/catch
around request.json() in validateUpdateChatBody.ts currently swallows JSON parse
errors and sets body = {}, causing misleading validation errors; update the
catch to detect JSON parse failures from request.json() and immediately
return/throw a 400 Bad Request with a clear message like "Invalid JSON body"
(including the parse error message for debugging) instead of falling back to an
empty object, keeping the rest of the validation flow (the code that inspects
body and checks chatId, title, etc.) unchanged.

- validateUpdateChatBody now handles complete request validation:
  - JSON parsing
  - Body schema validation (chatId UUID, topic 3-50 chars)
  - Authentication via validateAuthContext
  - Room existence check via selectRoom
  - Access control via buildGetChatsParams
- Returns only { chatId, topic } on success (simpler interface)
- Handler now only calls updateRoom and formats response
- Validation tests: 16 tests (including 2 new access denied tests)
- Handler tests: 7 tests (simplified, validation errors pass through)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…-api-migrate-recoup-chatappapiroomupdateroutets-to-patch
@sweetmantech sweetmantech merged commit 5dbef4c into test Feb 3, 2026
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments