Skip to content

feat: migrate memories get to /api/chats/{id}/messages#383

Merged
sweetmantech merged 7 commits intotestfrom
codex/arpit-migrate-memories-get-api
Apr 1, 2026
Merged

feat: migrate memories get to /api/chats/{id}/messages#383
sweetmantech merged 7 commits intotestfrom
codex/arpit-migrate-memories-get-api

Conversation

@arpitgupta1214
Copy link
Copy Markdown
Collaborator

@arpitgupta1214 arpitgupta1214 commented Mar 31, 2026

Summary

  • add dedicated GET /api/chats/{id}/messages endpoint
  • add chat access validation usage and memories handler tests
  • align with chat API migration findings

Notes

  • branch is squashed to one commit

Summary by CodeRabbit

  • New Features
    • Added API endpoint to fetch chat messages for conversations.
    • Added input validation with clear error responses for invalid requests.
    • Improved error handling to return appropriate error responses when issues occur.
    • Enabled CORS support so messages can be accessed cross-origin.

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 31, 2026

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

Project Deployment Actions Updated (UTC)
recoup-api Ready Ready Preview Apr 1, 2026 1:55am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Warning

Rate limit exceeded

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

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 18 minutes and 38 seconds.

⌛ 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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 583e0242-0069-49a6-8d3b-41526cc48a75

📥 Commits

Reviewing files that changed from the base of the PR and between 46a5c72 and 21923d7.

⛔ Files ignored due to path filters (2)
  • lib/chats/__tests__/getChatMessagesHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/chats/__tests__/validateGetChatMessagesQuery.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (2)
  • lib/chats/getChatMessagesHandler.ts
  • lib/chats/validateGetChatMessagesQuery.ts
📝 Walkthrough

Walkthrough

Adds a new Next.js API route GET /api/chats/[id]/messages plus OPTIONS CORS preflight; the route forwards requests to getChatMessagesHandler, which validates the chat UUID, enforces access via validateChatAccess, and retrieves chat memories from storage before returning a JSON response with CORS headers.

Changes

Cohort / File(s) Summary
Chat Messages API
app/api/chats/[id]/messages/route.ts, lib/chats/getChatMessagesHandler.ts
Adds route with OPTIONS (CORS) and GET handlers. GET resolves route params, delegates to getChatMessagesHandler. New handler validates id (UUID), calls validateChatAccess, queries selectMemories(...), and returns 400/500/200 JSON responses with CORS headers; catches and logs errors.

Sequence Diagram

sequenceDiagram
    participant Client
    participant RouteHandler as GET Route
    participant ChatHandler as getChatMessagesHandler
    participant Validator as validateChatAccess
    participant Storage as selectMemories

    Client->>RouteHandler: GET /api/chats/{id}/messages
    RouteHandler->>ChatHandler: await getChatMessagesHandler(request, id)
    ChatHandler->>ChatHandler: validate id (UUID)
    alt invalid id
        ChatHandler-->>Client: 400 JSON Error (with CORS)
    else valid id
        ChatHandler->>Validator: validateChatAccess(request, id)
        alt access denied (NextResponse)
            Validator-->>ChatHandler: NextResponse (error)
            ChatHandler-->>Client: 401/403 Response (with CORS)
        else access granted
            ChatHandler->>Storage: selectMemories(roomId, {ascending:true})
            alt storage error / null
                Storage-->>ChatHandler: null / error
                ChatHandler-->>Client: 500 JSON Error (with CORS)
            else memories found
                Storage-->>ChatHandler: memories[]
                ChatHandler-->>Client: 200 JSON { data: memories } (with CORS)
            end
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

📨 A tiny route wakes to answer calls,
UUID checked before the memory halls,
Access granted, then the stories stream,
CORS waved kindly to keep the flow clean. ✨

🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Solid & Clean Code ⚠️ Warning Implementation violates DRY principle with duplicate UUID validation logic and inconsistent response format compared to similar handlers. Remove duplicate UUID schema from getChatMessagesHandler, delegate validation to validateChatAccess, standardize response format with status field, and extract repeated error message into constant.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/arpit-migrate-memories-get-api

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.

Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/api/chats/`[id]/messages/route.ts:
- Around line 21-27: The GET route currently forwards the request to
getMemoriesHandler which returns a nested payload under a data property,
violating the flat-body guideline; either update getMemoriesHandler to return a
flat response object (e.g., root-level fields like memories, meta, error) or
unwrap its result in the GET function before creating the NextResponse so the
response body has root-level fields; locate the GET function and
getMemoriesHandler and change the return shape (or the wrapper in GET) so no {
data: ... } nesting remains.

In `@lib/memories/getMemoriesHandler.ts`:
- Around line 28-49: Wrap the awaited calls to validateChatAccess(...) and
selectMemories(...) in a try/catch inside getMemoriesHandler to catch thrown
dependency errors and return a JSON error response with CORS headers instead of
letting the exception bubble. Specifically, enclose the block that calls
validateChatAccess and selectMemories in try { ... } and in catch (err) return
NextResponse.json({ status: "error", error: "Failed to retrieve memories",
details: String(err) }, { status: 500, headers: getCorsHeaders() }); optionally
log the error before returning to aid debugging, so the handler always respects
the JSON+CORS response contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5d1af798-3ace-48e4-8cf7-1905a26b9c17

📥 Commits

Reviewing files that changed from the base of the PR and between 4afc4cf and ffccf81.

⛔ Files ignored due to path filters (1)
  • lib/memories/__tests__/getMemoriesHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (2)
  • app/api/chats/[id]/messages/route.ts
  • lib/memories/getMemoriesHandler.ts

Copy link
Copy Markdown

@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.

♻️ Duplicate comments (1)
lib/chats/getChatMessagesHandler.ts (1)

34-55: ⚠️ Potential issue | 🟠 Major

Wrap dependency calls to preserve the JSON+CORS error contract.

If validateChatAccess(...) or selectMemories(...) throws, the handler can bypass your structured JSON+CORS responses and bubble an unhandled 500.

Suggested hardening
 export async function getChatMessagesHandler(
   request: NextRequest,
   id: string,
 ): Promise<NextResponse> {
-  const parsedId = chatIdSchema.safeParse(id);
-  if (!parsedId.success) {
-    return NextResponse.json(
-      {
-        status: "error",
-        error: parsedId.error.issues[0]?.message || "Invalid chat ID",
-      },
-      { status: 400, headers: getCorsHeaders() },
-    );
-  }
-
-  const roomResult = await validateChatAccess(request, parsedId.data);
-  if (roomResult instanceof NextResponse) {
-    return roomResult;
-  }
-
-  const memories = await selectMemories(roomResult.room.id, { ascending: true });
-  if (memories === null) {
+  try {
+    const parsedId = chatIdSchema.safeParse(id);
+    if (!parsedId.success) {
+      return NextResponse.json(
+        {
+          status: "error",
+          error: parsedId.error.issues[0]?.message || "Invalid chat ID",
+        },
+        { status: 400, headers: getCorsHeaders() },
+      );
+    }
+
+    const roomResult = await validateChatAccess(request, parsedId.data);
+    if (roomResult instanceof NextResponse) {
+      return roomResult;
+    }
+
+    const memories = await selectMemories(roomResult.room.id, { ascending: true });
+    if (memories === null) {
+      return NextResponse.json(
+        {
+          status: "error",
+          error: "Failed to retrieve memories",
+        },
+        { status: 500, headers: getCorsHeaders() },
+      );
+    }
+
     return NextResponse.json(
       {
-        status: "error",
-        error: "Failed to retrieve memories",
+        data: memories,
       },
-      { status: 500, headers: getCorsHeaders() },
+      { status: 200, headers: getCorsHeaders() },
     );
+  } catch {
+    return NextResponse.json(
+      {
+        status: "error",
+        error: "Internal server error",
+      },
+      { status: 500, headers: getCorsHeaders() },
+    );
   }
-
-  return NextResponse.json(
-    {
-      data: memories,
-    },
-    { status: 200, headers: getCorsHeaders() },
-  );
 }

As per coding guidelines, "For domain functions, ensure: ... Proper error handling".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/chats/getChatMessagesHandler.ts` around lines 34 - 55, The handler
currently calls validateChatAccess(...) and selectMemories(...) directly so any
thrown exception can escape and bypass your JSON+CORS error responses; wrap both
calls in try/catch blocks inside getChatMessagesHandler (or the function that
contains this snippet) so that if validateChatAccess or selectMemories throws
you return a well-formed NextResponse.json({ status: "error", error: "<brief
message or error.message>" }, { status: 500, headers: getCorsHeaders() });
preserve existing behavior for NextResponse returns from validateChatAccess
(i.e., if roomResult instanceof NextResponse return it), and include the same
getCorsHeaders() in all error responses.
🧹 Nitpick comments (1)
lib/chats/getChatMessagesHandler.ts (1)

8-24: Avoid duplicate chat ID validation to keep this handler DRY.

id is validated here and then validated again in validateChatAccess(...) (see lib/chats/validateChatAccess.ts:25-33). Consider centralizing this in one place to avoid drift in validation behavior/messages over time.

As per coding guidelines, "Extract shared logic into reusable utilities following Don't Repeat Yourself (DRY) principle".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/chats/getChatMessagesHandler.ts` around lines 8 - 24, This handler
duplicates ID validation—remove the chatIdSchema.safeParse block from
getChatMessagesHandler and delegate validation to the existing
validateChatAccess(id, request) utility; call validateChatAccess with the raw
id, handle/return its error response as before, and remove any references to
chatIdSchema or parsedId in this file so all chat ID validation lives in
validateChatAccess (preserving its error messages/behavior).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@lib/chats/getChatMessagesHandler.ts`:
- Around line 34-55: The handler currently calls validateChatAccess(...) and
selectMemories(...) directly so any thrown exception can escape and bypass your
JSON+CORS error responses; wrap both calls in try/catch blocks inside
getChatMessagesHandler (or the function that contains this snippet) so that if
validateChatAccess or selectMemories throws you return a well-formed
NextResponse.json({ status: "error", error: "<brief message or error.message>"
}, { status: 500, headers: getCorsHeaders() }); preserve existing behavior for
NextResponse returns from validateChatAccess (i.e., if roomResult instanceof
NextResponse return it), and include the same getCorsHeaders() in all error
responses.

---

Nitpick comments:
In `@lib/chats/getChatMessagesHandler.ts`:
- Around line 8-24: This handler duplicates ID validation—remove the
chatIdSchema.safeParse block from getChatMessagesHandler and delegate validation
to the existing validateChatAccess(id, request) utility; call validateChatAccess
with the raw id, handle/return its error response as before, and remove any
references to chatIdSchema or parsedId in this file so all chat ID validation
lives in validateChatAccess (preserving its error messages/behavior).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0344aff3-37fd-4c45-b47e-794ab00986ba

📥 Commits

Reviewing files that changed from the base of the PR and between ffccf81 and 7936b16.

⛔ Files ignored due to path filters (1)
  • lib/chats/__tests__/getChatMessagesHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (2)
  • app/api/chats/[id]/messages/route.ts
  • lib/chats/getChatMessagesHandler.ts
✅ Files skipped from review due to trivial changes (1)
  • app/api/chats/[id]/messages/route.ts

@arpitgupta1214
Copy link
Copy Markdown
Collaborator Author

Quick smoke test run against this PR preview deployment.

Preview URL: https://recoup-api-git-codex-arpit-migrate-m-af41dc-recoupable-ad724970.vercel.app
Auth: Authorization Bearer token

Results:

  • POST /api/chats with {} -> 200, chat created (fdd8829c-1922-4575-ba5a-804123cf3825)
  • GET /api/chats/fdd8829c-1922-4575-ba5a-804123cf3825/messages -> 200, { "data": [] }
  • GET /api/chats/not-a-uuid/messages -> 400, { "status": "error", "error": "id must be a valid UUID" }

Smoke verdict: endpoint is reachable and behaves as expected for valid + invalid ID paths.

Comment on lines +24 to +38
const parsedId = chatIdSchema.safeParse(id);
if (!parsedId.success) {
return NextResponse.json(
{
status: "error",
error: parsedId.error.issues[0]?.message || "Invalid chat ID",
},
{ status: 400, headers: getCorsHeaders() },
);
}

const roomResult = await validateChatAccess(request, parsedId.data);
if (roomResult instanceof NextResponse) {
return roomResult;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Move auth and param parsing to a standalone validate function following existing API endpoints.

@sweetmantech
Copy link
Copy Markdown
Contributor

Endpoint Verification

Tested GET /api/chats/{id}/messages on preview deployment:

curl -H "x-api-key: ***" \
  "https://recoup-api-git-codex-arpit-migrate-m-af41dc-recoupable-ad724970.vercel.app/api/chats/1f2632cc-4ee3-4b06-8a74-4c89dfc0f317/messages"

Result: ✅ 200 OK — returns { data: [...] } with messages in chronological order.

Response shape matches docsGetChatMessagesResponse schema expects { data: ChatMessage[] }.

Each message contains:

  • id (UUID)
  • room_id (UUID)
  • content (object with role, parts, content)
  • updated_at (timestamp)

Review feedback: Auth + param parsing should be extracted to a validateGetChatMessagesQuery.ts file per project conventions.

Move auth + param parsing to standalone validate function.
Handler now calls validateGetChatMessagesQuery then selectMemories.

1670/1670 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sweetmantech sweetmantech merged commit 9955e4a into test Apr 1, 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.

2 participants