Use when inserting new records into a Bknd entity via the SDK or REST API. Covers createOne, createMany, creating with relations ($set), response handling, error handling, and common patterns for client-side record creation.
npx claudepluginhub cameronapak/bknd-expert --plugin bknd-research-skillsThis skill uses the workspace's default tool permissions.
Insert new records into your Bknd database using the SDK or REST API.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Insert new records into your Bknd database using the SDK or REST API.
bknd-create-entity first)UI steps: Admin Panel > Data > Select Entity > Click "+" or "Add" > Fill form > Save
import { Api } from "bknd";
const api = new Api({
host: "http://localhost:7654", // Your Bknd server
});
// If auth required:
api.updateToken("your-jwt-token");
Use createOne(entity, data):
const { ok, data, error } = await api.data.createOne("posts", {
title: "My First Post",
content: "Hello world!",
published: false,
});
if (ok) {
console.log("Created post:", data.id);
} else {
console.error("Failed:", error.message);
}
The response object:
type CreateResponse = {
ok: boolean; // Success/failure
data?: { // Created record (if ok)
id: number; // Auto-generated ID
// ...all fields with defaults applied
};
error?: { // Error info (if !ok)
message: string;
code: string;
};
};
Link to existing related records using $set:
// Link to single related record (many-to-one)
const { data } = await api.data.createOne("posts", {
title: "New Post",
author: { $set: 1 }, // Link to user with ID 1
});
// Link to multiple related records (many-to-many)
const { data } = await api.data.createOne("posts", {
title: "Tagged Post",
tags: { $set: [1, 2, 3] }, // Link to tag IDs 1, 2, 3
});
// Combine both
const { data } = await api.data.createOne("posts", {
title: "Complete Post",
content: "Full content here",
author: { $set: userId },
category: { $set: categoryId },
tags: { $set: [tagId1, tagId2] },
});
Use createMany(entity, data[]):
const { ok, data } = await api.data.createMany("tags", [
{ name: "javascript" },
{ name: "typescript" },
{ name: "bknd" },
]);
// data is array of created records
console.log("Created", data.length, "tags");
curl -X POST http://localhost:7654/api/data/posts \
-H "Content-Type: application/json" \
-d '{"title": "New Post", "content": "Hello!"}'
curl -X POST http://localhost:7654/api/data/posts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"title": "Protected Post"}'
curl -X POST http://localhost:7654/api/data/tags \
-H "Content-Type: application/json" \
-d '[{"name": "tag1"}, {"name": "tag2"}]'
curl -X POST http://localhost:7654/api/data/posts \
-H "Content-Type: application/json" \
-d '{"title": "Post", "author": {"$set": 1}}'
import { useApp } from "bknd/react";
function CreatePostForm() {
const { api } = useApp();
const [title, setTitle] = useState("");
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
setError(null);
const { ok, data, error: apiError } = await api.data.createOne("posts", {
title,
published: false,
});
setLoading(false);
if (ok) {
console.log("Created:", data.id);
setTitle("");
} else {
setError(apiError.message);
}
}
return (
<form onSubmit={handleSubmit}>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Post title"
required
/>
<button type="submit" disabled={loading}>
{loading ? "Creating..." : "Create Post"}
</button>
{error && <p className="error">{error}</p>}
</form>
);
}
import { useApp } from "bknd/react";
import useSWR, { mutate } from "swr";
function PostsList() {
const { api } = useApp();
const { data: posts } = useSWR("posts", () =>
api.data.readMany("posts").then((r) => r.data)
);
async function createPost(title: string) {
const { ok, data } = await api.data.createOne("posts", { title });
if (ok) {
// Revalidate the posts list
mutate("posts");
}
return { ok, data };
}
return (
<div>
<CreateForm onCreate={createPost} />
<ul>
{posts?.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
import { Api } from "bknd";
const api = new Api({ host: "http://localhost:7654" });
// Authenticate first (if required)
await api.auth.login({ email: "user@example.com", password: "password" });
// Create a user
const { data: user } = await api.data.createOne("users", {
email: "newuser@example.com",
name: "New User",
role: "author",
});
// Create a post linked to user
const { data: post } = await api.data.createOne("posts", {
title: "My First Blog Post",
content: "This is the content of my post.",
published: true,
author: { $set: user.id },
});
// Create tags
const { data: tags } = await api.data.createMany("tags", [
{ name: "intro" },
{ name: "tutorial" },
]);
// Link tags to post (update after creation)
await api.data.updateOne("posts", post.id, {
tags: { $set: tags.map((t) => t.id) },
});
console.log("Created post:", post.id, "with tags:", tags.length);
Bknd applies defaults for omitted fields:
// Entity definition
entity("posts", {
title: text().required(),
status: text({ default_value: "draft" }),
view_count: number({ default_value: 0 }),
created_at: date({ default_value: "now" }),
});
// Create with minimal data
const { data } = await api.data.createOne("posts", {
title: "Just a title", // Only required field
});
// Result includes defaults
console.log(data);
// {
// id: 1,
// title: "Just a title",
// status: "draft", // default applied
// view_count: 0, // default applied
// created_at: "2025-01-20T..." // default applied
// }
Bknd validates data against schema:
// Entity with constraints
entity("users", {
email: text().required().unique(),
name: text(),
});
// Missing required field
const { ok, error } = await api.data.createOne("users", {
name: "No Email",
});
// ok: false, error: { message: "email is required" }
// Duplicate unique field
const { ok, error } = await api.data.createOne("users", {
email: "existing@example.com", // Already exists
});
// ok: false, error: { message: "UNIQUE constraint failed" }
async function createOrFind(
api: Api,
entity: string,
data: object,
uniqueField: string
) {
// Try to find existing
const { data: existing } = await api.data.readOneBy(entity, {
where: { [uniqueField]: { $eq: data[uniqueField] } },
});
if (existing) {
return { created: false, data: existing };
}
// Create new
const { data: created } = await api.data.createOne(entity, data);
return { created: true, data: created };
}
// Usage
const { created, data } = await createOrFind(
api,
"users",
{ email: "user@example.com", name: "User" },
"email"
);
function useCreatePost() {
const { api } = useApp();
const [posts, setPosts] = useState<Post[]>([]);
async function createPost(title: string) {
// Optimistic: add temp post immediately
const tempId = `temp-${Date.now()}`;
const tempPost = { id: tempId, title, status: "creating" };
setPosts((prev) => [...prev, tempPost]);
// Actual create
const { ok, data } = await api.data.createOne("posts", { title });
if (ok) {
// Replace temp with real
setPosts((prev) =>
prev.map((p) => (p.id === tempId ? data : p))
);
} else {
// Remove temp on failure
setPosts((prev) => prev.filter((p) => p.id !== tempId));
}
return { ok, data };
}
return { posts, createPost };
}
async function batchCreate(
api: Api,
entity: string,
items: object[],
onProgress?: (done: number, total: number) => void
) {
const results = [];
const batchSize = 100;
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const { data } = await api.data.createMany(entity, batch);
results.push(...data);
onProgress?.(Math.min(i + batchSize, items.length), items.length);
}
return results;
}
// Usage
const items = generateItems(500);
await batchCreate(api, "products", items, (done, total) => {
console.log(`Progress: ${done}/${total}`);
});
Problem: NOT NULL constraint failed
Fix: Include all required fields:
// Entity: email is required
entity("users", { email: text().required(), name: text() });
// Wrong - missing email
await api.data.createOne("users", { name: "Test" });
// Correct
await api.data.createOne("users", { email: "test@example.com", name: "Test" });
Problem: FOREIGN KEY constraint failed
Fix: Ensure related record exists:
// Wrong - author ID doesn't exist
await api.data.createOne("posts", { title: "Post", author: { $set: 999 } });
// Correct - verify first or handle error
const { data: author } = await api.data.readOne("users", authorId);
if (author) {
await api.data.createOne("posts", { title: "Post", author: { $set: authorId } });
}
Problem: UNIQUE constraint failed
Fix: Check before create or handle error:
// Option 1: Check first
const { data: exists } = await api.data.exists("users", {
email: { $eq: email },
});
if (exists.exists) {
throw new Error("Email already registered");
}
await api.data.createOne("users", { email, name });
// Option 2: Handle error
const { ok, error } = await api.data.createOne("users", { email, name });
if (!ok && error.message.includes("UNIQUE")) {
throw new Error("Email already registered");
}
Problem: Assuming success without checking.
Fix: Always check ok:
// Wrong - assumes success
const { data } = await api.data.createOne("posts", { title });
console.log(data.id); // data might be undefined!
// Correct
const { ok, data, error } = await api.data.createOne("posts", { title });
if (!ok) {
throw new Error(error.message);
}
console.log(data.id);
Problem: Unauthorized error.
Fix: Authenticate first or check permissions:
// Login first
await api.auth.login({ email, password });
// Or set token
api.updateToken(savedToken);
// Then create
await api.data.createOne("posts", { title });
After creating, verify the record:
const { ok, data } = await api.data.createOne("posts", { title: "Test" });
if (ok) {
// Read back to verify
const { data: fetched } = await api.data.readOne("posts", data.id);
console.log("Created and verified:", fetched);
}
Or via admin panel: Admin Panel > Data > Select Entity > Find new record.
DO:
ok field before using data$setDON'T:
$set$set linking