Skip to main content

Entity Definitions

Entities are the core building blocks of your Radish blueprint. Each entity represents a data type in your application.

Basic Entity Structure

{
"entities": {
"EntityName": {
"label": "Display Name",
"description": "Description of the entity",
"plural": "entities",
"fields": {},
"filters": [],
"indexes": []
}
}
}

Entity Properties

label

Human-readable name for the entity (singular form).

{
"label": "Task"
}

description

Brief description of what this entity represents.

{
"description": "A todo task that can be assigned to users"
}

plural

Plural form used for collections and endpoints.

{
"plural": "tasks"
}

ownership

Determines entity ownership model:

  • user (default) - Entity belongs to a specific user (has ownerId)
  • system - System-level entity, no owner
{
"ownership": "system"
}

extends

Inherit fields from another entity or base type.

{
"extends": "EntityBase"
}

Built-in base types:

  • EntityBase - Standard entity with id, ownerId, timestamps
  • SystemEntityBase - System entity with id, timestamps (no owner)
  • ContentBase - Extends EntityBase with title, slug, tags, status, etc.

versioning

Enable version tracking:

  • full - Complete snapshots with revert capability
  • simple - Lightweight audit logs
{
"versioning": "simple"
}

scope

Enable relationship-based access control (Layer 3). Two modes:

Through-entity — access via ownership of a related entity:

{
"scope": {
"field": "appId",
"through": "App",
"ownerField": "ownerId"
}
}

Direct-match (tenancy) — access via a shared field on the user:

{
"scope": {
"field": "orgId",
"matchUserField": "orgIds"
}
}

Supports array match: if user.orgIds is ["org1", "org2"], records where orgId is either value are accessible.

See Access Control for full documentation.

Enable search index integration for this entity:

{
"search": {
"enabled": true,
"engine": "typesense",
"indexName": "products",
"fields": {
"searchable": ["name", "description"],
"filterable": ["brand", "price"],
"sortable": ["price", "createdAt"],
"facetable": ["brand"]
},
"sync": "inline"
}
}
PropertyDefaultDescription
enabledfalseEnable search indexing
engine"typesense"Search engine (typesense, elastic, opensearch, meilisearch, mongoAtlas)
indexNameentity pluralSearch index name
fields.searchable[]Full-text searchable fields
fields.filterable[]Fields for filtering
fields.sortable[]Fields for sorting
fields.facetable[]Fields for faceted search
sync"inline"Sync mode: inline (immediate) or background

Example Entity

{
"entities": {
"Product": {
"label": "Product",
"description": "Product in the catalog",
"plural": "products",
"ownership": "system",
"versioning": "simple",
"scope": {
"field": "orgId",
"matchUserField": "orgIds"
},
"search": {
"enabled": true,
"engine": "typesense",
"fields": {
"searchable": ["name", "description", "brand"],
"filterable": ["brand", "price", "status"],
"sortable": ["price", "name", "createdAt"]
}
},
"fields": {
"name": {
"type": "string",
"required": true,
"label": "Product Name",
"search": true
},
"description": {
"type": "string",
"optional": true
},
"brand": {
"type": "string",
"label": "Brand"
},
"price": {
"type": "float",
"required": true
},
"costPrice": {
"type": "float",
"access": { "read": ["ADMIN", "MANAGER"] }
},
"status": {
"type": "enum",
"values": ["draft", "published", "archived"],
"default": "draft"
},
"orgId": {
"type": "objectId",
"ref": "Org",
"required": true
},
"images": {
"type": "url[]",
"default": []
}
},
"filters": ["status", "brand", "orgId"],
"indexes": [
{ "fields": ["orgId", "status"] },
{ "fields": ["brand"] }
]
}
}
}

This example shows:

  • Tenant scoping: scope.matchUserField for multi-org access
  • Search: Typesense integration with searchable/filterable/sortable fields
  • Field access: costPrice restricted to ADMIN and MANAGER roles
  • url[] type: Array of URLs for images
  • Enum with default: Status field with draft/published/archived

Next Steps