Claude
Skills
Sign in
Back

bknd-crud-update

Included with Lifetime
$97 forever

Use when updating existing records in a Bknd entity via the SDK or REST API. Covers updateOne, updateMany, updating relations ($set, $add, $remove, $unset), partial updates, conditional updates, response handling, and common patterns.

Backend & APIs

What this skill does


# CRUD Update

Update existing records in your Bknd database using the SDK or REST API.

## Prerequisites

- Bknd project running (local or deployed)
- Entity exists with records to update
- SDK configured or API endpoint known
- Record ID or filter criteria known

## When to Use UI Mode

- Quick one-off edits
- Manual data corrections
- Visual verification during development

**UI steps:** Admin Panel > Data > Select Entity > Click record > Edit fields > Save

## When to Use Code Mode

- Application logic for user edits
- Form submissions
- Bulk updates
- Automated data maintenance

## 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: Update Single Record

Use `updateOne(entity, id, data)`:

```typescript
const { ok, data, error } = await api.data.updateOne("posts", 1, {
  title: "Updated Title",
  status: "published",
});

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

### Step 3: Handle Response

The response object:

```typescript
type UpdateResponse = {
  ok: boolean;       // Success/failure
  data?: {           // Updated record (if ok)
    id: number;
    // ...all fields with new values
  };
  error?: {          // Error info (if !ok)
    message: string;
    code: string;
  };
};
```

### Step 4: Partial Updates

Only changed fields are required - other fields remain unchanged:

```typescript
// Only update title, keep everything else
await api.data.updateOne("posts", 1, {
  title: "New Title Only",
});

// Update multiple fields
await api.data.updateOne("users", 5, {
  name: "New Name",
  bio: "Updated bio",
  updated_at: new Date().toISOString(),
});
```

### Step 5: Update Relations

#### Change Linked Record (Many-to-One)

```typescript
// Change post author to user ID 2
await api.data.updateOne("posts", 1, {
  author: { $set: 2 },
});
```

#### Unlink Record (Set to NULL)

```typescript
// Remove author link
await api.data.updateOne("posts", 1, {
  author: { $unset: true },
});
```

#### Many-to-Many: Add Relations

```typescript
// Add tags 4 and 5 to existing tags
await api.data.updateOne("posts", 1, {
  tags: { $add: [4, 5] },
});
```

#### Many-to-Many: Remove Relations

```typescript
// Remove tag 2 from post
await api.data.updateOne("posts", 1, {
  tags: { $remove: [2] },
});
```

#### Many-to-Many: Replace All Relations

```typescript
// Replace all tags with new set
await api.data.updateOne("posts", 1, {
  tags: { $set: [1, 3, 5] },
});
```

#### Combined Field and Relation Update

```typescript
await api.data.updateOne("posts", 1, {
  title: "Updated Post",
  status: "published",
  author: { $set: newAuthorId },
  tags: { $add: [newTagId] },
});
```

### Step 6: Update Multiple Records (Bulk)

Use `updateMany(entity, where, data)`:

```typescript
// Archive all draft posts
const { ok, data } = await api.data.updateMany(
  "posts",
  { status: { $eq: "draft" } },     // where clause (required)
  { status: "archived" },            // update values
);

// data contains affected records
console.log("Archived", data.length, "posts");
```

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

```typescript
// Update posts by author
await api.data.updateMany(
  "posts",
  { author_id: { $eq: userId } },
  { author_id: newUserId },
);

// Update old records
await api.data.updateMany(
  "sessions",
  { last_active: { $lt: "2024-01-01" } },
  { expired: true },
);
```

## REST API Approach

### Update One

```bash
curl -X PATCH http://localhost:7654/api/data/posts/1 \
  -H "Content-Type: application/json" \
  -d '{"title": "Updated Title", "status": "published"}'
```

### Update with Auth

```bash
curl -X PATCH http://localhost:7654/api/data/posts/1 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{"title": "Protected Update"}'
```

### Update Relations

```bash
# Change author
curl -X PATCH http://localhost:7654/api/data/posts/1 \
  -H "Content-Type: application/json" \
  -d '{"author": {"$set": 2}}'

# Add tags
curl -X PATCH http://localhost:7654/api/data/posts/1 \
  -H "Content-Type: application/json" \
  -d '{"tags": {"$add": [4, 5]}}'
```

### Update Many

```bash
curl -X PATCH "http://localhost:7654/api/data/posts?where=%7B%22status%22%3A%22draft%22%7D" \
  -H "Content-Type: application/json" \
  -d '{"status": "archived"}'
```

## React Integration

### Edit Form

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

function EditPostForm({ postId }: { postId: number }) {
  const { api } = useApp();
  const [title, setTitle] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Load existing data
  useEffect(() => {
    api.data.readOne("posts", postId).then(({ data }) => {
      if (data) setTitle(data.title);
    });
  }, [postId]);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);
    setError(null);

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

    setLoading(false);

    if (!ok) {
      setError(apiError.message);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        placeholder="Post title"
      />
      <button type="submit" disabled={loading}>
        {loading ? "Saving..." : "Save"}
      </button>
      {error && <p className="error">{error}</p>}
    </form>
  );
}
```

### With SWR Revalidation

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

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

  async function updatePost(id: number, updates: object) {
    const { ok, data, error } = await api.data.updateOne("posts", id, updates);

    if (ok) {
      // Revalidate the post and list
      mutate(`posts/${id}`);
      mutate("posts");
    }

    return { ok, data, error };
  }

  return { updatePost };
}
```

### Optimistic Update

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

  async function updatePost(id: number, updates: Partial<Post>) {
    // Optimistic: update immediately
    const originalPosts = [...posts];
    setPosts((prev) =>
      prev.map((p) => (p.id === id ? { ...p, ...updates } : p))
    );

    // Actual update
    const { ok } = await api.data.updateOne("posts", id, updates);

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

    return { ok };
  }

  return { posts, updatePost };
}
```

## 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 field update
const { ok, data } = await api.data.updateOne("posts", 1, {
  title: "Updated Title",
  updated_at: new Date().toISOString(),
});

// Update with relation changes
await api.data.updateOne("posts", 1, {
  status: "published",
  published_at: new Date().toISOString(),
  category: { $set: 3 },        // Change category
  tags: { $add: [7, 8] },       // Add new tags
});

// Bulk update: mark old drafts as archived
await api.data.updateMany(
  "posts",
  {
    status: { $eq: "draft" },
    created_at: { $lt: "2024-01-01" },
  },
  { status: "archived" }
);

// Toggle boolean field
const post = await api.data.readOne("posts", 1);
if (post.ok) {
  await api.data.updateOne("posts", 1, {
    featured: !post.data.featured,
  });
}
```

## Common Patterns

### Upsert (Update or Insert)

```typescript
async function upsert(
  api: Api,
  entity: string,
  where: object,
  data: object
) {
  const { data: existing } = await api.data.readOneBy(entity, { where });

  if (existing) {
    r

Related in Backend & APIs