jeevanpillay
jeevanpillay3mo ago

optimistic updates but multiple documents at once

I'm creating a new chat thread + assocaited user message + the assitant reply. We are creating a new thread + user + assistant message. The thread is opimisitcally perfectly fine. But the useQuery for the message.list doens't have the information immediately.
// Mutations with proper Convex optimistic updates
const createThreadAndSend = useMutation(
api.messages.createThreadAndSend,
).withOptimisticUpdate((localStore, args) => {
const { title, clientId, body, modelId } = args
const now = Date.now()

// Create optimistic thread with a temporary ID that looks like a Convex ID
// This will be replaced by the real thread ID when the mutation completes
// Use a format that starts with 'k' to pass our optimistic ID checks
const optimisticThreadId = crypto.randomUUID() as Id<"threads">

// Create optimistic thread for sidebar display and message association
const optimisticThread: Partial<Doc<"threads">> & {
_id: Id<"threads">
clientId: string
} = {
_id: optimisticThreadId,
_creationTime: now,
clientId,
title,
userId: "optimistic" as Id<"users">,
createdAt: now,
lastMessageAt: now,
isTitleGenerating: true,
isGenerating: true,
}

// Get existing threads from the store
const existingThreads = localStore.getQuery(api.threads.list, {}) || []

// Add the new thread at the beginning of the list for sidebar display
localStore.setQuery(api.threads.list, {}, [
optimisticThread as Doc<"threads">,
...existingThreads,
])

// Also set the thread by clientId so it can be found while optimistic
localStore.setQuery(
api.threads.getByClientId,
{ clientId },
optimisticThread as Doc<"threads">,
)

// Create optimistic user message
const optimisticUserMessage: Doc<"messages"> = {
_id: crypto.randomUUID() as Id<"messages">,
_creationTime: now,
threadId: optimisticThreadId,
body,
messageType: "user",
modelId,
timestamp: now,
isStreaming: false,
isComplete: true,
}

// Create optimistic assistant message placeholder
const optimisticAssistantMessage: Doc<"messages"> = {
_id: crypto.randomUUID() as Id<"messages">,
_creationTime: now + 1,
threadId: optimisticThreadId,
body: "", // Empty body for streaming
messageType: "assistant",
modelId,
timestamp: now + 1,
isStreaming: true,
isComplete: false,
streamId: `stream_${clientId}_${now}`,
thinkingStartedAt: now,
}

// Set optimistic messages for this thread
// We use the optimistic thread ID here, which will be replaced when the real data arrives
// Messages are returned in descending order (newest first) by the backend
localStore.setQuery(api.messages.list, { threadId: optimisticThreadId }, [
optimisticAssistantMessage, // Assistant message has timestamp now + 1
optimisticUserMessage, // User message has timestamp now
])
})
// Mutations with proper Convex optimistic updates
const createThreadAndSend = useMutation(
api.messages.createThreadAndSend,
).withOptimisticUpdate((localStore, args) => {
const { title, clientId, body, modelId } = args
const now = Date.now()

// Create optimistic thread with a temporary ID that looks like a Convex ID
// This will be replaced by the real thread ID when the mutation completes
// Use a format that starts with 'k' to pass our optimistic ID checks
const optimisticThreadId = crypto.randomUUID() as Id<"threads">

// Create optimistic thread for sidebar display and message association
const optimisticThread: Partial<Doc<"threads">> & {
_id: Id<"threads">
clientId: string
} = {
_id: optimisticThreadId,
_creationTime: now,
clientId,
title,
userId: "optimistic" as Id<"users">,
createdAt: now,
lastMessageAt: now,
isTitleGenerating: true,
isGenerating: true,
}

// Get existing threads from the store
const existingThreads = localStore.getQuery(api.threads.list, {}) || []

// Add the new thread at the beginning of the list for sidebar display
localStore.setQuery(api.threads.list, {}, [
optimisticThread as Doc<"threads">,
...existingThreads,
])

// Also set the thread by clientId so it can be found while optimistic
localStore.setQuery(
api.threads.getByClientId,
{ clientId },
optimisticThread as Doc<"threads">,
)

// Create optimistic user message
const optimisticUserMessage: Doc<"messages"> = {
_id: crypto.randomUUID() as Id<"messages">,
_creationTime: now,
threadId: optimisticThreadId,
body,
messageType: "user",
modelId,
timestamp: now,
isStreaming: false,
isComplete: true,
}

// Create optimistic assistant message placeholder
const optimisticAssistantMessage: Doc<"messages"> = {
_id: crypto.randomUUID() as Id<"messages">,
_creationTime: now + 1,
threadId: optimisticThreadId,
body: "", // Empty body for streaming
messageType: "assistant",
modelId,
timestamp: now + 1,
isStreaming: true,
isComplete: false,
streamId: `stream_${clientId}_${now}`,
thinkingStartedAt: now,
}

// Set optimistic messages for this thread
// We use the optimistic thread ID here, which will be replaced when the real data arrives
// Messages are returned in descending order (newest first) by the backend
localStore.setQuery(api.messages.list, { threadId: optimisticThreadId }, [
optimisticAssistantMessage, // Assistant message has timestamp now + 1
optimisticUserMessage, // User message has timestamp now
])
})
I am wondering if Convex's sync engine is smart enough to automatically remap the optimstic Id's created. I'm struggling to get this implemntation right (new to convex), and particular whether convex is smart enough to remap the optimisticThreadId setter as seen below.
localStore.setQuery(api.messages.list, { threadId: optimisticThreadId }, [
optimisticAssistantMessage, // Assistant message has timestamp now + 1
optimisticUserMessage, // User message has timestamp now
])
localStore.setQuery(api.messages.list, { threadId: optimisticThreadId }, [
optimisticAssistantMessage, // Assistant message has timestamp now + 1
optimisticUserMessage, // User message has timestamp now
])
2 Replies
Convex Bot
Convex Bot3mo ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
jeevanpillay
jeevanpillayOP3mo ago
okay figured it out. solution: dont query message.list with real convex thread id but using our pre-generated client-id. note, we use pre-generated client-id to achieve instant routing.

Did you find this page helpful?