Skip to content

List Chats

Metadata

  • System type: flow

System Intent

  • What this is: Fetches a paginated list of the authenticated user's past AI chat conversations (most-recently-updated first) and surfaces them in the side menu.

Mermaid Diagram

flowchart TD
  SideMenu["SideMenu (index.tsx)"] -->|useChatsFeed limit=12| useChatsFeed
  useChatsFeed -->|fetchChats params cursor string| fetchChats
  fetchChats -->|ZodSchema validate| FetchChatsParamsSchema
  fetchChats -->|POST /chats/list| ChatsListLambda["chats-list Lambda"]
  ChatsListLambda -->|Cognito JWT auth| CognitoAuthorizer
  ChatsListLambda -->|DynamoDB query user+updatedAt| DynamoDB["DynamoDB Chats table"]
  ChatsListLambda -->|conversations + next_cursor| fetchChats
  fetchChats -->|FetchChatsResponse| useChatsFeed
  useChatsFeed -->|getNextPageParam String coerce| useChatsFeed
  useChatsFeed -->|pages| SideMenu

Flows

Flow: listChats

  • Test files: main/app/__tests__/chats-list-api.test.ts, main/app/__tests__/use-chats-api.test.ts
  • Core files: main/app/lib/api/chats/useChatsApi.ts, main/app/lib/api/chats/fetchChats.ts, main/server/api/chats/list/app.py

Types

FetchChatsParams {
  chat_id: string | null (optional)
  cursor: string | null (optional — DynamoDB LastEvaluatedKey, must be string)
  limit: number (optional, default 20, max 50)
}

ChatSummary {
  id: string
  title: string
  last_message: string (relative date label: "Today", "Yesterday", weekday, or "Mon D")
  updated_at: string (ISO timestamp)
}

FetchChatsResponse {
  conversations: ChatSummary[]
  next_cursor: string | null
}

Paths

path input output path-type notes
listChats.success FetchChatsParams FetchChatsResponse happy path Conversations ordered by updatedAt desc
listChats.paginated FetchChatsParams cursor=<string> FetchChatsResponse happy path cursor coerced to string before Zod validation
listChats.empty FetchChatsParams FetchChatsResponse conversations=[] happy path No conversations for user
listChats.forbidden FetchChatsParams FetchChatsResponse conversations=[] error 403 from Lambda returns empty list silently
listChats.error any conversationsErrorMessage="Something went wrong. Please try again." error Raw error logged to console.error; UI shows static string

Pseudocode

// useChatsFeed — cursor coercion (prevents Zod invalid_type errors)
queryFn({ pageParam }) {
  rawCursor = pageParam ?? params.cursor ?? null
  cursor = rawCursor != null ? String(rawCursor) : null
  return fetchChats({ ...params, cursor })
}
getNextPageParam(lastPage) {
  return lastPage.next_cursor != null ? String(lastPage.next_cursor) : undefined
}

// index.tsx — error display
if (isConversationsError) {
  console.error("[conversations] failed to load:", conversationsError)
}
conversationsErrorMessage = isConversationsError
  ? "Something went wrong. Please try again."
  : undefined

The cursor coercion guards against next_cursor arriving as a non-string (e.g. number) from the server. Without coercion, FetchChatsParamsSchema.parse throws a Zod invalid_type error whose .message is a JSON array string. See docs/bugs/2026-05-07-conversations-cursor-object-invalid-type.md.

Logs

Source Location
Conversations load error console.error in main/app/app/index.tsx (prefixed [conversations] failed to load:)
Chats feed query step: "chats_feed_query_requested" via useLogging
Chat messages query step: "chat_messages_query_requested" via useLogging

Deployment

  • Mechanism: SAM
  • Deploy command:
    # Deploy chats-list Lambda via SAM from repo root
    sam deploy
    
  • Notes: Lambda requires CHATS_TABLE_NAME (default Chats) and MESSAGES_TABLE_NAME (default Messages) env vars. API Gateway uses Cognito JWT authorizer.