erquhart
erquhart2y ago

crypto.randomUUID() not working as _id in optimistic update

Following the docs, I'm attempting to use crypto.randomUUID() to generate an id for an optimistic update. This produces the following error:
Types of property '_id' are incompatible.
Type '`${string}-${string}-${string}-${string}-${string}`' is not assignable to type 'Id<"reserves">'.
Type '`${string}-${string}-${string}-${string}-${string}`' is not assignable to type '{ __tableName: "reserves"; }'.ts(2322)
Types of property '_id' are incompatible.
Type '`${string}-${string}-${string}-${string}-${string}`' is not assignable to type 'Id<"reserves">'.
Type '`${string}-${string}-${string}-${string}-${string}`' is not assignable to type '{ __tableName: "reserves"; }'.ts(2322)
Guessing this stopped working when ids became strings in 0.17.0, but that's just a guess. In case it helps, here's the optimistic update function body:
const existingReserves = localStore.getQuery(api.budget.getReserves, {
budgetId,
})
if (existingReserves !== undefined) {
const now = Date.now()
const newReserve = {
_id: crypto.randomUUID(),
_creationTime: now,
budgetId: args.budgetId,
}
localStore.setQuery(api.budget.getReserves, { budgetId }, [
...existingReserves,
newReserve,
])
}
const existingReserves = localStore.getQuery(api.budget.getReserves, {
budgetId,
})
if (existingReserves !== undefined) {
const now = Date.now()
const newReserve = {
_id: crypto.randomUUID(),
_creationTime: now,
budgetId: args.budgetId,
}
localStore.setQuery(api.budget.getReserves, { budgetId }, [
...existingReserves,
newReserve,
])
}
9 Replies
erquhart
erquhartOP2y ago
Looks like I can cast it to Id<'reserves'>
sshader
sshader2y ago
Yeah it needs a type cast _id: crypto.randomUUID() as Id<"reserves"> (most of our doc samples are in JS at the moment, and I debated how to best document this, because it's definitely confusing)
erquhart
erquhartOP2y ago
Ah! Never noticed. This line indicates a function for generating id's, is that something we can import?
Similarly, the update creates a temporary Id with new Id("messages", crypto.randomUUID()).
Similarly, the update creates a temporary Id with new Id("messages", crypto.randomUUID()).
sshader
sshader2y ago
Oops that's out of date, so thank you for pointing that out -- will fix! Currently there's no way of creating IDs from clients, but we'd probably like to add this down the road. And on top of that allow clients to specify the IDs used by the server-side Convex functions so you can do compound optimistic updates like "create a user with ID X" followed by "set the name of user with ID X to sarah" and have it all work
erquhart
erquhartOP2y ago
That would be awesome! Thanks for confirming that casting is the expected approach here 👍 @sshader actually it looks like randomUUID isn't there. This is discussed in a convo from April: https://discord.com/channels/1019350475847499849/1093536834899951668/1093544068702814208 Is there a recommended workaround?
sshader
sshader2y ago
Ah in React Native? All we need is a unique string for the lifetime of the optimistic update, so if you have a favorite library for creating unique strings you can use that (e.g. found https://www.npmjs.com/package/react-native-uuid through googling and have never used it). The answer in https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid/2117523#2117523 might also work since I think our setup instructions suggest adding getRandomValues
npm
react-native-uuid
react-native-uuid is a zero-dependency TypeScript implementation of RFC4122.. Latest version: 2.0.1, last published: 2 years ago. Start using react-native-uuid in your project by running npm i react-native-uuid. There are 189 other projects in the npm registry using react-native-uuid.
Stack Overflow
How do I create a GUID / UUID?
How do I create GUIDs (globally-unique identifiers) in JavaScript? The GUID / UUID should be at least 32 characters and should stay in the ASCII range to avoid trouble when passing them around. I'm...
erquhart
erquhartOP2y ago
Okay cool, wasn't sure if there were other requirements besides uniqueness. Thank you!
fawwaz
fawwaz2y ago
hey @sshader not sure if the example in the docs is still outdated but I tried to follow it and I keep hitting an error. do you see what I'm doing wrong
export const ImageGenerationConvexModelDefinition = v.object({
id: v.id("imageGenerationTasks"),
prompt: v.string(),
replicateUlr: v.optional(v.string()),
outputStorageId: v.optional(v.string()),
createdTime: v.number(),
});
export type ImageGenerationTask = Infer<typeof ImageGenerationConvexModelDefinition>;

export const createImageGenerationTask = (args: PartialBy<ImageGenerationTask, "id" | "createdTime">): ImageGenerationTask => {
return {
id: args.id ?? (crypto.randomUUID() as Id<"imageGenerationTasks">),
createdTime: args.createdTime ?? Date.now(),
...args,
};
};
export const ImageGenerationConvexModelDefinition = v.object({
id: v.id("imageGenerationTasks"),
prompt: v.string(),
replicateUlr: v.optional(v.string()),
outputStorageId: v.optional(v.string()),
createdTime: v.number(),
});
export type ImageGenerationTask = Infer<typeof ImageGenerationConvexModelDefinition>;

export const createImageGenerationTask = (args: PartialBy<ImageGenerationTask, "id" | "createdTime">): ImageGenerationTask => {
return {
id: args.id ?? (crypto.randomUUID() as Id<"imageGenerationTasks">),
createdTime: args.createdTime ?? Date.now(),
...args,
};
};
then inside the mutation handler
export const saveImageGenerationTask = internalMutation({
args: {
args: ImageGenerationConvexModelDefinition,
},
handler: async (ctx, args) => {
const { _id, ...rest } = ImageGenerationTaskToPromptAndResponse(args.args);
const storedImageGenerationTask = _id ? await ctx.db.get(_id) : null;
const newImageGenerationTask = { ...storedImageGenerationTask, ...rest };
const newId = _id ? await ctx.db.replace(_id, newImageGenerationTask) : await ctx.db.insert("imageGenerationTasks", newImageGenerationTask);
return (_id ?? newId) as Id<"imageGenerationTasks">;
},
});
export const saveImageGenerationTask = internalMutation({
args: {
args: ImageGenerationConvexModelDefinition,
},
handler: async (ctx, args) => {
const { _id, ...rest } = ImageGenerationTaskToPromptAndResponse(args.args);
const storedImageGenerationTask = _id ? await ctx.db.get(_id) : null;
const newImageGenerationTask = { ...storedImageGenerationTask, ...rest };
const newId = _id ? await ctx.db.replace(_id, newImageGenerationTask) : await ctx.db.insert("imageGenerationTasks", newImageGenerationTask);
return (_id ?? newId) as Id<"imageGenerationTasks">;
},
});
Optimistic Updates | Convex Developer Hub
Even though Convex queries are completely reactive, sometimes you'll want to
No description
Michal Srb
Michal Srb2y ago
Hey @fawwaz , as @sshader mentioned above: "Currently there's no way of creating IDs from clients, but we'd probably like to add this down the road." So in your code, you cannot pass a crypto.randomUUID() to the backend function which expects v.id("imageGenerationTasks"). To get a valid document ID you need to create a document on the backend. The doc you're referencing doesn't pass the randomUUID back to the backend, it's only used to "mock" a server response for the lifetime of the optimistic update - when the mutation finishes it will be replaced with a real document with a real ID.

Did you find this page helpful?