### Install Fluent Convex Source: https://github.com/mikecann/fluent-convex/blob/main/README.md Install the fluent-convex package using npm. ```bash npm install fluent-convex ``` -------------------------------- ### Develop Docs Locally Source: https://github.com/mikecann/fluent-convex/blob/main/README.md Starts a local development server for the interactive documentation. ```bash npm run docs:dev ``` -------------------------------- ### Run Integration Tests for Example App Source: https://github.com/mikecann/fluent-convex/blob/main/AGENTS.md Execute integration tests for the example application using `convex-test`. Ensure you are in the `apps/example` directory. ```bash cd apps/example && npx vitest run ``` -------------------------------- ### Install Zod and Convex Helpers Source: https://github.com/mikecann/fluent-convex/blob/main/README.md Install the optional peer dependencies `zod` and `convex-helpers` if you are using the Zod plugin. ```bash npm install zod convex-helpers ``` -------------------------------- ### Before/After Example: Standard Convex Query Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Illustrates a standard Convex query function definition before migration. ```typescript import { query } from "./_generated/server"; import { v } from "convex/values"; import { getAuthUserId } from "@convex-dev/auth/server"; export const listItems = query({ args: { limit: v.optional(v.number()) }, returns: v.array(v.object({ _id: v.id("items"), name: v.string() })), handler: async (ctx, args) => { const userId = await getAuthUserId(ctx); if (!userId) return []; return await ctx.db.query("items").order("desc").take(args.limit ?? 50); }, }); ``` -------------------------------- ### Install Workspace Dependencies Source: https://github.com/mikecann/fluent-convex/blob/main/README.md Installs all dependencies for the monorepo's npm workspaces. ```bash npm install ``` -------------------------------- ### Before/After Example: Fluent-Convex Query Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Shows the equivalent query function definition after migrating to fluent-convex, utilizing the `authedQuery` builder. ```typescript import { v } from "convex/values"; import { authedQuery } from "./fluent"; export const listItems = authedQuery .input({ limit: v.optional(v.number()) }) .returns(v.array(v.object({ _id: v.id("items"), name: v.string() }))) .handler(async (ctx, args) => { return await ctx.db.query("items").order("desc").take(args.limit ?? 50); }) .public(); ``` -------------------------------- ### Create Root Builder and Auth Middleware (TypeScript) Source: https://context7.com/mikecann/fluent-convex/llms.txt Initializes the root builder with a DataModel and defines reusable authentication middleware. This setup is typically done once per project. ```typescript import { createBuilder } from "fluent-convex"; import type { DataModel } from "./_generated/dataModel"; import type { Auth } from "convex/server"; // Create the root builder — DataModel provides typed ctx.db access export const convex = createBuilder(); // Create cross-function-type auth middleware using $context const authMiddleware = convex .$context<{ auth: Auth }>() .createMiddleware(async (ctx, next) => { const identity = await ctx.auth.getUserIdentity(); if (!identity) throw new Error("Unauthorized"); return next({ ...ctx, user: { id: identity.subject, name: identity.name ?? "Unknown" } }); }); // Export pre-configured reusable chains export const authedQuery = convex.query().use(authMiddleware); export const authedMutation = convex.mutation().use(authMiddleware); export const authedAction = convex.action().use(authMiddleware); ``` -------------------------------- ### Generate Convex Codegen Source: https://github.com/mikecann/fluent-convex/blob/main/AGENTS.md Run the Convex codegen command if you add or rename exported functions in the example app. Ensure you are in the `apps/example` directory. ```bash cd apps/example && npx convex codegen ``` -------------------------------- ### Create a Basic Convex Query Source: https://github.com/mikecann/fluent-convex/blob/main/README.md Define a simple Convex query function using createBuilder. This example shows how to set up the builder, define input arguments, and implement the handler logic for fetching data. ```typescript import { createBuilder } from "fluent-convex"; import { v } from "convex/values"; import type { DataModel } from "./_generated/dataModel"; const convex = createBuilder(); export const listNumbers = convex .query() .input({ count: v.number() }) .handler(async (ctx, args) => { const rows = await ctx.db.query("numbers").order("desc").take(args.count); return rows.map((r) => r.value); }) .public(); ``` -------------------------------- ### Implement Protected Query with Authentication Middleware Source: https://github.com/mikecann/fluent-convex/blob/main/README.md This example demonstrates how to use custom middleware for authentication. The authMiddleware checks for user identity and adds user information to the context, which is then used in the handler. This snippet requires the user to be authenticated before accessing the data. ```typescript const authMiddleware = convex.query().createMiddleware(async (ctx, next) => { const identity = await ctx.auth.getUserIdentity(); if (!identity) throw new Error("Unauthorized"); return next({ ...ctx, user: { id: identity.subject, name: identity.name ?? "Unknown" }, }); }); export const listNumbersProtected = convex .query() .use(authMiddleware) .input({ count: v.number() }) .handler(async (ctx, args) => { const rows = await ctx.db.query("numbers").order("desc").take(args.count); return { viewer: ctx.user.name, numbers: rows.map((r) => r.value) }; }) .public(); ``` -------------------------------- ### Create a Custom Plugin for Fluent Convex Source: https://github.com/mikecann/fluent-convex/blob/main/README.md Extend the Fluent Convex builder by subclassing `ConvexBuilderWithFunctionKind` and overriding `_clone()` to preserve your plugin's type through the builder chain. This example shows a custom method `myCustomMethod`. ```typescript import { ConvexBuilderWithFunctionKind, type ConvexBuilderDef, } from "fluent-convex"; class MyPlugin extends ConvexBuilderWithFunctionKind { constructor(builderOrDef: any) { const def = builderOrDef instanceof ConvexBuilderWithFunctionKind ? (builderOrDef as any).def : builderOrDef; super(def); } protected _clone(def: ConvexBuilderDef): any { return new MyPlugin(def); } myCustomMethod(param: string) { console.log("Custom method called with:", param); return this; } } export const myQuery = convex .query() .extend(MyPlugin) .extend(WithZod) .myCustomMethod("hello") .input(z.object({ count: z.number() })) .handler(async (ctx, args) => { /* ... */ }) .public(); ``` -------------------------------- ### Define Public Action (TypeScript) Source: https://context7.com/mikecann/fluent-convex/llms.txt Defines a public action function that can call external APIs or run queries/mutations. This example generates random numbers within a specified range. ```typescript import { v } from "convex/values"; import { convex } from "./fluent"; // Public action — can call external APIs, run queries/mutations export const generateRandom = convex .action() .input({ count: v.number(), min: v.number(), max: v.number() }) .returns(v.array(v.number())) .handler(async (_ctx, args) => { return Array.from({ length: args.count }, () => Math.floor(Math.random() * (args.max - args.min + 1)) + args.min ); }) .public(); ``` -------------------------------- ### Run Core and Zod Plugin Unit Tests Source: https://github.com/mikecann/fluent-convex/blob/main/AGENTS.md Execute unit and type tests for the core library and the Zod plugin. Ensure you are in the `packages/fluent-convex` directory. ```bash cd packages/fluent-convex && npx vitest run ``` -------------------------------- ### Manual npm Publish Source: https://github.com/mikecann/fluent-convex/blob/main/PUBLISHING.md Use this command for emergency manual publishing if the automated CI/CD process fails. Note that this will not include provenance attestation. ```bash # Ensure you're logged in npm login # Build the package npm run build # Publish npm publish --access public ``` -------------------------------- ### Apply Middleware with .use() Source: https://context7.com/mikecann/fluent-convex/llms.txt Applies middleware to the builder chain. Middleware runs in the order it is added. Placement determines composition order at runtime. Returns a new builder with the enriched context type automatically merged in. ```typescript import { convex } from "./fluent"; import type { Auth } from "convex/server"; // Onion middleware: wraps the entire downstream chain const withLogging = (name: string) => convex.createMiddleware(async (ctx, next) => { console.log(`[${name}] Starting...`); const start = Date.now(); try { const result = await next(ctx); console.log(`[${name}] Done in ${Date.now() - start}ms`); return result; } catch (err: any) { console.error(`[${name}] Failed: ${err.message}`); throw err; } }); // Context-enrichment middleware const addTimestamp = convex.createMiddleware(async (ctx, next) => next({ ...ctx, timestamp: Date.now() }) ); // Chaining multiple middleware — they compose in declaration order export const richQuery = convex .query() .use(withLogging("richQuery")) // runs outermost .use(addTimestamp) // runs next, enriches ctx.timestamp .input({ count: v.number() }) .handler(async (ctx, args) => { // ctx.timestamp is fully typed here const rows = await ctx.db.query("numbers").take(args.count); return { timestamp: ctx.timestamp, numbers: rows.map((r) => r.value) }; }) .public(); // Onion execution order for .use(A).use(B): // A-before → B-before → handler → B-after → A-after ``` -------------------------------- ### Create fluent-convex builder instance Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Create a `convex/fluent.ts` file to serve as the single source of truth for your builder instance, auth middleware, and shared reusable chains. Ensure the `DataModel` type parameter is provided for typed `ctx.db` access. ```typescript import { createBuilder } from "fluent-convex"; import type { DataModel } from "./_generated/dataModel"; export const convex = createBuilder(); ``` -------------------------------- ### Complete Fluent Convex Template with Auth Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Set up a complete `fluent.ts` template for a project using `@convex-dev/auth`. This includes creating a builder, defining an authentication middleware, and exporting reusable authenticated query, mutation, and action chains. ```typescript import { createBuilder } from "fluent-convex"; import { getAuthUserId } from "@convex-dev/auth/server"; import type { DataModel, Id } from "./_generated/dataModel"; import type { Auth } from "convex/server"; export const convex = createBuilder(); const authMiddleware = convex .$context<{ auth: Auth }>() .createMiddleware(async (ctx, next) => { const userId = await getAuthUserId(ctx); if (!userId) { throw new Error("Not authenticated"); } return next({ ...ctx, userId: userId as Id<"users">, }); }); export const authedQuery = convex.query().use(authMiddleware); export const authedMutation = convex.mutation().use(authMiddleware); export const authedAction = convex.action().use(authMiddleware); ``` -------------------------------- ### Build Fluent Convex Library Source: https://github.com/mikecann/fluent-convex/blob/main/AGENTS.md Rebuild the fluent-convex library, including the Zod subpath, after making changes to source files. This is necessary before running downstream tests. ```bash npm run build ``` -------------------------------- ### Create Middleware with Type Helpers Source: https://context7.com/mikecann/fluent-convex/llms.txt Provides type helpers for creating middleware with the correct input context. Three patterns exist depending on which Convex context properties the middleware needs. ```typescript import { convex } from "./fluent"; import type { Auth } from "convex/server"; import { getAuthUserId } from "@convex-dev/auth/server"; import type { Id } from "./_generated/dataModel"; // Pattern 1: convex.createMiddleware() — input context is EmptyObject // Use for middleware that needs no context properties (e.g. pure transforms) const addRequestId = convex.createMiddleware(async (ctx, next) => next({ ...ctx, requestId: Math.random().toString(36).slice(2) }) ); // Pattern 2: convex.$context().createMiddleware() — explicit minimal context // Use when middleware needs specific shared properties (auth, etc.) // Works with queries, mutations, AND actions const authMiddleware = convex .$context<{ auth: Auth }>() .createMiddleware(async (ctx, next) => { const userId = await getAuthUserId(ctx); if (!userId) throw new Error("Not authenticated"); return next({ ...ctx, userId: userId as Id<"users"> }); }); // Pattern 3: convex.query().createMiddleware() — input context is QueryCtx // Use when middleware needs db, storage, etc. (query-only middleware) const requireRecord = convex.query().createMiddleware(async (ctx, next) => { const count = await ctx.db.query("numbers").collect(); return next({ ...ctx, totalCount: count.length }); }); // Error-catching middleware (onion pattern) const errorBoundary = convex.createMiddleware(async (ctx, next) => { try { return await next(ctx); } catch (err: any) { // log to external service, rethrow, or return default console.error("Caught:", err.message); throw err; } }); ``` -------------------------------- ### Run All Tests in Monorepo Source: https://github.com/mikecann/fluent-convex/blob/main/README.md Executes all defined tests for the project. ```bash npm test ``` -------------------------------- ### Export pre-configured auth chains Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Export pre-configured query, mutation, and action builder chains from `fluent.ts` that include the auth middleware. This is the recommended pattern for applying middleware consistently across all function types. ```typescript /** Pre-configured query builder with auth middleware applied. */ export const authedQuery = convex.query().use(authMiddleware); /** Pre-configured mutation builder with auth middleware applied. */ export const authedMutation = convex.mutation().use(authMiddleware); /** Pre-configured action builder with auth middleware applied. */ export const authedAction = convex.action().use(authMiddleware); ``` -------------------------------- ### Update Package Version with npm Source: https://github.com/mikecann/fluent-convex/blob/main/PUBLISHING.md Use npm's version command to bump the version in package.json and create a git tag. Ensure you are in the package directory. ```bash cd packages/fluent-convex ``` ```bash # For a patch release (0.3.0 → 0.3.1) npm version patch # For a minor release (0.3.0 → 0.4.0) npm version minor # For a major release (0.3.0 → 1.0.0) npm version major ``` -------------------------------- ### createBuilder Source: https://context7.com/mikecann/fluent-convex/llms.txt Creates the root builder instance, which is typed to a specific Convex data model. This function should typically be called once per project and the instance shared across all function files. ```APIDOC ## createBuilder() ### Description Creates the root builder instance typed to a specific Convex data model. Must be called once per project (typically in `convex/fluent.ts`) and the instance shared across all function files. ### Usage ```ts import { createBuilder } from "fluent-convex"; type DataModel = any; // Replace with your actual DataModel type export const convex = createBuilder(); ``` ``` -------------------------------- ### Extend Fluent Convex with a Custom Logging Plugin Source: https://context7.com/mikecann/fluent-convex/llms.txt Subclass ConvexBuilderWithFunctionKind to create custom plugins. The _clone method ensures the custom builder type is preserved through the chain. Use the .use() method to add middleware like logging. ```typescript import { ConvexBuilderWithFunctionKind, type ConvexBuilderDef, type ConvexArgsValidator, type ConvexReturnsValidator, type Context, type EmptyObject, type FunctionType, type GenericDataModel, } from "fluent-convex"; import { v } from "convex/values"; import { convex } from "./fluent"; class LoggedBuilder extends ConvexBuilderWithFunctionKind< TDataModel, TFunctionType, TCurrentContext, TArgsValidator, TReturnsValidator > { constructor(builder: ConvexBuilderWithFunctionKind) { super((builder as any).def); } // _clone() ensures LoggedBuilder is preserved after .use(), .input(), etc. protected _clone(def: ConvexBuilderDef): any { return new LoggedBuilder(def as any); } withStandardLogging(operationName: string) { return this.use(async (ctx, next) => { console.log(`[${operationName}] start`); try { const result = await next(ctx); console.log(`[${operationName}] ok`); return result; } catch (err) { console.error(`[${operationName}] error`, err); throw err; } }); } } // Usage — .extend() hands the current builder to the class constructor export const loggedQuery = convex .query() .extend(LoggedBuilder) // builder is now LoggedBuilder .withStandardLogging("myQuery") // custom method available .input({ echo: v.string() }) .handler(async (_ctx, args) => `Echo: ${args.echo}`) .public(); ``` -------------------------------- ### Handling Soft Auth Checks Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Demonstrates how to maintain a 'soft fail' authentication pattern by using the base `convex.query()` and manually checking authentication within the handler, rather than relying on the `authedQuery` middleware which throws on failure. ```typescript const userId = await getAuthUserId(ctx); if (!userId) return []; // soft fail ``` -------------------------------- ### Builder Methods: query, mutation, action Source: https://context7.com/mikecann/fluent-convex/llms.txt These methods set the Convex function type on the builder, narrowing the context type to `QueryCtx`, `MutationCtx`, or `ActionCtx` respectively. Each returns a new `ConvexBuilderWithFunctionKind` instance. ```APIDOC ## .query() / .mutation() / .action() ### Description Sets the Convex function type on the builder, narrowing the context type to `QueryCtx`, `MutationCtx`, or `ActionCtx` respectively. Returns a new `ConvexBuilderWithFunctionKind` instance. ### Usage ```ts import { v } from "convex/values"; import { convex } from "./fluent"; // Define a public query export const listNumbers = convex .query() .input({ count: v.number() }) .handler(async (ctx, args) => { return await ctx.db.query("numbers").order("desc").take(args.count); }) .public(); // Define a public mutation export const addNumber = convex .mutation() .input({ value: v.number() }) .returns(v.id("numbers")) .handler(async (ctx, args) => { return await ctx.db.insert("numbers", { value: args.value }); }) .public(); // Define a public action export const generateRandom = convex .action() .input({ count: v.number(), min: v.number(), max: v.number() }) .returns(v.array(v.number())) .handler(async (_ctx, args) => { return Array.from({ length: args.count }, () => Math.floor(Math.random() * (args.max - args.min + 1)) + args.min ); }) .public(); // Define an internal query (server-only) export const internalListAll = convex .query() .input({}) .handler(async (ctx) => { return await ctx.db.query("numbers").collect(); }) .internal(); ``` ``` -------------------------------- ### Integrate Zod for Input and Return Validation with Fluent Convex Source: https://context7.com/mikecann/fluent-convex/llms.txt The WithZod plugin allows using Zod schemas for `.input()` and `.returns()`, which are converted to Convex validators and parsed at runtime. This enables advanced Zod features like `.min()`, `.max()`, and `.email()`. ```typescript // Requires: npm install zod convex-helpers import { WithZod } from "fluent-convex/zod"; import { z } from "zod"; import { v } from "convex/values"; import { convex } from "./fluent"; export const filterNumbers = convex .query() .extend(WithZod) .input(z.object({ filter: z.enum(["all", "positive", "negative", "zero"]), limit: z.number().int().min(1).max(100).default(10), })) .returns(z.object({ filter: z.string(), numbers: z.array(z.number()), totalMatching: z.number(), })) .handler(async (ctx, args) => { const all = await ctx.db.query("numbers").order("desc").take(100); let values = all.map((n) => n.value); if (args.filter === "positive") values = values.filter((n) => n > 0); if (args.filter === "negative") values = values.filter((n) => n < 0); if (args.filter === "zero") values = values.filter((n) => n === 0); return { filter: args.filter, numbers: values.slice(0, args.limit), totalMatching: values.length }; }) .public(); // Mix Zod input with Convex returns validator — both work in the same chain export const addValidated = convex .mutation() .extend(WithZod) .input(z.object({ value: z.number().positive(), label: z.string().optional() })) .returns(v.id("numbers")) // plain Convex validator still works .handler(async (ctx, args) => { return await ctx.db.insert("numbers", { value: args.value }); }) .public(); ``` -------------------------------- ### Function Definition Methods Source: https://github.com/mikecann/fluent-convex/blob/main/README.md These methods are used to define the behavior and validation of Convex functions using a fluent interface. ```APIDOC ## Function Definition Methods ### `.query()` / `.mutation()` / `.action()` **Description**: Sets the type of the Convex function (query, mutation, or action). ### `.input(validator)` **Description**: Sets the input validation schema for the function using a provided validator (e.g., Zod). ### `.returns(validator)` **Description**: Sets the return type validation schema for the function. This must be called before `.handler()`. ### `.use(middleware)` **Description**: Applies middleware to the function. This can be called before or after `.handler()`. ### `.handler(fn)` **Description**: Defines the actual function handler that will be executed. ### `.extend(plugin)` **Description**: Extends the function builder with functionality from a plugin class. ### `.createMiddleware(fn)` **Description**: A utility function to create a middleware function. ### `.public()` / `.internal()` **Description**: Registers the function with Convex as either public or internal. This must be called after `.handler()`. ``` -------------------------------- ### Push Git Commit and Tags Source: https://github.com/mikecann/fluent-convex/blob/main/PUBLISHING.md Push the newly created commit and its associated version tag to the remote repository to trigger the automated publishing workflow. ```bash git push origin main --follow-tags ``` -------------------------------- ### Auth-Protected Query Conversion Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Define auth-protected queries using the fluent-convex chain. The `.public()` method should be used at the end. ```typescript Auth-protected query | `export const f = authedQuery.input(args).returns(returns).handler(fn).public()` ``` -------------------------------- ### Create Cross-Function-Type Authentication Middleware Source: https://github.com/mikecann/fluent-convex/blob/main/README.md This middleware is designed to work across different Convex function types (queries, mutations, actions) by using $context to specify only the required shared properties, such as 'auth'. This allows for more flexible middleware application. ```typescript import type { Auth } from "convex/server"; // Works with queries, mutations, AND actions const authMiddleware = convex .$context<{ auth: Auth }>() .createMiddleware(async (ctx, next) => { const identity = await ctx.auth.getUserIdentity(); if (!identity) throw new Error("Unauthorized"); return next({ ...ctx, user: { id: identity.subject, name: identity.name ?? "Unknown" }, }); }); const authQuery = convex.query().use(authMiddleware); const authMutation = convex.mutation().use(authMiddleware); const authAction = convex.action().use(authMiddleware); ``` -------------------------------- ### Attach Handler Calling an Internal Query Source: https://context7.com/mikecann/fluent-convex/llms.txt Attach an action handler that calls an internal query using `ctx.runQuery`. The builder becomes callable after `.handler()`. ```typescript // Action handler calling an internal query export const getAllViaInternal = convex .action() .input({}) .handler(async (ctx) => { const all = await ctx.runQuery(internal.myFunctions.internalListAll, {}); return { count: all.length, numbers: all }; }) .public(); ``` -------------------------------- ### Integrate Zod for Input and Output Validation Source: https://github.com/mikecann/fluent-convex/blob/main/README.md Use the Zod plugin to add Zod schema support for `.input()` and `.returns()`, enabling full runtime validation with refinements. This automatically converts Zod schemas to Convex validators. ```typescript import { WithZod } from "fluent-convex/zod"; import { z } from "zod"; export const listNumbers = convex .query() .extend(WithZod) .input( z.object({ count: z.number().int().min(1).max(100), }), ) .returns(z.object({ numbers: z.array(z.number()) })) .handler(async (ctx, args) => { const numbers = await ctx.db.query("numbers").take(args.count); return { numbers: numbers.map((n) => n.value) }; }) .public(); ``` -------------------------------- ### Auth-Protected Mutation Conversion Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Convert auth-protected mutations to the fluent-convex builder chain. Ensure `.public()` is the final method call. ```typescript Auth-protected mutation | `export const f = authedMutation.input(args).returns(returns).handler(fn).public()` ``` -------------------------------- ### .handler(fn) Source: https://context7.com/mikecann/fluent-convex/llms.txt Attaches the asynchronous handler function. The handler receives the enriched Convex context and arguments typed according to the `.input()` definition. After `.handler()`, the builder becomes callable. ```APIDOC ## .handler(fn) ### Description Attaches the asynchronous handler function `(ctx, args) => Promise`. The `ctx` parameter is the Convex context enriched with all middleware additions. The `args` parameter is typed to the shape declared by `.input()`. After `.handler()` the builder becomes a *callable* — it can be invoked directly like a regular async function. ### Usage ```ts .handler(async (ctx, args) => { // handler logic }) ``` ``` -------------------------------- ### Query Function Conversion Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Convert standard Convex queries to the fluent-convex builder chain. Ensure `.input()`, `.returns()`, and `.handler()` are used in the correct order, followed by `.public()` or `.internal()`. ```typescript export const f = query({ args, returns, handler }) | `export const f = convex.query().input(args).returns(returns).handler(fn).public()` ``` -------------------------------- ### Action Function Conversion Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Convert standard Convex actions to the fluent-convex builder chain. Follow the specified method chain ordering for `.input()`, `.returns()`, `.handler()`, and `.public()`/`.internal()`. ```typescript export const f = action({ args, returns, handler }) | `export const f = convex.action().input(args).returns(returns).handler(fn).public()` ``` -------------------------------- ### Create auth middleware with @convex-dev/auth Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Implement an auth middleware specifically for projects using `@convex-dev/auth`. This middleware uses `getAuthUserId` to retrieve the user ID and ensures it's authenticated before proceeding. The authenticated user ID is then added to the context. ```typescript import { getAuthUserId } from "@convex-dev/auth/server"; import type { Auth } from "convex/server"; import type { DataModel, Id } from "./_generated/dataModel"; const authMiddleware = convex .$context<{ auth: Auth }>() .createMiddleware(async (ctx, next) => { const userId = await getAuthUserId(ctx); if (!userId) { throw new Error("Not authenticated"); } return next({ ...ctx, userId: userId as Id<"users">, }); }); ``` -------------------------------- ### Create cross-function-type auth middleware Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Define an auth middleware using `$context` to ensure compatibility across queries, mutations, and actions. This middleware requires an `Auth` object and checks for user identity, throwing an error if not authenticated. It then passes user information to the next context. ```typescript import type { Auth } from "convex/server"; const authMiddleware = convex .$context<{ auth: Auth }>() .createMiddleware(async (ctx, next) => { const identity = await ctx.auth.getUserIdentity(); if (!identity) { throw new Error("Not authenticated"); } return next({ ...ctx, user: { id: identity.subject, name: identity.name ?? "Unknown" }, }); }); ``` -------------------------------- ### Auth-Protected Action Conversion Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Define auth-protected actions using the fluent-convex chain. The `.public()` method must be called last. ```typescript Auth-protected action | `export const f = authedAction.input(args).returns(returns).handler(fn).public()` ``` -------------------------------- ### .input(validator) Source: https://context7.com/mikecann/fluent-convex/llms.txt Sets argument validation for mutations and queries. It accepts property validators, object validators, or Zod schemas. This method must be called before `.handler()`. ```APIDOC ## .input(validator) ### Description Sets argument validation for mutations and queries. It accepts property validators, object validators, or Zod schemas. This method must be called before `.handler()`. ### Usage ```ts .input({ value: v.number(), label: v.optional(v.string()) }) .input(v.object({ count: v.number() })) .input(z.object({ value: z.number().positive() })) ``` ``` -------------------------------- ### Mutation Function Conversion Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Convert standard Convex mutations to the fluent-convex builder chain. The order of `.input()`, `.returns()`, `.handler()`, and `.public()`/`.internal()` is critical. ```typescript export const f = mutation({ args, returns, handler }) | `export const f = convex.mutation().input(args).returns(returns).handler(fn).public()` ``` -------------------------------- ### Builder State Source: https://github.com/mikecann/fluent-convex/blob/main/README.md Information about the state of the function builder before and after registration. ```APIDOC ## Builder State **Callable Builder**: The builder is callable before registration (i.e., before calling `.public()` or `.internal()`). **Non-Callable Builder**: After registration with `.public()` or `.internal()`, the builder is no longer callable. ``` -------------------------------- ### Internal Query Function Conversion Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Convert standard internal Convex queries to the fluent-convex builder chain. Use `.internal()` as the final method. ```typescript export const f = internalQuery({ ... }) | `export const f = convex.query().input(args).returns(returns).handler(fn).internal()` ``` -------------------------------- ### Register Public Convex Query Source: https://context7.com/mikecann/fluent-convex/llms.txt Registers a Convex query function that can be called from the client. Requires `convex/values` and `fluent` imports. ```typescript import { v } from "convex/values"; import { convex } from "./fluent"; import { internal } from "./_generated/api"; // Public — callable from frontend via useQuery / useMutation / useAction export const listNumbers = convex .query() .input({ count: v.number() }) .handler(async (ctx, args) => ctx.db.query("numbers").take(args.count)) .public(); ``` -------------------------------- ### Set Input Validation with Zod Schema Source: https://context7.com/mikecann/fluent-convex/llms.txt Use a Zod schema for input validation, requiring the `WithZod` plugin and enabling schema refinements. Must be called before `.handler()`. ```typescript // Form 3: Zod schema — requires WithZod plugin, enables refinements import { WithZod } from "fluent-convex/zod"; import { z } from "zod"; export const withZodInput = convex .mutation() .extend(WithZod) .input(z.object({ value: z.number().positive("Value must be positive"), description: z.string().optional(), })) .returns(v.id("numbers")) .handler(async (ctx, args) => { // Zod refinement enforced server-side before handler runs return await ctx.db.insert("numbers", { value: args.value }); }) .public(); ``` -------------------------------- ### Attach Handler with Enriched Context Source: https://context7.com/mikecann/fluent-convex/llms.txt Attach an async handler function that has access to the Convex context enriched by middleware. The `args` parameter is typed to the shape declared by `.input()`. ```typescript // Handler has full access to enriched context from middleware export const listAuthNumbers = authedQuery .input({ count: v.number() }) .handler(async (ctx, args) => { // ctx.user is injected by authMiddleware — fully typed const rows = await ctx.db.query("numbers").order("desc").take(args.count); return { viewer: ctx.user.name, numbers: rows.map((r) => r.value) }; }) .public(); ``` -------------------------------- ### Register Public Convex Action Calling Internal Mutation Source: https://context7.com/mikecann/fluent-convex/llms.txt Registers a Convex action that calls an internal mutation. Requires `convex/values` and `fluent` imports. ```typescript import { v } from "convex/values"; import { convex } from "./fluent"; import { internal } from "./_generated/api"; // Action calling an internal mutation export const orchestrate = convex .action() .input({ value: v.number() }) .handler(async (ctx, args) => { await ctx.runMutation(internal.myFunctions.internalInsert, { value: args.value }); return { ok: true }; }) .public(); ``` -------------------------------- ### Set Return Type Validation with Zod Schema Source: https://context7.com/mikecann/fluent-convex/llms.txt Define the return type using a Zod schema, with runtime validation enforced after the handler. Requires the `WithZod` plugin and must be called before `.handler()`. ```typescript // Zod schema for returns — runtime validation enforced after handler export const getFilteredStats = convex .query() .extend(WithZod) .input(z.object({ limit: z.number().optional() })) .returns(z.object({ total: z.number(), average: z.number(), min: z.number().nullable(), max: z.number().nullable(), })) .handler(async (ctx, args) => { const rows = await ctx.db.query("numbers").take(args.limit ?? 100); const values = rows.map((r) => r.value); return { total: values.length, average: values.length ? values.reduce((a, b) => a + b, 0) / values.length : 0, min: values.length ? Math.min(...values) : null, max: values.length ? Math.max(...values) : null, }; }) .public(); ``` -------------------------------- ### Internal Action Function Conversion Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Convert standard internal Convex actions to the fluent-convex builder chain. The `.internal()` method must be the last in the chain. ```typescript export const f = internalAction({ ... }) | `export const f = convex.action().input(args).returns(returns).handler(fn).internal()` ``` -------------------------------- ### Define and Use Reusable Chains (Callables) Source: https://context7.com/mikecann/fluent-convex/llms.txt A builder with `.handler()` but without `.public()` / `.internal()` is a callable — a fully typed async function invocable directly from other handlers. The same callable can be registered under multiple names with different middleware stacks. ```typescript import { v } from "convex/values"; import { convex, authedQuery } from "./fluent"; import { addTimestamp } from "./middleware"; // Define once — not yet registered with Convex const getNumbers = convex .query() .input({ count: v.number() }) .handler(async (ctx, args) => { const rows = await ctx.db.query("numbers").order("desc").take(args.count); return rows.map((r) => r.value); }); // Register as a public query export const listNumbers = getNumbers.public(); // Call directly from another handler — no extra Convex function invocation export const getNumbersWithMeta = convex .query() .input({ count: v.number() }) .handler(async (ctx, args) => { const numbers = await getNumbers(ctx, args); // direct call, fully typed return { numbers, fetchedAt: Date.now(), count: numbers.length }; }) .public(); // Register the same callable with additional middleware export const listNumbersAuthed = getNumbers.use(authedQuery).public(); export const listNumbersLogged = getNumbers.use(addTimestamp).public(); ``` -------------------------------- ### Set Input Validation with Property Validators Source: https://context7.com/mikecann/fluent-convex/llms.txt Use property validators for simple input validation, supporting optional fields. Must be called before `.handler()`. ```typescript import { v } from "convex/values"; import { convex } from "./fluent"; // Form 1: Property validators — simplest, supports optional fields export const withProps = convex .mutation() .input({ value: v.number(), label: v.optional(v.string()), tags: v.optional(v.array(v.string())), }) .returns(v.id("numbers")) .handler(async (ctx, args) => { // args.label is string | undefined, args.tags is string[] | undefined return await ctx.db.insert("numbers", { value: args.value }); }) .public(); ``` -------------------------------- ### Set Input Validation with Object Validator Source: https://context7.com/mikecann/fluent-convex/llms.txt Use an object validator for input validation, which also enables `.returns()` pairing. Must be called before `.handler()`. ```typescript // Form 2: Object validator — same as above but enables .returns() pairing export const withObjectValidator = convex .query() .input(v.object({ count: v.number() })) .returns(v.object({ numbers: v.array(v.number()) })) .handler(async (ctx, args) => { const rows = await ctx.db.query("numbers").take(args.count); return { numbers: rows.map((r) => r.value) }; }) .public(); ``` -------------------------------- ### Define a Convex Query Function Source: https://github.com/mikecann/fluent-convex/blob/main/apps/docs/convex/README.md Define a query function with argument validators and implement database reads. Use this for fetching data from your Convex database. ```typescript // convex/myFunctions.ts import { query } from "./_generated/server"; import { v } from "convex/values"; export const myQueryFunction = query({ // Validators for arguments. args: { first: v.number(), second: v.string(), }, // Function implementation. handler: async (ctx, args) => { // Read the database as many times as you need here. // See https://docs.convex.dev/database/reading-data. const documents = await ctx.db.query("tablename").collect(); // Arguments passed from the client are properties of the args object. console.log(args.first, args.second); // Write arbitrary JavaScript here: filter, aggregate, build derived data, // remove non-public properties, or create new objects. return documents; }, }); ``` -------------------------------- ### .returns(validator) Source: https://context7.com/mikecann/fluent-convex/llms.txt Sets the return type validation for mutations and queries. When specified, TypeScript uses the validator's type instead of inferring the handler return. This method must be called before `.handler()`. ```APIDOC ## .returns(validator) ### Description Sets the return type validation for mutations and queries. When specified, TypeScript uses the validator's type instead of inferring the handler return. This method must be called before `.handler()`. ### Usage ```ts .returns(v.object({ count: v.number() })) .returns(z.object({ total: z.number() })) ``` ``` -------------------------------- ### Register Internal Convex Mutation Source: https://context7.com/mikecann/fluent-convex/llms.txt Registers a Convex mutation function restricted to server-side calls. Requires `convex/values` and `fluent` imports. ```typescript import { v } from "convex/values"; import { convex } from "./fluent"; import { internal } from "./_generated/api"; // Internal — only callable server-side via ctx.runQuery(internal.file.fn) export const internalInsert = convex .mutation() .input({ value: v.number() }) .handler(async (ctx, args) => { await ctx.db.insert("numbers", { value: args.value }); }) .internal(); ``` -------------------------------- ### Internal Mutation Function Conversion Source: https://github.com/mikecann/fluent-convex/blob/main/MIGRATING.md Convert standard internal Convex mutations to the fluent-convex builder chain. Ensure `.internal()` is called last. ```typescript export const f = internalMutation({ ... }) | `export const f = convex.mutation().input(args).returns(returns).handler(fn).internal()` ``` -------------------------------- ### Cross-Function-Type Middleware Declaration Source: https://github.com/mikecann/fluent-convex/blob/main/AGENTS.md Declare middleware that only requires shared context properties like `auth`. This makes the middleware compatible with all function types (queries, mutations, actions). ```typescript convex.$context<{ auth: Auth }>().createMiddleware(fn) ``` -------------------------------- ### Define Public Mutation (TypeScript) Source: https://context7.com/mikecann/fluent-convex/llms.txt Defines a public mutation function that writes data to the 'numbers' table. It accepts a 'value' argument and returns the ID of the newly inserted number. ```typescript import { v } from "convex/values"; import { convex } from "./fluent"; // Public mutation — writes to db export const addNumber = convex .mutation() .input({ value: v.number() }) .returns(v.id("numbers")) .handler(async (ctx, args) => { return await ctx.db.insert("numbers", { value: args.value }); }) .public(); ``` -------------------------------- ### Use a Convex Mutation Function in React Source: https://github.com/mikecann/fluent-convex/blob/main/apps/docs/convex/README.md Invoke a Convex mutation function from a React component using the `useMutation` hook. Mutations can be fire-and-forget or their results can be handled. ```typescript const mutation = useMutation(api.myFunctions.myMutationFunction); function handleButtonPress() { // fire and forget, the most common way to use mutations mutation({ first: "Hello!", second: "me" }); // OR // use the result once the mutation has completed mutation({ first: "Hello!", second: "me" }).then((result) => console.log(result), ); } ``` -------------------------------- ### Use a Convex Query Function in React Source: https://github.com/mikecann/fluent-convex/blob/main/apps/docs/convex/README.md Invoke a Convex query function from a React component using the `useQuery` hook. Ensure the arguments match the function's validators. ```typescript const data = useQuery(api.myFunctions.myQueryFunction, { first: 10, second: "hello", }); ``` -------------------------------- ### Define Internal Query (TypeScript) Source: https://context7.com/mikecann/fluent-convex/llms.txt Defines an internal query function, accessible only from the server-side. It retrieves all entries from the 'numbers' table. ```typescript import { v } from "convex/values"; import { convex } from "./fluent"; // Internal (server-only) query — not callable from clients export const internalListAll = convex .query() .input({}) .handler(async (ctx) => { return await ctx.db.query("numbers").collect(); }) .internal(); ``` -------------------------------- ### Define Public Query (TypeScript) Source: https://context7.com/mikecann/fluent-convex/llms.txt Defines a public query function that reads data from the 'numbers' table. It accepts a 'count' argument and returns a list of numbers. ```typescript import { v } from "convex/values"; import { convex } from "./fluent"; // Public query — reads from db export const listNumbers = convex .query() .input({ count: v.number() }) .handler(async (ctx, args) => { return await ctx.db.query("numbers").order("desc").take(args.count); }) .public(); ``` -------------------------------- ### Set Return Type Validation with Convex Validator Source: https://context7.com/mikecann/fluent-convex/llms.txt Define the return type using a Convex validator. Must be called before `.handler()`. ```typescript // Convex validator for returns export const getNumberStats = convex .query() .input({}) .returns(v.object({ count: v.number(), sum: v.number(), })) .handler(async (ctx) => { const rows = await ctx.db.query("numbers").collect(); const values = rows.map((r) => r.value); return { count: values.length, sum: values.reduce((a, b) => a + b, 0) }; }) .public(); ``` -------------------------------- ### Define and Reuse Callable Convex Functions Source: https://github.com/mikecann/fluent-convex/blob/main/README.md Define a callable function that can be reused across other handlers without an extra Convex function call. It can be registered publicly or with middleware. ```typescript const getNumbers = convex .query() .input({ count: v.number() }) .handler(async (ctx, args) => { const rows = await ctx.db.query("numbers").order("desc").take(args.count); return rows.map((r) => r.value); }); export const listNumbers = getNumbers.public(); export const getNumbersWithTimestamp = convex .query() .input({ count: v.number() }) .handler(async (ctx, args) => { const numbers = await getNumbers(ctx, args); return { numbers, fetchedAt: Date.now() }; }) .public(); export const listNumbersProtected = getNumbers.use(authMiddleware).public(); export const listNumbersLogged = getNumbers.use(withLogging("logged")).public(); ``` -------------------------------- ### Define a Convex Mutation Function Source: https://github.com/mikecann/fluent-convex/blob/main/apps/docs/convex/README.md Define a mutation function with argument validators for modifying data. It can also read from the database. ```typescript // convex/myFunctions.ts import { mutation } from "./_generated/server"; import { v } from "convex/values"; export const myMutationFunction = mutation({ // Validators for arguments. args: { first: v.string(), second: v.string(), }, // Function implementation. handler: async (ctx, args) => { // Insert or modify documents in the database here. // Mutations can also read from the database like queries. // See https://docs.convex.dev/database/writing-data. const message = { body: args.first, author: args.second }; const id = await ctx.db.insert("messages", message); // Optionally, return a value from your mutation. return await ctx.db.get("messages", id); }, }); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.