Convex Data Patterns

Convex Data Patterns

Learn effective Convex data patterns for scalable NowStack applications.

DateDecember 20, 2024
AuthorMelvynx
Reading time2 min read

Convex is the application backend for NowStack. Data reads and writes should go through Convex queries, mutations, and actions so authentication, organization access, and realtime updates stay in one place.

Model Data In Convex

Define tables and indexes in convex/schema.ts:

import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  projects: defineTable({
    organizationId: v.string(),
    name: v.string(),
    status: v.union(v.literal("draft"), v.literal("active")),
  }).index("by_organization", ["organizationId"]),
});

Query With Indexes

Use indexes for reads that target a subset of data:

const projects = await ctx.db
  .query("projects")
  .withIndex("by_organization", (q) =>
    q.eq("organizationId", args.organizationId),
  )
  .collect();

Keep Writes In Mutations

Mutations should own app data changes:

await ctx.db.insert("projects", {
  organizationId: args.organizationId,
  name: args.name,
  status: "draft",
});

Use Actions For External Services

Use actions when the function needs to call external services such as Stripe, Resend, or Cloudflare R2. Keep the database update in a mutation when possible, and call that mutation from the action after the external request succeeds.

Practical Tips

  • Put organization-scoped functions behind the shared org builders.
  • Keep public API routes thin and call Convex for app data.
  • Store backend secrets in Convex env.
  • Add indexes before introducing new query access patterns.
  • Return DTOs from shared mappers when the same shape is used in multiple places.

Related Posts