Entities
An entity is whatever your ORM already gives you: a TypeORM @Entity() class, a Prisma marker class, a Mongoose model, or a MikroORM @Entity() class. Kavo doesn't ask you to declare anything a second time. It reads the entity's own metadata (columns, relations, the primary key) through the ORM adapter you installed, and derives everything else from that.
// book.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class Book {
@PrimaryGeneratedColumn()
id!: number;
@Column()
title!: string;
@Column()
author!: string;
}@Kavo()
One decorator turns an empty controller into a full CRUD surface for that entity:
@Kavo(Book)
@Controller("books")
export class BookController {}@Kavo(Entity, config?) runs at class-definition time, the moment Nest's router scan can see the generated methods. It does three things: builds the entity's operation registry, generates one route per enabled operation, and binds a typed DefaultKavoService that both the generated routes and your own code can call. config is entirely optional. Every field it accepts falls back to an entity-derived default, which is why the example above works with no config object at all.
Outside Nest, the same thing happens through createCrud(Entity, config?). @Kavo is sugar over it for the Nest binding (see Routes & controllers).
Zero-config behavior
With no config, Kavo derives everything it needs from the entity's own metadata:
- Writable columns (
create/update/patchbodies): every scalar column that isn't database-generated. An auto-increment id or a@CreateDateColumnis excluded automatically. Sending one in a request body is silently stripped, not an error. - Readable columns (responses): every scalar column, plus any virtual fields your ORM reports.
- Filterable, sortable, and selectable fields: every scalar column, unless you narrow one of the allowed explicitly.
- Includable relations: none, until you name one in
include.fields. This is the one allowlist that defaults closed rather than open (ADR-0028).
None of this requires a schema class, a service, or a repository. See Schemas for what registering one narrows, and Quick start for the end-to-end walkthrough.
What Kavo needs from an entity
- At least one primary column. A single primary column is the common case, reported as
idField.@kavo/typeormalso supports a composite primary key, two or more@PrimaryColumns, reported ascompositeIdFields; the other ORM adapters still require exactly one. See Composite primary keys for the route-id encoding, the creatable/updatable split, and what still doesn't work. - Scalar columns and relations, distinguished by the ORM's own metadata. A relation-shaped property is never treated as a writable scalar. Association happens by id, not by embedding a related object in the body (ADR-0014).
- A registered identity the ORM can resolve at runtime. For TypeORM and MikroORM, that's the decorated class itself. Mongoose uses the model, since it already is the identity (ADR-0018). Prisma needs a small marker class, since it generates no runtime class per model (ADR-0017). See your ORM's integration page for the exact shape.
An entity that never goes through @Kavo/createCrud can still get served as a relation target included from another entity, through a derived, unconfigured projection of its own columns.