Structures & Records¶
Overview¶
Structures and Records are the foundation of data management in Centrali. Think of structures as database schemas or data models, and records as the actual data rows that follow those schemas.
What are Structures?¶
A structure defines the shape and validation rules for your data. It's similar to: - A database table schema - A JSON Schema - A TypeScript interface - A class definition
Key Concepts¶
Properties: Fields that define the data structure - Each property has a type (string, number, boolean, etc.) - Properties can have validation rules - Properties can be required or optional
Validation: Built-in validation for data integrity - Type checking - Required field validation - Format validation (email, URL, etc.) - Custom validation rules
Versioning: Track changes to structure definitions over time
Property Types¶
Centrali supports the following property types:
String¶
Text data with optional validation.
Formats: - email: Email address validation - url: URL validation - uuid: UUID format - date: ISO 8601 date string - datetime: ISO 8601 datetime string
Number¶
Numeric data (integers or decimals).
Validation options: - minimum: Minimum value - maximum: Maximum value - integer: Force integer values only
Boolean¶
True/false values.
Datetime¶
Date and time values stored as ISO 8601 strings.
Array¶
List of values (can be any type).
Object¶
Nested object with its own properties.
{
"name": "address",
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zipCode": { "type": "string" }
}
}
Reference¶
Reference to another record in a different structure.
Creating a Structure¶
Via API¶
See the complete Structures API Guide for detailed examples.
curl -X POST "https://api.centrali.io/data/workspace/my-workspace/api/v1/structures" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Products",
"recordSlug": "products",
"properties": [
{
"name": "name",
"type": "string",
"required": true
},
{
"name": "price",
"type": "number",
"minimum": 0,
"required": true
},
{
"name": "inStock",
"type": "boolean",
"default": true
},
{
"name": "tags",
"type": "array",
"items": { "type": "string" }
}
]
}'
Via Console UI¶
- Navigate to your workspace
- Click "Structures" in the sidebar
- Click "Create Structure"
- Define properties:
- Add property name and type
- Configure validation rules
- Mark required fields
- Click "Save"
What are Records?¶
Records are instances of data that conform to a structure definition. If structures are the schema, records are the data.
Record Properties¶
Every record has: - id: Unique identifier (UUID) - createdAt: Creation timestamp - updatedAt: Last update timestamp - createdBy: User who created the record - version: Version number (for change tracking) - Custom fields: Your structure properties
Creating Records¶
Single Record¶
curl -X POST "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"name": "Laptop",
"price": 999.99,
"inStock": true,
"tags": ["electronics", "computers"]
}
}'
Response:
{
"id": "rec_abc123",
"data": {
"name": "Laptop",
"price": 999.99,
"inStock": true,
"tags": ["electronics", "computers"]
},
"createdAt": "2025-01-15T10:30:00Z",
"updatedAt": "2025-01-15T10:30:00Z",
"version": 1
}
Bulk Create¶
curl -X POST "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products/bulk" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"data": {
"name": "Laptop",
"price": 999.99,
"inStock": true
}
},
{
"data": {
"name": "Mouse",
"price": 29.99,
"inStock": true
}
}
]
}'
Upsert (Create or Update)¶
Use the upsert endpoint to atomically create a record if it doesn't exist, or update it if a matching record is found. This is useful for syncing external data, deduplicating records, or aggregating events.
curl -X POST "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products/upsert" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"match": { "sku": "LAPTOP-001" },
"data": {
"sku": "LAPTOP-001",
"name": "Laptop",
"price": 999.99,
"inStock": true
}
}'
- Returns 201 with
"operation": "created"if no matching record existed - Returns 200 with
"operation": "updated"if a matching record was found and updated - Uses advisory locking to prevent race conditions on concurrent calls
See the Records API Reference for full details.
Querying Records¶
Get All Records¶
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products" \
-H "Authorization: Bearer YOUR_TOKEN"
Response:
Filtering¶
Use bracket notation for filter operators. Custom fields require the data. prefix. See the Query Guide for full syntax.
# Records where price >= 500
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?data.price[gte]=500" \
-H "Authorization: Bearer YOUR_TOKEN"
# Records where inStock is true
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?data.inStock=true" \
-H "Authorization: Bearer YOUR_TOKEN"
# Records with specific tags (array contains any)
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?data.tags[hasAny]=electronics,computers" \
-H "Authorization: Bearer YOUR_TOKEN"
# Multiple conditions (AND)
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?data.price[gte]=50&data.price[lte]=500&data.inStock=true" \
-H "Authorization: Bearer YOUR_TOKEN"
# Status in list
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?data.category[in]=electronics,computers" \
-H "Authorization: Bearer YOUR_TOKEN"
Available Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, startswith, endswith, hasAny, hasAll
Pagination¶
Use page and pageSize for pagination. Default page size is 50, maximum is 500.
# First page, 25 records per page
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?page=1&pageSize=25" \
-H "Authorization: Bearer YOUR_TOKEN"
# Second page
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?page=2&pageSize=25" \
-H "Authorization: Bearer YOUR_TOKEN"
Sorting¶
Use the sort parameter with comma-separated field names. Prefix with - for descending order. JSONB fields require the data. prefix.
# Sort by price descending
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?sort=-data.price" \
-H "Authorization: Bearer YOUR_TOKEN"
# Sort by name ascending
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?sort=data.name" \
-H "Authorization: Bearer YOUR_TOKEN"
# Multi-sort: createdAt descending, then name ascending
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?sort=-createdAt,data.name" \
-H "Authorization: Bearer YOUR_TOKEN"
Search¶
Use search and searchFields to perform text search across records:
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?search=laptop&searchFields=data.name,data.description" \
-H "Authorization: Bearer YOUR_TOKEN"
Select Specific Fields¶
Reduce response size by requesting only the fields you need:
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products?fields=id,data.name,data.price" \
-H "Authorization: Bearer YOUR_TOKEN"
Updating Records¶
Update Single Record¶
curl -X PUT "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products/rec_abc123" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"price": 899.99,
"inStock": false
}
}'
Bulk Update¶
Update multiple records by providing an array of IDs and the data to apply:
curl -X PATCH "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products/bulk" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ids": ["rec_abc123", "rec_def456", "rec_ghi789"],
"data": {
"discount": 10
}
}'
Deleting Records¶
Delete Single Record¶
Soft delete (default) — the record is marked as deleted but can be restored:
curl -X DELETE "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products/rec_abc123" \
-H "Authorization: Bearer YOUR_TOKEN"
Bulk Delete¶
Delete multiple records by providing an array of IDs. Set hard: true for permanent deletion:
curl -X DELETE "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products/bulk" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ids": ["rec_abc123", "rec_def456", "rec_ghi789"],
"hard": false
}'
Restore a Deleted Record¶
curl -X POST "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products/rec_abc123/restore" \
-H "Authorization: Bearer YOUR_TOKEN"
Relationships¶
One-to-Many¶
Use reference properties to create relationships between structures:
Structure: authors
{
"name": "authors",
"properties": [
{ "name": "name", "type": "string", "required": true },
{ "name": "email", "type": "string", "required": true }
]
}
Structure: posts (with reference to authors)
{
"name": "posts",
"properties": [
{ "name": "title", "type": "string", "required": true },
{ "name": "content", "type": "string", "required": true },
{
"name": "author",
"type": "reference",
"target": "authors",
"displayField": "name",
"relationship": "many-to-one",
"onDelete": "restrict",
"required": true
}
]
}
Creating a post with author reference:
curl -X POST "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/posts" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"title": "My First Post",
"content": "Hello world!",
"author": "rec_author_123"
}
}'
Expanding References¶
Fetch records with referenced data expanded using the expand query parameter:
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/posts?expand=author" \
-H "Authorization: Bearer YOUR_TOKEN"
Response includes the full referenced object in _expanded:
{
"data": [
{
"id": "rec_post_123",
"data": {
"title": "My First Post",
"content": "Hello world!",
"author": "rec_author_123",
"_expanded": {
"author": {
"id": "rec_author_123",
"data": {
"name": "John Doe",
"email": "john@example.com"
}
}
}
}
}
]
}
Expanding Multiple References¶
Expand multiple reference fields by comma-separating them:
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/orders?expand=customer,product" \
-H "Authorization: Bearer YOUR_TOKEN"
Nested Expansion¶
Expand nested references using dot notation (up to 3 levels deep):
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/orders?expand=customer,customer.address" \
-H "Authorization: Bearer YOUR_TOKEN"
This expands the customer reference and also expands the address reference within the customer record.
Validation¶
Built-in Validation¶
Centrali automatically validates all record data against the structure schema. Validation is enforced on both create and update operations.
Type Validation¶
Every property value must match its declared type:
| Property Type | Validates |
|---|---|
| string | minLength, maxLength, pattern (regex), enum, not (disallowed values) |
| number | minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, enum |
| boolean | Must be true or false |
| datetime | Must be ISO 8601 format, earliestDate, latestDate constraints |
| array | minItems, maxItems, uniqueItems, item type validation, itemSchema for object arrays |
| object | Nested property validation, requiredProperties, isStrict (reject extra fields) |
| reference | Target record must exist, relationship type enforced, delete behavior enforced |
Required Fields¶
Properties marked required: true must be present when creating a record. On updates, only provided fields are validated (partial updates are supported).
Unique Fields¶
Properties marked isUnique: true enforce uniqueness across all records in the structure. Attempting to create or update a record with a duplicate value returns an error.
Immutable Fields¶
Properties marked immutable: true cannot be changed after the record is created.
Schema Discovery Modes¶
The schemaDiscoveryMode on a structure controls how unrecognized fields are handled:
strict(default) — Rejects records with fields not defined in the schemaauto-evolving— Accepts extra fields and suggests new schema properties. You can query and sort by any registered property with full type-aware behavior (numeric sorting, range comparisons, etc.)schemaless— No schema enforcement; any fields are accepted. You can query and sort by any field, but all fields are treated as strings for filtering and sorting purposes since there is no type information available. If you need type-aware queries (e.g., numeric range filters or proper numeric sorting), switch to auto-evolving mode so that field types are registered
Validation Errors¶
Invalid data returns a 400 error with details about each validation failure:
{
"error": "Validation failed",
"details": [
{
"property": "email",
"message": "Invalid email format"
},
{
"property": "price",
"message": "Must be greater than or equal to 0"
}
]
}
Versioning & History¶
Change Tracking¶
Every record update increments the version:
{
"id": "rec_abc123",
"name": "Updated Product",
"version": 5,
"updatedAt": "2025-01-15T14:30:00Z"
}
View Record History¶
curl -X GET "https://api.centrali.io/data/workspace/my-workspace/api/v1/records/slug/products/rec_abc123/history" \
-H "Authorization: Bearer YOUR_TOKEN"
Response shows all changes:
{
"history": [
{
"version": 1,
"changes": { "name": "Laptop", "price": 999.99 },
"changedBy": "user_123",
"changedAt": "2025-01-15T10:00:00Z"
},
{
"version": 2,
"changes": { "price": 899.99 },
"changedBy": "user_123",
"changedAt": "2025-01-15T12:00:00Z"
}
]
}
Best Practices¶
Structure Design¶
- Use clear, descriptive names:
users,products,orders - Mark fields required: Only when truly necessary
- Set defaults: For boolean and number fields
- Use references: For relationships between structures
- Keep it simple: Start with basic properties, add complexity as needed
Record Management¶
- Validate before creation: Check data on client side
- Use bulk operations: For multiple records
- Index frequently queried fields: Improves query performance
- Use pagination: For large result sets
- Implement soft deletes: Mark records inactive instead of deleting
Performance¶
- Use specific queries: Avoid fetching all records
- Limit expanded references: Only expand what you need
- Use appropriate page sizes: Balance between requests and data volume
- Cache frequently accessed data: On your application side
- Monitor usage: Track query performance and optimize
Common Use Cases¶
E-commerce¶
structures:
- products (name, price, description, images)
- categories (name, description)
- orders (customer, items, total, status)
- customers (name, email, address)
Content Management¶
structures:
- articles (title, content, author, published_date)
- authors (name, bio, avatar)
- categories (name, slug)
- comments (article, user, content)
Project Management¶
structures:
- projects (name, description, status)
- tasks (project, title, assignee, due_date)
- users (name, email, role)
- comments (task, user, content)
Related Documentation¶
- Structures API Guide - Complete API reference
- Query Guide - Query syntax and filtering
- Query Cheatsheet - Quick reference
- Compute Functions - Process and transform records
Next Steps¶
- Create your first structure
- Learn query syntax
- Set up compute functions to process records
- Configure webhooks for record events