Claude
Skills
Sign in
Back

bknd-crud-delete

Included with Lifetime
$97 forever

Use when deleting records from a Bknd entity via the SDK or REST API. Covers deleteOne, deleteMany, soft delete patterns, cascade considerations, response handling, and common patterns.

Backend & APIs

What this skill does


# CRUD Delete

Delete records from your Bknd database using the SDK or REST API.

## Prerequisites

- Bknd project running (local or deployed)
- Entity exists with records to delete
- SDK configured or API endpoint known
- Record ID or filter criteria known
- Understanding of any relationships/dependencies

## When to Use UI Mode

- Quick one-off deletions
- Manual data cleanup
- Visual verification of what's being deleted

**UI steps:** Admin Panel > Data > Select Entity > Click record > Delete button > Confirm

## When to Use Code Mode

- Application logic for user-initiated deletes
- Automated data cleanup/maintenance
- Bulk deletions
- Soft delete implementations

## Code Approach

### Step 1: Set Up SDK Client

```typescript
import { Api } from "bknd";

const api = new Api({
  host: "http://localhost:7654",
});

// If auth required:
api.updateToken("your-jwt-token");
```

### Step 2: Delete Single Record

Use `deleteOne(entity, id)`:

```typescript
const { ok, data, error } = await api.data.deleteOne("posts", 1);

if (ok) {
  console.log("Deleted post:", data.id);
} else {
  console.error("Failed:", error.message);
}
```

### Step 3: Handle Response

The response object:

```typescript
type DeleteResponse = {
  ok: boolean;       // Success/failure
  data?: {           // Deleted record (if ok)
    id: number;
    // ...all fields of deleted record
  };
  error?: {          // Error info (if !ok)
    message: string;
    code: string;
  };
};
```

### Step 4: Delete Multiple Records (Bulk)

Use `deleteMany(entity, where)`:

```typescript
// Delete all archived posts
const { ok, data } = await api.data.deleteMany("posts", {
  status: { $eq: "archived" },
});

// data contains deleted records
console.log("Deleted", data.length, "posts");
```

**Important:** `where` clause is required to prevent accidental delete-all.

```typescript
// Delete old sessions
await api.data.deleteMany("sessions", {
  last_active: { $lt: "2024-01-01" },
});

// Delete by multiple conditions
await api.data.deleteMany("logs", {
  level: { $eq: "debug" },
  created_at: { $lt: "2024-06-01" },
});
```

### Step 5: Verify Before Delete

Always verify record exists or check count before deleting:

```typescript
// Check record exists
const { data: existing } = await api.data.readOne("posts", id);
if (!existing) {
  throw new Error("Post not found");
}
await api.data.deleteOne("posts", id);

// Check count before bulk delete
const { data: countResult } = await api.data.count("logs", {
  level: { $eq: "debug" },
});
console.log(`About to delete ${countResult.count} records`);
```

## REST API Approach

### Delete One

```bash
curl -X DELETE http://localhost:7654/api/data/posts/1
```

### Delete with Auth

```bash
curl -X DELETE http://localhost:7654/api/data/posts/1 \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

### Delete Many

```bash
# Delete all archived posts
curl -X DELETE "http://localhost:7654/api/data/posts?where=%7B%22status%22%3A%22archived%22%7D"

# URL-decoded where: {"status":"archived"}
```

## React Integration

### Delete Button

```tsx
import { useApp } from "bknd/react";
import { useState } from "react";

function DeleteButton({ postId, onDeleted }: { postId: number; onDeleted?: () => void }) {
  const { api } = useApp();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function handleDelete() {
    if (!confirm("Are you sure you want to delete this post?")) {
      return;
    }

    setLoading(true);
    setError(null);

    const { ok, error: apiError } = await api.data.deleteOne("posts", postId);

    setLoading(false);

    if (ok) {
      onDeleted?.();
    } else {
      setError(apiError.message);
    }
  }

  return (
    <>
      <button onClick={handleDelete} disabled={loading}>
        {loading ? "Deleting..." : "Delete"}
      </button>
      {error && <p className="error">{error}</p>}
    </>
  );
}
```

### With SWR Revalidation

```tsx
import { mutate } from "swr";

