Database · Convex component
@sholajegede/convex-cascading-deletes
A convex cascading deletes component for Convex.
npm install @sholajegede/convex-cascading-deletes5,590downloads last week
47,235downloads last 12 months
v0.1.2latest, Mar 13, 2026
3releases since Mar 13, 2026
Downloads
Download history
Loading download history…
Build this
Build with @sholajegede/convex-cascading-deletes
The install and setup steps from the package documentation.
- 1
Installation
shnpm install @sholajegede/convex-cascading-deletesAdd the component to your
convex/convex.config.ts:tsimport { defineApp } from "convex/server"; import convexCascadingDeletes from "@sholajegede/convex-cascading-deletes/convex.config.js"; const app = defineApp(); app.use(convexCascadingDeletes); export default app; - 2
Usage
Instantiate the client once, declaring your table relationships:
ts// convex/cascadeDeletes.ts import { components } from "./_generated/api.js"; import { CascadingDeletes } from "@sholajegede/convex-cascading-deletes"; export const cascadingDeletes = new CascadingDeletes(components.convexCascadingDeletes, { relationships: [ { sourceTable: "posts", // child table targetTable: "users", // parent table indexName: "by_user", // index on posts that references users fieldName: "userId", // field on posts that holds the user ID }, { sourceTable: "comments", targetTable: "posts", indexName: "by_post", fieldName: "postId", }, ], });Delete with cascade
ts// convex/users.ts import { action } from "./_generated/server.js"; import { cascadingDeletes } from "./cascadeDeletes.js"; import { v } from "convex/values"; export const deleteUser = action({ args: { userId: v.string() }, handler: async (ctx, args) => { const counts = await cascadingDeletes.deleteWithCascade(ctx, { table: "users", id: args.userId, }); // counts: { users: 1, posts: 4, comments: 12 } return counts; }, });Deleting a user cascades to their posts, then to each post's comments — all automatically, in the correct order.
Enforce cascade-only deletions
tsexport const updatePost = mutation({ args: { postId: v.id("posts"), title: v.string() }, handler: async (ctx, args) => { const db = cascadingDeletes.getSafeDb(ctx); // db.delete() now throws — use deleteWithCascade instead await db.patch(args.postId, { title: args.title }); // fine }, });Query deletion logs
ts// convex/logs.ts import { query } from "./_generated/server.js"; import { components } from "./_generated/api.js"; import { v } from "convex/values"; export const getDeletionLog = query({ args: { table: v.string(), id: v.string() }, handler: async (ctx, args) => { return await ctx.runQuery(components.convexCascadingDeletes.lib.getDeletionLog, { rootTable: args.table, rootId: args.id, }); }, });tsx// React — subscribes reactively const log = useQuery(api.logs.getDeletionLog, { table: "users", id: userId }); // log.deletedCounts — JSON string: { "users": 1, "posts": 4, "comments": 12 } // log.deletedAt — timestamp of when the cascade ranValidate indexes at startup
tsexport const onStartup = mutation({ handler: async (ctx) => { await cascadingDeletes.validate(ctx); }, });