Type Inference
Infer input and output types from your Convex API.
In this guide, we'll explore type inference in cRPC. You'll learn to extract input and output types from your API for use in components, utilities, and type-safe operations.
Overview
cRPC provides type inference utilities similar to tRPC:
| Type | Description |
|---|---|
ApiOutputs | Return types of all procedures |
ApiInputs | Input/argument types of all procedures |
Api | Combined API type with HTTP router |
Let's set it up.
import type {
inferApiInputs,
inferApiOutputs,
WithHttpRouter,
} from 'better-convex/server';
import type { api } from './_generated/api';
import type { appRouter } from './http'; // optional, if using HTTP routes
// WithHttpRouter combines API with HTTP router (http is optional)
export type Api = WithHttpRouter<typeof api, typeof appRouter>;
export type ApiInputs = inferApiInputs<Api>;
export type ApiOutputs = inferApiOutputs<Api>;Usage
Access nested procedure types using bracket notation:
import type { ApiInputs, ApiOutputs } from '@convex/types';
// Output types
type User = ApiOutputs['user']['get'];
type UserList = ApiOutputs['user']['list'];
type Post = ApiOutputs['post']['get'];
// Input types
type GetUserArgs = ApiInputs['user']['get'];
type CreatePostArgs = ApiInputs['post']['create'];In Components
Use output types for component props:
import type { ApiOutputs } from '@convex/types';
type User = ApiOutputs['user']['get'];
function UserProfile({ user }: { user: User }) {
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}In Utility Functions
Use both input and output types for type-safe utilities:
import type { ApiInputs, ApiOutputs } from '@convex/types';
type User = ApiOutputs['user']['get'];
type UpdateUserArgs = ApiInputs['user']['update'];
function formatUserName(user: User): string {
return user.name ?? 'Anonymous';
}
function validateUpdateArgs(args: UpdateUserArgs): boolean {
return !!args.name || !!args.email;
}Deep Nested Types
For deeply nested APIs, chain bracket notation:
// api.organization.members.list
type OrgMember = ApiOutputs['organization']['members']['list'][number];
// api.project.settings.get
type ProjectSettings = ApiOutputs['project']['settings']['get'];Tip: Use [number] to extract the item type from array return types.
Migrate from Convex
If you're coming from vanilla Convex, here's what changes.
What stays the same
- Type inference from function definitions
- Full TypeScript support
What's new
Before (vanilla Convex):
import type { FunctionReturnType, FunctionArgs } from 'convex/server';
import { api } from '@convex/_generated/api';
type User = FunctionReturnType<typeof api.user.get>;
type GetUserArgs = FunctionArgs<typeof api.user.get>;After (cRPC):
import type { ApiInputs, ApiOutputs } from '@convex/types';
type User = ApiOutputs['user']['get'];
type GetUserArgs = ApiInputs['user']['get'];Key differences:
| Feature | Description |
|---|---|
| Bracket notation | Cleaner than typeof api.path |
| Centralized types | All definitions in one file |
| Consistent pattern | Same across all procedures |
| Nested namespaces | Works with deep nesting |