allen
allen13mo ago

Has there been any consideration to

Has there been any consideration to adding v.partial? The behavior essentially would be v.object but to wrap all children in v.optional. My typical use case is something like:
//define a table shape:
export const myTableFields = {
field1: v.number(),
field2: v.string()
};
//make the table
export default defineTable(myTableFields);

//expose a partial update mutation
export default internalMutation({
args: {
id: v.id('myTable'),
updates: v.object({
field1: v.optional(v.number()),
field2: v.optional(v.string())
}),
},
async handler(ctx, args) {
await ctx.db.patch(args.id, args.updates);
},
});
//define a table shape:
export const myTableFields = {
field1: v.number(),
field2: v.string()
};
//make the table
export default defineTable(myTableFields);

//expose a partial update mutation
export default internalMutation({
args: {
id: v.id('myTable'),
updates: v.object({
field1: v.optional(v.number()),
field2: v.optional(v.string())
}),
},
async handler(ctx, args) {
await ctx.db.patch(args.id, args.updates);
},
});
If I was typing args normally, I would use Partial<...> to define this. Likewise, it would helpful to do v.partial(myTableFields) to not have to redefine each field in my mutation args.
6 Replies
allen
allenOP13mo ago
I understand I could introduce a helper util to perform this for me, but curious the interest of having it be officially supported. I'm also wondering about a v.doc that has a similar interface of v.id but returns the entire document schema.
Michal Srb
Michal Srb13mo ago
Definitely things we could add to core or to convex-helpers. Usual advice applies: 1. You probably want to specify your API manually, so that changing your schema doesn't automatically change your API, perhaps in a way you didn't predict 2. You can pass around IDs and load documents when needed, this way they are the "freshest" versions (for example when scheduling an action) But we know that in some scenarios following this advice can be onerous.
allen
allenOP13mo ago
yeah, on #2 I specifically don't want freshness in this case. I want to pass a document via a scheduler as it existed at the time of the operation execution. v.doc would be less of a need if extraneous fields didnt throw argument validators, but at the same time pass typesafety checks. eg:
args: {
docWithoutSystemFields: ...
}
args: {
docWithoutSystemFields: ...
}
passes type checks of:
{docWithoutSystemFields: doc as Doc<...>}
{docWithoutSystemFields: doc as Doc<...>}
but the arg validator will throw because _id and _creationTime are present. Seems to me either the argument validator needs to be relaxed to ignore or strip extraneous fields or the type checking needs to be more strict so this can be caught at build time instead of run time.
Michal Srb
Michal Srb13mo ago
Another option you have is to not use argument validation for the scheduled call (which is OK if you keep the scheduled action an internalAction).
ian
ian13mo ago
@allen I just published convex-helpers 0.1.19 that includes a partial helper (that gets the types right). Along with pick, omit, nullable, deprecated, literals, brandedString, and a Table utility that gives you a .doc: https://www.npmjs.com/package/convex-helpers#validator-utilities
npm
convex-helpers
A collection of useful code to complement the official convex package.. Latest version: 0.1.19, last published: 3 minutes ago. Start using convex-helpers in your project by running npm i convex-helpers. There are no other projects in the npm registry using convex-helpers.
ian
ian13mo ago
Thanks for the nudge - I've been meaning to put this out. I'll write a stack post soon I hope

Did you find this page helpful?