#Infer the type of the arguments to a mutation
1 messages · Page 1 of 1 (latest)
To infer the type of arguments to a Convex mutation in TypeScript, you have several options:
Using Validators with Automatic Type Inference
When you define a mutation with argument validation, TypeScript will automatically infer the argument types:
import { mutation } from "./_generated/server";
import { v } from "convex/values";
export const send = mutation({
args: {
body: v.string(),
author: v.string(),
},
handler: async (ctx, args) => {
// TypeScript knows that args is { body: string, author: string }
const { body, author } = args;
await ctx.db.insert("messages", { body, author });
},
});
This approach provides both runtime validation and compile-time type checking Functions > Argument and Return Value Validation.
Using the Infer Type Helper
You can extract TypeScript types from validators using the Infer type helper:
import { mutation } from "./_generated/server";
import { Infer, v } from "convex/values";
const messageArgs = v.object({
body: v.string(),
author: v.string(),
});
// This resolves to { body: string, author: string }
export type MessageArgs = Infer<typeof messageArgs>;
export const send = mutation({
args: messageArgs,
handler: async (ctx, args) => {
// args is typed as MessageArgs
await ctx.db.insert("messages", args);
},
});
This is useful when you want to reuse the same validator in multiple places Functions > Extracting TypeScript Types.
Using FunctionArgs Type
If you need to reference the argument type of an existing mutation elsewhere in your code, you can use the FunctionArgs type:
import { api } from "./_generated/api";
import { FunctionArgs } from "convex/server";
// This will be the type of arguments for the send mutation
type SendArgs = FunctionArgs<typeof api.messages.send>;
### Reusing Schema Validators
You can also define validators in your schema and reuse them for argument validation:
```typescript
// in convex/schema.ts
export const messageFields = {
body: v.string(),
author: v.string(),
};
// in convex/messages.ts
import { mutation } from "./_generated/server";
import { messageFields } from "./schema";
export const send = mutation({
args: messageFields,
handler: async (ctx, args) => {
// args is typed according to messageFields
return await ctx.db.insert("messages", args);
},
});
This approach helps maintain consistency between your schema and function arguments Stack > Argument Validation without Repetition.
By using these techniques, you can ensure type safety throughout your Convex application while avoiding duplication of type definitions.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@quasi iron Kapa.ai is still learning and improving, please let me know how I did by reacting below.
so i need to define the args outside of the mutation? @strong prawn
Let me check the knowledge base for you again. One moment! :books:
No, you don't need to define the args outside of the mutation. You have two options:
Option 1: Define args directly in the mutation (inline)
You can define the args directly within the mutation definition, which is the most common approach:
import { mutation } from "./_generated/server";
import { v } from "convex/values";
export const send = mutation({
args: {
body: v.string(),
author: v.string(),
},
handler: async (ctx, args) => {
// TypeScript automatically infers args as { body: string, author: string }
const { body, author } = args;
await ctx.db.insert("messages", { body, author });
},
});
With this approach, TypeScript will automatically infer the types of your arguments Functions > Argument and Return Value Validation.
Option 2: Define args separately (for reusability)
Defining args outside the mutation is optional but useful when you want to:
- Reuse the same validators across multiple functions
- Extract the type using
Infer - Share validators between your schema and functions
import { mutation } from "./_generated/server";
import { Infer, v } from "convex/values";
// Optional: define args separately for reuse
const messageArgs = {
body: v.string(),
author: v.string(),
};
// Optional: extract the type if needed elsewhere
export type MessageArgs = Infer<typeof messageArgs>;
export const send = mutation({
args: messageArgs, // Use the predefined args
handler: async (ctx, args) => {
await ctx.db.insert("messages", args);
},
});
Both approaches provide the same type safety and runtime validation. The choice depends on your specific needs for code organization and reusability.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@quasi iron Kapa.ai is still learning and improving, please let me know how I did by reacting below.
in the first example: how can i get the inferred type of the send arguments? @strong prawn
Let me check the knowledge base for you again. One moment! :books:
To get the inferred type of the send mutation arguments from the first example, you can use the FunctionArgs type from Convex. Here's how to do it:
import { api } from "./_generated/api";
import { FunctionArgs } from "convex/server";
// This extracts the argument type from the send mutation
type SendArgs = FunctionArgs<typeof api.messages.send>;
// Now you can use SendArgs elsewhere in your code
function processSendArgs(args: SendArgs) {
// ...
}
Alternatively, if you want to extract the type directly from the validator without referencing the API, you can use the Infer type helper:
import { Infer, v } from "convex/values";
// Define your validators (same as in your mutation)
const sendArgsValidator = {
body: v.string(),
author: v.string(),
};
// Extract the type
type SendArgs = Infer<typeof sendArgsValidator>;
The Infer type helper is particularly useful when you want to extract TypeScript types from your validators for use elsewhere in your codebase Stack > Types and Validators in TypeScript: A Convex Cookbook.
Both approaches give you the same result: a TypeScript type that matches the structure defined by your validators.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@quasi iron Kapa.ai is still learning and improving, please let me know how I did by reacting below.
how can i get the actual validator object? @strong prawn
Let me check the knowledge base for you again. One moment! :books:
To get the actual validator object from a Convex function, you have a few approaches depending on your specific use case:
1. Export the validator directly
If you define your validators separately, you can simply export them:
import { v } from "convex/values";
// Define and export the validator
export const sendArgsValidator = {
body: v.string(),
author: v.string(),
};
// Use it in your mutation
export const send = mutation({
args: sendArgsValidator,
handler: async (ctx, args) => {
// ...
},
});
2. Extract validators from schema
If you're using a schema-based approach, you can export field validators from your schema:
// in convex/schema.ts
import { v } from "convex/values";
export const recipeFields = {
name: v.string(),
course: v.union(v.literal("appetizer"), v.literal("main"), v.literal("dessert")),
ingredients: v.array(v.string()),
steps: v.array(v.string()),
};
// Then import in your function file
import { recipeFields } from "./schema";
This approach is recommended in Stack > Types and Validators in TypeScript: A Convex Cookbook to avoid repetition.
3. Use the Table helper from convex-helpers
For a more structured approach, you can use the Table helper from the convex-helpers package:
// convex/schema.ts
import { defineSchema } from "convex/server";
import { v } from "convex/values";
import { Table } from "convex-helpers/server"; // npm i convex-helpers
export const Recipes = Table("recipes", {
name: v.string(),
course: v.union(v.literal("appetizer"), v.literal("main"), v.literal("dessert")),
ingredients: v.array(v.string()),
steps: v.array(v.string()),
});
export default defineSchema({
recipes: Recipes.table.index("by_course", ["course"]),
});
Then you can access validators in different ways:
// Access validators with or without system fields
Recipes.withoutSystemFields; // user-defined field validators
Recipes.withSystemFields; // those validators plus _id and _creationTime
Recipes.doc; // v.object() validator for the table's docs
This approach is detailed in Stack > Argument Validation without Repetition.
These methods give you access to the actual validator objects that you can reuse across your codebase for consistent validation and type safety.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@quasi iron Kapa.ai is still learning and improving, please let me know how I did by reacting below.
it is currently a mutation @strong prawn
Let me check the knowledge base for you again. One moment! :books:
I understand you're working with a Convex mutation. Based on our previous conversation, you're looking to extract the validator object from an existing mutation.
If your mutation is defined inline like this:
export const send = mutation({
args: {
body: v.string(),
author: v.string(),
},
handler: async (ctx, args) => {
// ...
},
});
You have a few options to get the validator object:
1. Extract the args definition to a separate variable
The simplest approach is to refactor your code to define the args separately:
import { v } from "convex/values";
// Extract the validator object
export const sendArgsValidator = {
body: v.string(),
author: v.string(),
};
export const send = mutation({
args: sendArgsValidator, // Use the extracted validator
handler: async (ctx, args) => {
// ...
},
});
2. Create a helper function to reuse validators
If you need to reuse validators across multiple functions, you can create helper functions:
import { v } from "convex/values";
// Define a helper function that returns the validator
export function myReadHelper(ctx, id) {
// Use validators here
// ...
}
export const send = mutation({
args: {
body: v.string(),
author: v.string(),
},
handler: async (ctx, args) => {
// ...
},
});
This pattern is described in Type annotating server-side helpers.
Unfortunately, there's no direct way to access the validator object after it's been defined inline within a mutation. The best practice is to define your validators separately if you need to reuse them.
Is there a specific reason you need to extract the validator from an existing mutation rather than defining it separately?
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@quasi iron Kapa.ai is still learning and improving, please let me know how I did by reacting below.
is there a resolver similar to the zodresolver in @hookform for convex @strong prawn
Let me check the knowledge base for you again. One moment! :books:
Yes, Convex has integration options with Zod for validation similar to the zodResolver in React Hook Form. The convex-helpers package provides utilities for using Zod with Convex functions.
There are a few approaches you can use:
1. Using withZod wrapper
You can use the withZod wrapper from convex-helpers to validate your function inputs:
export default mutation(
withZod({
args: {
body: z.string(),
author: z.string(),
},
handler: async ({ db }, { body, author }) => {
const message = { body, author };
return await db.insert("messages", message);
}
})
);
This approach is detailed in Stack > Zod Validation: Wrappers as "Middleware".
2. Using zCustomQuery and similar functions
For a more integrated approach, you can use the zCustomQuery, zCustomMutation, or zCustomAction functions:
import { z } from "zod";
import { NoOp } from "convex-helpers/server/customFunctions";
import { zCustomQuery } from "convex-helpers/server/zod";
import { query } from "./_generated/server";
// Make this once, to use anywhere you would have used `query`
const zQuery = zCustomQuery(query, NoOp);
export const greeting = zQuery({
args: { name: z.string() },
handler: async (_ctx, args) => {
return `Hello ${args.name}`;
},
});
This is explained in Stack > Zod with TypeScript for Server-side Validation.
3. Using zid for Document IDs
For validating Convex Document IDs, you can use the zid helper:
import { zCustomQuery, zid } from "convex-helpers/server/zod";
const zQuery = zCustomQuery(query, NoOp)
export const getUser = zQuery({
args: {userId: zid("users")},
handler: async (ctx, args) => {
const user = await ctx.db.get(args.userId);
return user && { id: user._id, name: user.name };
},
});
This is shown in [Stack > Zod with TypeScript for Server-side Validation](<https://stack.convex.dev/typescript-zod-function-validation#vidtablename--zidtablename>).
You can install the necessary package with:
npm i convex-helpers@latest
These approaches provide similar functionality to zodResolver in React Hook Form, but for Convex server functions.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------