function useDeletePost() {
  const { api } = useApp();

  async function deletePost(id: number) {
    const { ok, error } = await api.data.deleteOne("posts", id);

    if (ok) {
      // Revalidate list
      mutate("posts");
    }

    return { ok, error };
  }

  return { deletePost };
}
```

### Optimistic Delete

```tsx
function useOptimisticDelete() {
  const { api } = useApp();
  const [posts, setPosts] = useState<Post[]>([]);

  async function deletePost(id: number) {
    // Optimistic: remove immediately
    const originalPosts = [...posts];
    setPosts((prev) => prev.filter((p) => p.id !== id));

    // Actual delete
    const { ok } = await api.data.deleteOne("posts", id);

    if (!ok) {
      // Rollback on failure
      setPosts(originalPosts);
    }

    return { ok };
  }

  return { posts, deletePost };
}
```

## Full Example

```typescript
import { Api } from "bknd";

const api = new Api({ host: "http://localhost:7654" });

// Authenticate
await api.auth.login({ email: "[email protected]", password: "password" });

// Simple delete
const { ok, data } = await api.data.deleteOne("posts", 1);
if (ok) {
  console.log("Deleted:", data.title);
}

// Delete with verification
const postId = 5;
const { data: post } = await api.data.readOne("posts", postId);
if (post) {
  await api.data.deleteOne("posts", postId);
}

// Bulk delete: remove old archived posts
const { data: deleted } = await api.data.deleteMany("posts", {
  status: { $eq: "archived" },
  created_at: { $lt: "2023-01-01" },
});
console.log("Deleted", deleted.length, "old archived posts");

// Cleanup expired sessions
await api.data.deleteMany("sessions", {
  expires_at: { $lt: new Date().toISOString() },
});
```

## Common Patterns

### Soft Delete (Recommended for User Data)

Instead of permanent deletion, mark as deleted:

```typescript
// Soft delete: set timestamp
async function softDelete(api: Api, entity: string, id: number) {
  return api.data.updateOne(entity, id, {
    deleted_at: new Date().toISOString(),
  });
}

// Restore soft-deleted record
async function restore(api: Api, entity: string, id: number) {
  return api.data.updateOne(entity, id, {
    deleted_at: null,
  });
}

// Query non-deleted records
async function findActive(api: Api, entity: string, query = {}) {
  return api.data.readMany(entity, {
    ...query,
    where: {
      ...query.where,
      deleted_at: { $isnull: true },
    },
  });
}

// Permanently delete soft-deleted records older than 30 days
async function purgeDeleted(api: Api, entity: string) {
  const thirtyDaysAgo = new Date();
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

  return api.data.deleteMany(entity, {
    deleted_at: { $lt: thirtyDaysAgo.toISOString() },
  });
}
```

### Delete with Confirmation

```typescript
async function deleteWithConfirmation(
  api: Api,
  entity: string,
  id: number,
  confirm: () => Promise<boolean>
) {
  const { data } = await api.data.readOne(entity, id);
  if (!data) {
    return { ok: false, error: { message: "Record not found" } };
  }

  const confirmed = await confirm();
  if (!confirmed) {
    return { ok: false, error: { message: "Cancelled by user" } };
  }

  return api.data.deleteOne(entity, id);
}
```

### Cascade Delete (Manual)

When Bknd doesn't auto-cascade, delete children first:

```typescript
async function cascadeDelete(api: Api, userId: number) {
  // Delete children first
  await api.data.deleteMany("posts", { author_id: { $eq: userId } });
  await api.data.deleteMany("comments", { user_id: { $eq: userId } });
  await api.data.deleteMany("likes", { user_id: { $eq: userId } });

  // Then delete parent
  return api.data.deleteOne("users", userId);
}
```

### Batch Delete with Progress

```typescript
async function batchDelete(
  api: Api,
  entity: string,
  ids: number[],
  onProgress?: (done: number, total: number) => void
) {
  const results = [];

  for (let i = 0; i < ids.length; i++) {
    const result = await api.data.delete

Related in Backend & APIs