home / skills / cameronapak / bknd-skills / bknd-create-entity

bknd-create-entity skill

/skills/bknd-create-entity

This skill helps you create new entities in Bknd, offering UI and code-mode workflows with type-safe definitions.

npx playbooks add skill cameronapak/bknd-skills --skill bknd-create-entity

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
6.3 KB
---
name: bknd-create-entity
description: Use when creating a new entity/table in Bknd. Covers entity definition with em() and entity(), primary key configuration, field basics, UI creation via admin panel, and code-first approach with type safety.
---

# Create Entity

Create a new entity (database table) in Bknd. Entities are the foundation of your data model.

## Prerequisites

- Bknd project initialized (`npx bknd create` or existing project)
- For code mode: TypeScript project with `bknd` package installed

## When to Use UI vs Code

### Use UI Mode When
- Exploring/prototyping quickly
- Non-developer or visual learner
- Making one-off changes
- Testing schema ideas before committing to code

### Use Code Mode When
- Version control needed
- Reproducible setups across environments
- Team collaboration
- CI/CD pipelines
- Type safety required

## UI Approach

### Step 1: Access Admin Panel

1. Start your Bknd server: `npx bknd run`
2. Open browser to `http://localhost:1337` (default port)
3. Navigate to **Data** section in sidebar

### Step 2: Create Entity

1. Click **+ Add Entity** button
2. Enter entity name (use plural, lowercase: `posts`, `users`, `comments`)
3. Configure primary key format:
   - **Integer** (default): Auto-incrementing ID
   - **UUID**: Universally unique identifier
4. Click **Create**

### Step 3: Add Fields

After entity creation, you're taken to the field editor:

1. Click **+ Add Field**
2. Select field type (text, number, boolean, date, enum, json)
3. Configure field options:
   - **Name**: snake_case (e.g., `first_name`, `created_at`)
   - **Required**: Toggle if field cannot be null
   - **Default Value**: Optional default
4. Click **Save Field**
5. Repeat for additional fields

### Step 4: Sync Schema

Click **Sync Database** to apply changes to the actual database.

## Code Approach

### Step 1: Import Dependencies

```typescript
import { em, entity, text, number, boolean, date, enumm, json } from "bknd";
```

### Step 2: Define Entity

Create your entity within `em()`:

```typescript
const schema = em({
  posts: entity("posts", {
    title: text().required(),
    content: text(),
    published: boolean({ default_value: false }),
    view_count: number({ default_value: 0 }),
  }),
});
```

### Step 3: Configure Primary Key (Optional)

Default is auto-incrementing integer. For UUID:

```typescript
const schema = em({
  posts: entity("posts", {
    title: text().required(),
  }, {
    primary_format: "uuid",
  }),
});
```

### Step 4: Export Types

Enable type-safe queries:

```typescript
const schema = em({
  posts: entity("posts", {
    title: text().required(),
    content: text(),
  }),
});

// Extract and declare types
type Database = (typeof schema)["DB"];
declare module "bknd" {
  interface DB extends Database {}
}
```

### Step 5: Use in App Configuration

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

const app = new App({
  data: schema,
  // ... other config
});
```

### Full Example

```typescript
import { App, em, entity, text, number, boolean, date } from "bknd";

const schema = em({
  users: entity("users", {
    email: text().required().unique(),
    name: text(),
    active: boolean({ default_value: true }),
  }),
  posts: entity("posts", {
    title: text().required(),
    content: text(),
    published: boolean({ default_value: false }),
    published_at: date(),
  }),
});

type Database = (typeof schema)["DB"];
declare module "bknd" {
  interface DB extends Database {}
}

const app = new App({
  data: schema,
});

export default app;
```

## Entity Naming Conventions

| Convention | Example | Notes |
|------------|---------|-------|
| Plural | `users`, `posts` | NOT `user`, `post` |
| Lowercase | `blog_posts` | NOT `BlogPosts` |
| snake_case | `user_profiles` | NOT `userProfiles` |

## Auto-Generated Fields

Every entity automatically includes:

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer/uuid | Primary key (format depends on config) |

**Note:** For `created_at`/`updated_at`, use the timestamps plugin or add manually:

```typescript
entity("posts", {
  title: text().required(),
  created_at: date({ default_value: "now" }),
  updated_at: date(),
})
```

## Common Pitfalls

### Entity Already Exists

**Error:** `Entity "posts" already defined`

**Fix:** Each entity name must be unique within `em()`. Check for duplicates.

### Invalid Entity Name

**Error:** `Invalid entity name`

**Fix:** Use lowercase letters, numbers, and underscores only. Must start with letter.

```typescript
// ✅ Valid
entity("posts", { ... })
entity("user_profiles", { ... })
entity("blog_posts_2024", { ... })

// ❌ Invalid
entity("Posts", { ... })        // No uppercase
entity("2024_posts", { ... })   // Can't start with number
entity("post-items", { ... })   // No hyphens
```

### Schema Not Syncing

**Problem:** Created entity in code but table doesn't exist in database.

**Fix:** Ensure you're using the schema in your App config:

```typescript
const app = new App({
  data: schema,  // Must pass schema here
});
```

Then restart the server - Bknd auto-syncs on startup.

### Missing Type Safety

**Problem:** `api.data.readMany("posts", ...)` has no type hints.

**Fix:** Add type declaration:

```typescript
type Database = (typeof schema)["DB"];
declare module "bknd" {
  interface DB extends Database {}
}
```

## Verification

### UI Mode
1. Check entity appears in Data section
2. Click entity to see fields
3. Try creating a test record

### Code Mode
```typescript
// After app starts, verify entity exists
const api = app.getApi();
const result = await api.data.readMany("posts");
console.log(result); // Should return { data: [] } for empty entity
```

### CLI Check
```bash
npx bknd debug routes
# Should show /api/data/posts endpoints
```

## DOs and DON'Ts

**DO:**
- Use plural, lowercase entity names
- Start with essential fields; add more later
- Add type declarations for type safety
- Use `primary_format: "uuid"` for distributed systems

**DON'T:**
- Use singular names (`user` instead of `users`)
- Use PascalCase or camelCase for entity names
- Create entities without at least one field
- Forget to sync database after UI changes

## Related Skills

- **bknd-add-field** - Add fields to existing entity
- **bknd-define-relationship** - Connect entities with relationships
- **bknd-modify-schema** - Rename or change entity configuration
- **bknd-delete-entity** - Safely remove an entity

Overview

This skill teaches how to create a new entity (database table) in Bknd using both the Admin UI and the code-first API. It covers entity definition with em() and entity(), primary key configuration, field basics, UI sync steps, and how to enable type-safe queries in TypeScript. Follow this to reliably add entities and keep schema changes reproducible across environments.

How this skill works

In UI mode you create an entity via the Data section in the admin panel, add fields, choose a primary key format (integer or UUID), and click Sync Database to apply changes. In code mode you define entities inside em() using entity() and type helpers (text, number, boolean, date, enumm, json), configure primary_format when needed, and export types to get type-safe API calls. On app startup Bknd uses the provided schema to create or sync tables.

When to use it

  • Quick prototyping or when a visual editor is preferred (UI mode).
  • When you need version-controlled, reproducible schemas (code mode).
  • To enforce type safety across your application (TypeScript + code mode).
  • When adding new domain objects that require database backing.
  • When preparing schema changes for CI/CD deployments.

Best practices

  • Use plural, lowercase, snake_case names for entities (e.g., users, blog_posts).
  • Prefer code mode for team projects, CI/CD, and version control.
  • Declare types from your schema so api.data.* calls are type-safe.
  • Start with essential fields and add additional fields iteratively.
  • Choose primary_format: 'uuid' for distributed systems, integer for simple auto-increment ids.

Example use cases

  • Create a users entity with email, name, and active flag for authentication.
  • Model blog posts with title, content, published boolean, and published_at date.
  • Add comments entity tied to posts where each comment stores author and body.
  • Rapidly prototype a new feature via the admin panel then port to code mode for production.
  • Switch primary key to UUID before scaling to multiple regions.

FAQ

What naming conventions should I follow for entities?

Use plural, lowercase, snake_case names. Start with a letter and avoid hyphens or uppercase characters.

How do I get type safety for api.data calls?

Export the schema types: derive Database = (typeof schema)["DB"] and declare module "bknd" { interface DB extends Database {} } to enable IDE hints and compile-time checks.

Why doesn't my code-defined entity appear in the database?

Ensure the schema is passed to new App({ data: schema }) and restart the server so Bknd can auto-sync the database.