YeetCode
Convex App

Database Schema

Convex database tables, fields, and indexes.

The database schema is defined in apps/convex/schema.ts using Convex's schema API. It serves as the single source of truth for your data model — Convex generates TypeScript types from it automatically.

Tables

users

Stores application users synced from Clerk.

FieldTypeDescription
externalIdstringClerk user ID (e.g., user_2abc123)

Indexes:

  • by_external_id on ["externalId"] — Fast lookup by Clerk user ID

webhooks

Stores Clerk webhook events for audit and processing.

FieldTypeDescription
externalIdstringClerk user ID from the event
type"user.created" | "user.deleted"Event type (discriminated union)
payloadanyFull Clerk webhook payload

Indexes:

  • by_external_id on ["externalId"] — Fast lookup by Clerk user ID

Schema Definition

// apps/convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  users: defineTable({
    externalId: v.string(),
  }).index("by_external_id", ["externalId"]),

  webhooks: defineTable({
    externalId: v.string(),
    type: v.union(v.literal('user.created'), v.literal('user.deleted')),
    payload: v.any(),
  }).index("by_external_id", ["externalId"]),
});

Design Notes

  • The users table is intentionally minimal — Clerk is the source of truth for user profile data (name, email, avatar). We only store the externalId to create a Convex-side reference for relationships and access control.
  • The webhooks table uses v.any() for payload because Clerk webhook payloads vary by event type. Validation happens at the application layer via Zod schemas in clerk/webhookSchemas.ts.
  • Both tables index on externalId for efficient lookups during webhook processing.

On this page