Model your data: entities, fields, relations
Create entities, add typed fields, set required, unique and indexed, and link records with relationships. Every change updates your REST API at once.
Last updated
Your data model is the set of entities and fields in your project. An entity is a table. A field is a typed column on it. When you create or change either one, Anythink changes the database and your REST API in the same step. There is no migration to write and no deploy to wait for.
| Concept | What it is | Example |
|---|---|---|
| Entity | A table of records, with its own REST API and permissions | customers |
| Field | One typed value on every record of an entity | email, signed_up_at |
| Relationship | A link from a field on one entity to records of another | orders.customer points at customers |
This page walks through the whole life of a data model: creating entities, adding fields, setting constraints, linking entities, changing things later, and the limits to design around.
Create an entity
Create one entity per kind of thing you store: customers, orders, blog_posts.
In the Anythink dashboard
- Go to Settings › Data Model.
- Select Add Entity. The Create New Entity drawer opens.
- Enter an Entity Name, for example
customers. - Leave the options at their defaults for now. They're explained below, and you can change them later.
- Select Create Entity.
With the CLI
anythink entities create customers
Creating entity 'customers'...
✓ Entity customers created (id: 653).
Check what was created:
anythink entities get customers
The output lists the entity's settings and its fields.
With an AI assistant (MCP)
Create an entity called customers.
The assistant uses the cli tool to run entities create customers and confirms with entities get customers.
Name rules
| Rule | Detail |
|---|---|
| Characters | Lower-case letters, numbers and underscores. Start with a letter. |
| Case | Anythink lower-cases the name and replaces any character that isn't a letter or number with an underscore, so Customer Orders becomes customer_orders. |
| Length | 63 characters or fewer. |
| Reserved | Names starting with anythink_, and the names of Anythink's built-in resources, are refused. |
| Unique | Each name can be used once per project. |
| Permanent | An entity can't be renamed. To change a name, create a new entity and move the records across. |
Use plural, snake_case names. The name appears in your API paths (/entities/customers/items) and in permission names (customers:read), so it's worth choosing well.
What gets created
Creating an entity sets up three things at once:
- A database table named after the entity, with an auto-incrementing
idandcreated_atandupdated_attimestamps that Anythink manages. These system fields are locked: you can't edit or delete them. - A REST API for the entity: list, get, create, update and delete endpoints, available as soon as the entity exists. See Call the instant REST API.
- Four permissions:
customers:read,customers:create,customers:updateandcustomers:delete. Administrator roles get all four automatically. Every other role gets none, so grant them under Roles and permissions before your app's users can call the API.
Entity options
| Option (dashboard) | CLI | What it does |
|---|---|---|
| Enable Row-Level Security | --rls |
Records are only visible to users granted access. See Groups and row-level security. |
| Public | --public |
The entity can be read without signing in. Only fields marked publicly searchable are exposed. |
| Lock New Records | --lock-records |
Every new record is created locked. A locked record can't be changed or deleted through the API. |
| Lock Schema | Dashboard only | Nobody can add, change or delete the entity's fields, and the entity can't be deleted. |
| Add to Menu | Dashboard only | Adds the entity to your dashboard menu when you create it. |
Add fields
Add one field per piece of information you want on each record. An entity with no fields beyond id and the timestamps can't hold much, so this is where the model takes shape.
In the Anythink dashboard
- Go to Settings › Data Model and select the entity.
- Select Add Field. The Create New Field drawer opens.
- Pick a field type, such as Small text or Integer.
- Enter a Name (for example
email). Add a Label, Description and Default Value if you want them. - Choose a display type and set any display options (see Display options).
- Tick any constraints you need (see Required, unique, indexed and name field).
- Select Create Field.
The field shows up on the entity straight away, and in the REST API.
With the CLI
anythink fields add customers email --type varchar --unique --required
anythink fields add customers notes --type text
anythink fields add customers plan --type varchar --display select --options "Free,Pro,Team"
anythink fields add customers signed_up_at --type timestamp
✓ Field email (id: 3163) added to customers.
Leave out --display and the CLI picks the usual display type for the field type (the table below shows the default first). List a table's fields with anythink fields list customers, or inspect one with anythink fields get customers email.
With an AI assistant (MCP)
Add these fields to customers: email (unique, required), notes (long text), plan (a select with Free, Pro and Team) and signed_up_at (timestamp).
The assistant uses the cli tool to run one fields add per field.
Field names
Field names follow the entity rules, with two extra restrictions: you can't use id, locked, created_at or updated_at (or other names Anythink reserves for itself), and a name can't start with anythink. A name is permanent once the field exists, and so is its type.
A Label is the friendly name shown in the dashboard (up to 100 characters). A Description is a note for your team (up to 500 characters). Neither changes the API.
Field types
Choose the type by what the value is. The type decides how the value is stored, and which display types you can pick.
| Type (dashboard) | --type / API value |
Stored as | Display types |
|---|---|---|---|
| Small text | varchar |
Text (VARCHAR) |
input (default), select, entity-select |
| Large text | text |
Text (TEXT) |
textarea (default), rich-text |
| Integer | integer |
Whole number (INTEGER) |
input (default), select |
| Big Integer | bigint |
Large whole number (BIGINT) |
input (default), select |
| Decimal | decimal |
Exact decimal number (DECIMAL) |
input (default), select |
| Boolean | boolean |
True or false (BOOLEAN). Defaults to false |
checkbox (default), radio |
| Date | date |
Calendar date, no time (DATE) |
short-date (default), long-date |
| Timestamp | timestamp |
Date and time with time zone (TIMESTAMPTZ) |
timestamp |
| JSON | jsonb |
JSON document (JSONB) |
jsonb (JSON editor) |
| Geo | geo |
Location, stored as JSON (JSONB) |
geo (map) |
| Select with Multiple ticked | varchar[], integer[], decimal[] |
Array of values | select |
| File Upload | file |
Link to an uploaded file | file |
| User | user |
Link to a user in your project | user |
| One-to-One, Many-to-One | one-to-one, many-to-one |
Link to one record of another entity | relationship |
| One-to-Many, Many-to-Many | one-to-many, many-to-many |
Links to many records of another entity | relationship |
| Dynamic Reference | dynamic-reference |
Text reference to a record of any entity | dynamic-reference |
Choosing a type:
- Money and measurements. Use
decimal, not a float: it keeps exact values. For prices you can also store whole pence or cents in anintegerorbigint. - Counts, ages and IDs.
integer. Usebigintfor very large numbers such as epoch timestamps. - Short strings you filter on.
varcharwithselectfor a fixed list of choices, such as a status. - Anything longer than a line.
text, withrich-textif editors need Markdown. - When something happened.
timestamp. Usedateonly when the time of day is meaningless, such as a birthday. - Free-form structure.
jsonb, when the shape varies between records and you don't need to filter on its contents.
Types that point at other data (file, user, the four relationship types and dynamic-reference) are covered in Link entities with relationships and in Upload and manage files. For a longer description of each type, see Field types reference.
Display options
The display type controls how the field looks in the dashboard's forms and grids. It doesn't change the stored value or the REST API. Each display type has its own options, set in the Create New Field drawer or through the options object of the API.
| Display type | Options |
|---|---|
input |
Placeholder, Addon before, Addon after (for example a currency symbol), Masked to hide the value as it is typed |
select |
The list of choices (each a label and a value), Multiple, Placeholder, Addon before, Addon after |
textarea |
Rows |
rich-text |
Rows, Enable Markdown Support |
file |
Allowed file types, Number of files allowed for upload, Public |
Give a select field its choices in one go from the CLI. A plain list uses each word as both label and value. Use label=value to store something different from what people see:
anythink fields add orders status --type varchar --display select --options "Pending=pending,Shipped=shipped,Delivered=delivered"
Tick Multiple (CLI: --multiple) to let one record hold several choices. Anythink then stores the field as an array (varchar[], integer[] or decimal[], matching the type you picked).
anythink fields add customers interests --type varchar --display select --multiple --options "Running,Cycling,Rugby"
Required, unique, indexed and name field
These switches sit on every field. Some are about data quality, some about speed, and some about how a record is shown.
| Switch (dashboard) | CLI flag | What it does |
|---|---|---|
| Required field | --required |
The value can't be empty. The dashboard's record form won't submit without it. |
| Unique field | --unique |
No two records can share a value. A duplicate write is refused with a 400. |
| Immutable field | Dashboard and API | Once a record is saved, updates leave this field's value as it was. |
| Indexed | --indexed |
Adds a database index, so filtering and sorting on the field stays fast on large tables. |
| Name field | --name-field |
Marks the field that identifies a record visually in the dashboard. It implies Searchable. |
| Searchable | --searchable |
Includes the field in the entity's search, and makes it usable in API search queries. |
| Publicly Searchable | --public |
Lets signed-out visitors search this field on a public entity. It needs Searchable, and the request is refused without it. |
Things to know:
- Choose Unique and Indexed when you create the field. Unique is applied to the database as the column is made, so decide it up front.
- Unique and nulls. Unique compares values, so any number of records can leave an optional unique field empty.
- Defaults fill old records. When you add a field with a default value to an entity that already has records, existing records get the default. Without one they get an empty value. Boolean fields default to
false. - Required relationships. A required relationship field can't use Set null for its on-delete behaviour, since that would empty a required value.
- Pick one name field per entity. Choose the field that makes a record recognisable at a glance: a title, a name or a reference.
Searchable fields feed Search records across your entities, which also covers public search.
Link entities with relationships
A relationship is a field that points at records of another entity: an order belongs to a customer, a post has many tags. You create one by adding a field of a relationship type.
| Type | Meaning | Example |
|---|---|---|
many-to-one |
Many records here point at one record there | Many orders belong to one customer |
one-to-many |
One record here has many records there | One customer has many orders |
one-to-one |
Each record points at exactly one record there, and no two share it | One user_profile per customer |
many-to-many |
Records on both sides can have several partners | posts and tags |
Each relationship field asks for a target entity and an on-delete behaviour: what happens to a record when the record it points at is deleted. The choices are CASCADE (delete it too), SET NULL (clear the link), RESTRICT (refuse the delete) and SET DEFAULT. The CLI defaults to CASCADE.
anythink fields add orders customer --type many-to-one --target customers --on-delete "SET NULL"
The REST API stores the id of the related record in the field, and can expand and filter on related records. The relationships page covers every type in depth, including how to read, write and filter linked records: Connect entities with relationships.
Edit fields and entities
You can change a data model whenever you like. The API updates as soon as you save.
Edit a field
In the Anythink dashboard
- Go to Settings › Data Model and select the entity.
- Select the field. Fields marked System can't be edited.
- Change what you need and select Update Field.
With the CLI
Edit fields in the Anythink dashboard, using the steps above.
With an AI assistant (MCP)
Edit fields in the Anythink dashboard, using the steps above.
What you can and can't change:
| You can change | You can't change |
|---|---|
| Label, description, default value | Name |
| Display type (to one compatible with the field's type) and display options | Field type |
| Select choices, Multiple | A relationship's target entity |
| Required, searchable, publicly searchable | System fields (id, created_at, updated_at) |
| Width (see Field order and display) | Anything, while the entity's Lock Schema is on |
Changing a display type, select choices or other display options never alters stored values. A record that holds a value no longer in the choices keeps it. To change a field's type, add a new field of the type you want and copy the values across (a workflow or a short script works).
Edit an entity
Select the entity in Settings › Data Model, then select Edit to change Enable Row-Level Security, Public, Lock New Records or Lock Schema. The name stays fixed. With the CLI:
anythink entities update customers --rls true --public false --lock-records false
Turning on row-level security hides records that have no grants. Read Groups and row-level security before you switch it on for an entity that already holds data. Lock New Records affects only records created after you turn it on.
Delete an entity
Deleting an entity permanently removes the entity, every record in it, its search index, and its four permissions. Its API endpoints stop working at once and return 404.
In the Anythink dashboard
- Go to Settings › Data Model.
- Select the entity, then select Delete.
- Confirm Delete Entity.
With the CLI
anythink entities delete temp_data --yes
An entity with Lock Schema on can't be deleted. Turn the option off first if you really mean to remove it.
With an AI assistant (MCP)
Delete the temp_data entity and everything in it.
Field order and display
Fields appear in the dashboard's record form and grid in the order you set. Drag them into the order you want.
In the Anythink dashboard
- Go to Settings › Data Model and select the entity.
- Drag a field by its handle to a new position. Locked and system fields have no handle.
The new order saves automatically. Each field also has a Width (from 1 to 1000, default 100) that sets how wide it appears in the dashboard.
With the CLI
There's no dedicated command, but the field-order endpoint is one call. Send each field's id and its new position:
anythink fields list customers
anythink fetch /entities/customers/fields/reorder --method PUT \
--body '[{"field_id": 3163, "sort_order": 1}, {"field_id": 3164, "sort_order": 2}]'
With an AI assistant (MCP)
On customers, put email first, then plan, then notes.
The assistant lists the field ids and calls the reorder endpoint through the cli tool.
Order and width are presentation only. They don't change the API response or the stored data. The REST API returns the same fields whatever order you pick.
Read the model from your app
Your app can read the model it sits on: an entity with its fields, how its fields and relationships are laid out, and the workflows a user can start from its records. All three calls need anythink_entities:read, and they only show fields your role is allowed to see.
| Endpoint | Returns |
|---|---|
GET /entities/id/{id} |
One entity by its numeric id, with its fields. Same shape as GET /entities/{name}. |
GET /entities/{name}/hierarchy |
A list of the entity's fields and relationships |
GET /entities/{name}/workflows |
The enabled workflows whose Manual trigger includes this entity |
In the Anythink dashboard
Settings › Data Model shows the same model. The record page lists the workflows you can run on a record in its Workflows section.
With the CLI
anythink fetch /entities/id/365
anythink fetch /entities/orders/hierarchy
anythink fetch /entities/orders/workflows
The raw request:
GET /org/{project_id}/entities/orders/hierarchy
Each item in the hierarchy list has a name, a path, a type, is_system and a definition. The type is field for an ordinary field, or the relationship kind (one-to-many or many-to-one) for a link to another entity, which also carries target_entity. The definition is the field's full definition, including display_type, is_required and, for a relationship, the relationship details.
workflows returns each matching workflow's id, name, description, group, enabled and triggers. Read /workflows/{id} for its steps. See Triggers and scheduling.
An id or name that doesn't exist returns 404.
With an AI assistant (MCP)
Show me how the orders entity connects to other entities.
The assistant calls fetch /entities/orders/hierarchy through the cli tool.
Limits
| Limit | Value |
|---|---|
| Entity name | 63 characters, unique in the project, can't start with anythink_ |
| Field name | 63 characters, unique on the entity, can't be id, locked, created_at or updated_at, can't start with anythink |
| Field label | 100 characters |
| Field description | 500 characters |
| Field width | 1 to 1000 |
| Renaming | Entities and fields can't be renamed |
| Changing a field's type | Not possible. Add a new field of the type you want and copy the values across |
| Deleting | Permanent. Fields and entities have no undo |
| Locked entities | Under Lock Schema no field can be added, changed or deleted, and the entity can't be deleted |
| Built-in fields | id, created_at and updated_at exist on every entity and can't be edited or deleted |
Next steps
- Connect entities with relationships: link records across entities and filter on them.
- Field types reference: a longer description of every field type.
- Call the instant REST API: read and write records through the API your model generates.
- Roles and permissions: give your users access to the entities you create.
- Groups and row-level security: show each user only their own records.
- Search records across your entities: make fields searchable.
- Upload and manage files: add file fields.