ServerMetadata
Metadata Hooks
EntityHook and FieldHook logic, execution patterns, and type mapping.
The metadata hooks connect logical model changes to physical database operations.
EntityHook
Located in features/metadata-admin/, manages table lifecycle.
after:create
When a new Entity is created:
- Extract
orgSlugfromHandlerContext - Invoke
createModelwith default fields:
| Field | Type | Purpose |
|---|---|---|
id | UUID | Primary key |
created_at | DateTime | Creation timestamp |
updated_at | DateTime | Last update timestamp |
created_by_id | UUID | Creator user ID |
updated_by_id | UUID | Last updater user ID |
org_id | UUID | Tenant identifier |
after:delete
When an Entity is deleted:
- Invoke
dropModelto remove physical table - Warning: Destructive, cannot be undone
// Entity hook
after: {
create: async (ctx) => {
await createModel(ctx.org, ctx.result);
},
delete: async (ctx) => {
await dropModel(ctx.org, ctx.params.where.id);
},
}FieldHook
Located in features/metadata-admin/, manages column lifecycle.
The dropColumn Flag
By default, deleting a Field does not drop the database column. To force drop, pass dropColumn: true:
// Client request to delete and drop column
await client.field.deleteOne.$post({
json: {
where: { id: "field_123" },
meta: { dropColumn: true },
},
});Type Mapping
CRM types mapped to database types:
| CRM Type | DB Type | Notes |
|---|---|---|
TEXT | VARCHAR(255) | Short text |
EMAIL | VARCHAR(255) | With validation |
URL | VARCHAR(500) | |
LONG_TEXT | TEXT | |
NUMBER | DECIMAL(18,2) | |
CHECKBOX | BOOLEAN | |
PICKLIST | VARCHAR(100) | |
LOOKUP | UUID | Foreign key |
LOOKUP Relationships
For LOOKUP fields, the hook configures foreign key:
fieldSpec.references = {
schema: org,
table: targetEntityName,
column: "id",
onDelete: "SET NULL",
};Error Handling
Hooks use Effect.tryPromise. If DDL fails, the hook returns CrudError. Depending on CrudPolicy, this may roll back the metadata transaction.