Skip to content

Custom function context type helpers #8

@sbkl

Description

@sbkl

Having some hard time with these custom functions to get type helpers for the context of query, mutation and action.

import * as ConvexBase from "@/convex/_generated/server";
import {
  zCustomQueryBuilder,
  customCtx,
} from "zodvex";

// convex helpers trigger
export const triggers = new Triggers<DataModel>();

// convex helpers rls config
async function rlsRules(ctx: ConvexBase.QueryCtx) {
  const user = await getCurrentUserOrThrow(ctx);
  return {
    rules: {} satisfies Rules<ConvexBase.QueryCtx, DataModel>,
    user,
  };
}

// Helper to extract the convex ctx type from the custom functions
type CustomCtx<T> = T extends (config: infer Config) => any
  ? Config extends { handler: (ctx: infer Ctx, ...args: any[]) => any }
    ? Ctx
    : never
  : never;

// custom function
export const protectedQuery = zCustomQueryBuilder(
  ConvexBase.query,
  customCtx(async (ctx) => {
    const { rules, user } = await rlsRules(ctx);
    return {
      db: wrapDatabaseReader(ctx, ctx.db, rules),
      user,
    };
  })
);

// Works. I get the correct type
export type ProtectedQueryCtx = CustomCtx<typeof protectedQuery>;

But not for this custom mutation using triggers

import { Triggers } from "convex-helpers/server/triggers";
import {
  Rules,
  wrapDatabaseReader,
  wrapDatabaseWriter,
} from "convex-helpers/server/rowLevelSecurity";

// custom function wrapped with the triggers and rls config
export const protectedMutation = zCustomMutationBuilder(
  ConvexBase.mutation,
  customCtx(async (ctx) => {
    const withTriggersCtx = triggers.wrapDB(ctx);
    const { rules, user } = await rlsRules(withTriggersCtx);
    return {
      ...withTriggersCtx,
      db: wrapDatabaseWriter(withTriggersCtx, withTriggersCtx.db, rules),
      user,
    };
  })
);

// becomes any
export type ProtectedMutationCtx = CustomCtx<typeof protectedMutation>;

I think it is because the ctx in customCtx function is of type any to start with or maybe how the ctx merge is handled? If I remove the spread of the withTriggerCtx, the types go through properly. Any idea?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions