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.
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.
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
])