#How do I specify a function return value that is a Doc object?

1 messages · Page 1 of 1 (latest)

sand apex
#

How do I specify a function return value that is a Doc object? I want to specify that a function returns an array of Doc<"users">, but without manually having to add the system _id and _creationTime fields in a union.

This is what I don't want to do because it's convoluted:

export const listPaginated = query({
  args: {
    paginationOpts: paginationOptsValidator,
    searchQuery: v.optional(v.string()),
    listId: v.optional(v.id("contactLists")),
  },
  returns: v.object({
    page: v.array(
      v
        .object({
          _id: v.id("contacts"),
          _creationTime: v.number(),
        })
        .extend(ContactSchema.contacts.validator.fields)
    ),
    continueCursor: v.union(v.string(), v.record(v.string(), v.null())),
    isDone: v.boolean(),
  }),

If I JUST use the validator (ContactSchema.contacts.validator), I get an error that my return includes _id and _creationTime fields, which I actually do want.

The following code is incorrect, but illustrative of what I want to do:

export const listPaginated = query({
  args: {
    paginationOpts: paginationOptsValidator,
    searchQuery: v.optional(v.doc("users")), // imaginary v.doc() function
    listId: v.optional(v.id("contactLists")),
  },
  returns: v.object({
    page: v.array(v.),
    continueCursor: v.union(v.string(), v.record()),
    isDone: v.boolean(),
  }),
wild crowBOT
#

Hi @sand apex. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

To specify a function return value that is an array of Doc<"users"> (i.e., a full Convex document including the system fields like _id and _creationTime), you do not need to manually construct the validator by extending the fields and adding the system fields yourself.

Recommended Approach

You can use helper utilities from the convex-helpers package, specifically the doc validator, which creates a validator for a full document (including system fields) for a given table. Here’s how you can do it:

import { doc } from "convex-helpers/validators";
import schema from "./schema";

export const listPaginated = query({
  args: {
    paginationOpts: paginationOptsValidator,
    searchQuery: v.optional(v.string()),
    listId: v.optional(v.id("contactLists")),
  },
  returns: v.object({
    page: v.array(doc(schema, "users")),
    continueCursor: v.string(),
    isDone: v.boolean(),
  }),
  // handler...
});

This way, doc(schema, "users") gives you a validator for a full user document, including _id and _creationTime, without manual union or extension. This is much cleaner and automatically stays in sync with your schema if you change it later.

Alternative: Using withSystemFields

If you want to construct the validator yourself, you can use the withSystemFields helper:

import { withSystemFields } from "convex-helpers/validators";
import schema from "./schema";

const userWithSystemFields = withSystemFields("users", schema.tables.users.validator.fields);

export const listPaginated = query({
  // ...
  returns: v.object({
    page: v.array(v.object(userWithSystemFields)),
    continueCursor: v.string(),
    isDone: v.boolean(),
  }),
});

Both approaches avoid the need to manually add _id and _creationTime fields and keep your code DRY and type-safe.

#

Note: The built-in validator at schema.tables.users.validator does not include system fields by default, so you need to use one of the helpers above to get a full document validator with system fields included.
Helpers for schema definition & validation


Summary:
Use doc(schema, "users") from convex-helpers/validators for a full document validator, or withSystemFields if you want to build it yourself. This keeps your return type in sync with your schema and includes system fields automatically.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: