Use when removing an entity from Bknd. Covers safely deleting entities, handling relationships and dependencies, data backup, the sync workflow with --drop flag, and cleaning up orphaned data.
Safely remove an entity (table) from Bknd, handling dependencies and avoiding data loss.
bknd-create-entity)bknd.config.tsDeleting an entity:
Before deleting an entity, verify:
Entities may be referenced by other entities via:
const api = app.getApi();
const count = await api.data.count("entity_to_delete");
console.log(`Records to delete: ${count.data.count}`);
Search codebase for:
"entity_name"DB["entity_name"]api.data.*("entity_name")// Export data before deletion
const api = app.getApi();
const allRecords = await api.data.readMany("entity_to_delete", {
limit: 100000,
});
// Save to file
import { writeFileSync } from "fs";
writeFileSync(
"backup-entity_to_delete.json",
JSON.stringify(allRecords.data, null, 2)
);
Check your schema for relationships:
// Look for relationships involving this entity
const schema = em(
{
users: entity("users", { email: text().required() }),
posts: entity("posts", { title: text().required() }),
comments: entity("comments", { body: text() }),
},
({ relation }, { users, posts, comments }) => {
// posts depends on users (foreign key)
relation(posts).manyToOne(users);
// comments depends on posts (foreign key)
relation(comments).manyToOne(posts);
}
);
Dependency order matters: Delete children before parents.
If entity is a target of relationships, update schema to remove them:
// BEFORE: posts references users
const schema = em(
{
users: entity("users", { email: text().required() }),
posts: entity("posts", { title: text().required() }),
},
({ relation }, { users, posts }) => {
relation(posts).manyToOne(users);
}
);
// AFTER: Remove relationship before deleting users
const schema = em({
users: entity("users", { email: text().required() }),
posts: entity("posts", { title: text().required() }),
});
Simply remove the entity definition from your bknd.config.ts:
// BEFORE
const schema = em({
users: entity("users", { email: text().required() }),
posts: entity("posts", { title: text().required() }),
deprecated_entity: entity("deprecated_entity", { data: text() }),
});
// AFTER - entity removed
const schema = em({
users: entity("users", { email: text().required() }),
posts: entity("posts", { title: text().required() }),
});
# See what will be dropped (dry run)
npx bknd sync
Output shows:
Tables to drop: deprecated_entity
Columns affected: (none on other tables)
# Apply with drop flag (destructive)
npx bknd sync --drop
Or with force (enables all destructive operations):
npx bknd sync --force
Remove all references:
Navigate to http://localhost:1337 (or your configured URL).
Click Data in the sidebar.
Click on the entity you want to delete.
Look for:
If you need the data:
After deletion, ensure database is synced:
npx bknd sync --drop from CLIProblem: Deleting users when posts has users_id foreign key.
Solution 1: Delete Children First
// 1. Delete all posts referencing users
const api = app.getApi();
await api.data.deleteMany("posts", {});
// 2. Then delete users
// (via schema removal + sync)
Solution 2: Remove Relationship First
// 1. Remove relationship from schema
// 2. Sync to remove foreign key
// 3. Remove entity from schema
// 4. Sync again with --drop
Problem: tags is used in posts_tags junction table.
Solution:
// 1. Remove many-to-many relationship
const schema = em(
{
posts: entity("posts", { title: text() }),
tags: entity("tags", { name: text() }),
}
// Remove: ({ relation }, { posts, tags }) => { relation(posts).manyToMany(tags); }
);
// 2. Sync to drop junction table
// npx bknd sync --drop
// 3. Remove tags entity
const schema = em({
posts: entity("posts", { title: text() }),
});
// 4. Sync again to drop tags table
// npx bknd sync --drop
Problem: categories references itself (parent/children).
Solution:
// 1. Remove self-reference relation
const schema = em({
categories: entity("categories", { name: text() }),
// Remove self-referencing relation definition
});
// 2. Sync to remove foreign key
// npx bknd sync --drop
// 3. Remove entity
// (then sync again)
Order matters. Delete in dependency order (children first):
// Dependency tree:
// users <- posts <- comments
// <- likes
// Delete order:
// 1. comments (depends on posts)
// 2. likes (depends on posts)
// 3. posts (depends on users)
// 4. users (no dependencies)
// scripts/cleanup-entities.ts
import { App } from "bknd";
async function cleanup() {
const app = new App({
connection: { url: process.env.DB_URL! },
});
await app.build();
const api = app.getApi();
// Delete in order
const entitiesToDelete = ["comments", "likes", "posts"];
for (const entity of entitiesToDelete) {
const count = await api.data.count(entity);
console.log(`Deleting ${count.data.count} records from ${entity}...`);
await api.data.deleteMany(entity, {});
console.log(`Deleted all records from ${entity}`);
}
console.log("Data cleanup complete. Now remove from schema and sync.");
}
cleanup().catch(console.error);
Error: Cannot drop table: foreign key constraint
Cause: Another entity references this one.
Fix: Remove relationship first, sync, then remove entity.
Problem: After removing many-to-many relation, junction table remains.
Fix: Run npx bknd sync --drop to include destructive operations.
Problem: Deleted from code but still shows in admin panel.
Fix:
npx bknd sync --dropProblem: Code still references deleted entity.
Fix:
grep -r "entity_name" src/Problem: Deleted production data.
Fix:
# 1. Check schema export (entity should be absent)
npx bknd schema --pretty | grep entity_name
# 2. Verify sync status
npx bknd sync
# Should show no pending changes
const api = app.getApi();
// This should fail/return error for deleted entity
try {
await api.data.readMany("deleted_entity", { limit: 1 });
console.log("ERROR: Entity still exists!");
} catch (e) {
console.log("Confirmed: Entity deleted successfully");
}
# Should return 404 or error
curl http://localhost:1337/api/data/deleted_entity
DO:
npx bknd sync before --dropDON'T:
--drop without previewing changes