Typed Returns (auth.v)

Convex requires a returns: validator on every public function, and the client’s useQuery type is inferred from that validator. To keep consumers from hand-rolling DTO validators and casting query results, createAuth exposes ready-made validators on auth.v, and the package exports the matching document types.

The facade reads are fully typed end to end: auth.user.list returns { items: Doc<"User">[]; nextCursor: string | null } and auth.user.get / auth.user.viewer return Doc<"User"> | null — no casts required.

auth.v.*

ValidatorShape
auth.v.userSingle User document (extend-aware)
auth.v.groupSingle Group document (extend-aware)
auth.v.memberSingle GroupMember (extend-aware)
auth.v.inviteSingle GroupInvite document
auth.v.viewerUser \| null — current-user query
auth.v.list(item){ items: item[]; nextCursor: string \| null }
// convex/functions.ts
import { customQuery } from "convex-helpers/server/customFunctions";
import { query } from "./_generated/server";
import { auth } from "./auth";

export const authQuery = customQuery(query, auth.ctx());
// convex/users.ts
import { v } from "convex/values";
import { authQuery } from "./functions";
import { auth } from "./auth";

export const viewer = authQuery({
  returns: auth.v.viewer,
  handler: (ctx) => ctx.auth.user.viewer(ctx),
});

export const users = authQuery({
  returns: auth.v.list(auth.v.user),
  handler: (ctx) => ctx.auth.user.list(ctx),
});

Composing richer reads

There is no bespoke “viewer with groups” helper — compose the existing auth.user.viewer, auth.member.list, and auth.group.get facade methods and build the returns: validator from the building blocks.

export const groups = authQuery({
  returns: v.union(
    v.object({
      ...auth.v.user.fields,
      memberships: v.array(auth.v.member),
      groups: v.array(auth.v.group),
    }),
    v.null(),
  ),
  handler: async (ctx) => {
    const me = await ctx.auth.user.viewer(ctx);
    if (me === null) return null;
    const { items: memberships } = await ctx.auth.member.list(ctx, {
      where: { userId: me._id },
    });
    const groups = await ctx.auth.group.get(
      ctx,
      memberships.map((m) => m.groupId),
    );
    return { ...me, memberships, groups };
  },
});

Extend-aware types

When you pass extend validators to createAuth (see Configuration), auth.v.* and the exported types carry that shape — viewer.extend.<field> is typed instead of any.

import type { Viewer, Group, Membership, Doc } from "@robelest/convex-auth/server";
ExportDescription
Doc<T>A document from any auth table
ViewerUser document type (extend-aware)
GroupGroup document type (extend-aware)
MembershipGroupMember document type (extend-aware)

The raw validators (vUserDoc, vGroupDoc, vGroupMemberDoc, vGroupInviteDoc, vPaginated) and buildAuthValidators are also exported from @robelest/convex-auth/server for advanced composition.