Basic entity
Wire one event-sourced aggregate end-to-end with the framework’s built-in CRUD
helpers. The recipe ships a task entity with the standard verbs — create,
update, delete, restore, list, detail — via registerEntityCrud, plus
soft-delete enabled.
This is the smallest useful sample: it demonstrates the path from
createEntity({ fields }) to a working API surface without writing any Zod
schemas or handler bodies by hand.
What it shows
Section titled “What it shows”createEntitywith field factories — typed text, boolean, number fields with options likerequired,sortable,softDelete.registerEntityCrud— entity + standard handlers in one call; skip verbs inverbsand register explicitly when access differs per verb.- Per-verb access rules — different roles can create vs. update vs. delete. Editor and User roles can write; only Admin can delete or restore.
- Soft delete —
softDelete: trueon the entity gives the executor anisDeletedcolumn and thedeletehandler flips it instead of dropping the row.restoreflips it back.
Feature composition
Section titled “Feature composition”task-management → single feature, single `task` entity, six CRUD handlersNo bundled features required — this is the baseline before you add
r.requires("tenant"), auth, or cross-feature extensions.
- Define fields with
createEntity({ fields: { … } }). - Call
registerEntityCrud(r, "task", taskEntity, { write, read, verbs }). - Client calls
task-management:write:task:create→ row + event appended. deletesoft-flipsisDeleted;restoreflips back;listexcludes deleted rows by default.
When to reach for it
Section titled “When to reach for it”You’re starting a new feature with a single entity and want CRUD without
inventing your own handlers. Replace any single line with an explicit
r.writeHandler({ name, schema, handler }) when you outgrow the defaults
— see custom-handlers for that path.
bun kumiko test integration samples/basic-entityIntegration tests under src/__tests__/ exercise list-with-sort,
soft-delete + restore, and per-verb access boundaries (Editor vs Admin).
Related samples
Section titled “Related samples”- custom-handlers — replace generated CRUD helpers with explicit handlers.
- field-access — per-field read/write rules on top of the same entity pattern.
- custom-fields-basic — tenant- defined extra columns without migrations.
Source code
Section titled “Source code”The feature entry point — embedded straight from the source file, so the code here is exactly what runs. Multi-file samples keep their remaining files next to it on GitHub (link below):
// Basic Entity Sample// Shows: how to wire one event-sourced aggregate end-to-end via r.crud.// Custom logic or per-verb access? Skip a verb in `verbs` and register explicitly.
import { createBooleanField, createEntity, createTextField, defineEntityDeleteHandler, defineEntityRestoreHandler, defineFeature,} from "@cosmicdrift/kumiko-framework/engine";
export const taskEntity = createEntity({ table: "read_sample_tasks", fields: { title: createTextField({ required: true }), description: createTextField(), // sortable: true so the integration test can exercise list-with-sort. status: createTextField({ sortable: true }), isArchived: createBooleanField({ default: false }), }, softDelete: true,});
const editorWrite = { access: { roles: ["Admin", "User"] } } as const;const adminWrite = { access: { roles: ["Admin"] } } as const;const openRead = { access: { openToAll: true } } as const;
export const taskFeature = defineFeature("tasks", (r) => { r.crud("task", taskEntity, { write: editorWrite, read: openRead, verbs: { delete: false, restore: false }, }); r.writeHandler(defineEntityDeleteHandler("task", taskEntity, adminWrite)); r.writeHandler(defineEntityRestoreHandler("task", taskEntity, adminWrite));});📄 On GitHub: samples/recipes/basic-entity/src/feature.ts