Fluent Convex

This is an optional pattern for apps that want app-side Convex middleware on top of the minimal auth setup. You do not need this to use convex-auth.

If you do want custom app helpers, fluent-convex keeps auth middleware concise and explicit.

Setup

// convex/lib/functions.ts
import { createBuilder } from "fluent-convex";
import { WithZod } from "fluent-convex/zod";
import type { DataModel } from "./_generated/dataModel";
import { auth } from "../auth";

const convex = createBuilder<DataModel>();

// auth.context() resolves { userId, user, groupId, role, grants }
// and throws ConvexError if unauthenticated.
const withRequiredAuth = convex.createMiddleware(async (ctx, next) => {
  return next({ ...ctx, auth: await auth.context(ctx) });
});

export const query = convex.query().use(withRequiredAuth).extend(WithZod);
export const mutation = convex.mutation().use(withRequiredAuth).extend(WithZod);
export const internalMutation = convex.mutation().extend(WithZod);

Usage

// convex/chat.ts
import { z } from "zod/v4";
import { mutation } from "./lib/functions";

export const send = mutation
  .input(z.object({ body: z.string().trim().min(1) }))
  .handler(async (ctx, { body }) => {
    await ctx.db.insert("messages", { body, userId: ctx.auth.userId });
    return null;
  })
  .public();

This is app-specific code. The canonical convex-auth setup only needs:

  • convex/convex.config.ts
  • convex/auth.ts
  • convex/auth.config.ts
  • convex/http.ts