Is it possible with Convex and its typing system to have general internal functions that apply on every table? Like, you know, find or create or update or delete that you can essentially call on every table so that you can call like internal.update or internal.find and then you can pass in a table and an ID and it is type-safe and it will return that. Is that possible somehow?
#Functions for all tables
5 messages · Page 1 of 1 (latest)
Hi @timber glade. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
There isn’t an out‑of‑the‑box “generic internal.update / internal.find for all tables” API documented for Convex, but the building blocks are there to get close with good type safety.
What Convex does give you:
- A generated
DataModeltype and helpers likeDoc<TableName>andId<TableName>once you define a schema. [TS schema types] - Strongly typed
ctx.dbmethods that are generic over table names:insert,patch,replace,delete,get,query, etc. [Writing data; DB writer API] - The ability to write internal functions (
internalQuery,internalMutation) and helpers that are parameterized byTableNameandId<TableName>. [Internal functions; Helper example]
From the docs and examples, there are two main patterns:
1. Generic helpers inside Convex functions
You can define helpers that are generic over table names and IDs, and call them from your internal/public functions. For example:
import { Id, Doc } from "./_generated/dataModel";
import { MutationCtx, QueryCtx } from "./_generated/server";
async function getById<TableName extends keyof DataModel>(
ctx: QueryCtx,
table: TableName,
id: Id<TableName>
): Promise<Doc<TableName> | null> {
return ctx.db.get(table, id);
}
async function deleteById<TableName extends keyof DataModel>(
ctx: MutationCtx,
table: TableName,
id: Id<TableName>
): Promise<void> {
await ctx.db.delete(table, id);
}
This pattern is explicitly encouraged for “splitting up query code via helpers”; helpers are not exported as Convex functions, but they are fully type‑safe and reusable across all tables. [Helper example; TS best practices]
You can then wrap these helpers in specific internal functions if you want them callable via internal.*.
2. CRUD utilities per table (convex-helpers)
The Convex team’s convex-helpers package shows a pattern where you generate CRUD functions per table:
// convex/users.ts
import { crud } from "convex-helpers/server/crud";
import schema from "./schema.js";
export const { create, read, update, destroy } = crud(schema, "users");
These become internal.users.read, internal.users.update, etc., and are type‑safe for that table. [CRUD example]
This is per table, not a single internal.update for all tables, but it demonstrates how the type system is used to generate strongly typed operations from the schema.
What you can’t see in the docs
The knowledge sources do not show a way to register a single Convex function like internal.update that is generically typed over all tables and exposed through the generated internal API while still preserving per‑table type narrowing (i.e., “if I pass table "tasks", args.id must be Id<"tasks"> and the return type is Doc<"tasks">). The docs don’t describe such a pattern or helper. I can’t confirm that this is supported at the function‑registration level from the provided sources.
Practical takeaway
- Yes: You can write generic helpers that operate on any table with strong typing, and then wrap them in internal functions as needed.
- Yes, per table: You can generate CRUD‑style internal functions per table (e.g. via
convex-helpers’crud). - Not documented: A single global
internal.update/internal.findfunction that is generically typed over all tables and exposed once in the generatedinternalAPI is not described in the docs we have.