Start here
Concepts
Aaly has no backend code layer — everything below is data, held as your project's definition, and Aaly serves the API from it directly.
Project
An independent backend: its own definition, data, and REST API at
https://api.aaly.io/<your-slug>/<project>. Every project is multi-tenant
capable from creation — see Multi-tenancy for what the
multiTenant and publicSignup flags actually control.
Entity
A collection of records — the closest analogy is a database table, but an
entity is defined, not migrated. Each entity has a key (a short prefix like
TSK) used to generate readable record IDs (TSK1, TSK2, ...), and a set
of fields.
Fields and types
| Type | Notes |
|---|---|
text | Supports pattern (regex), min/max length. |
number | min/max value. |
boolean | — |
date | YYYY-MM-DD. |
datetime | ISO 8601 with milliseconds. |
enum | Set values; validated on write. |
reference | Links to another entity — see below. |
list | An array of primitives; set valueType. Not for relationships. |
object | A nested, unvalidated JSON blob. |
Common to every field: required (enforced on create only), unique
(case-insensitive), and search (makes the field filterable — see
REST API).
References — model relationships with these, not text or list
A reference field links one entity to another:
{ "name": "assignee", "type": "reference", "targets": [{ "entity": "users", "projection": ["name", "email"] }] }
Write a value as {"id": "<recordId>"}; the projected target fields are
inlined automatically when you read the record back. Set multiple: true for
an array of links. References can nest up to three levels deep.
Validation
Enforced at write time, from the field definition: required (create only),
unique, min/max (length or value), enum membership, regex pattern
(text fields), and type checks. A violation returns a 400 with a
ValidationErrorResponse — see REST API for the shape.
The immutability rule — model relationships correctly the first time
Once a field is created, its type, unique, search, multiple,
targets, and valueType cannot change. Changing one of those after
records exist would leave old data inconsistent with the new shape, so
update_field rejects the whole call if it touches any of them — even mixed
with mutable attributes in the same call.
required, hidden, min, max, pattern, and values (enum options)
are mutable at any time — they only affect future validation, never
rewrite existing records.
If you need to change one of the immutable attributes: create a new field under a different name with the correct shape, migrate data into it through the REST API (once per tenant — data is partitioned per tenant), then remove the old field once every tenant is verified migrated. Don't try to reuse the old field's name — a field's old data isn't purged when you remove it, and a same-named field created later reads it back inconsistently.
What's next
REST API for endpoints, filtering, and errors · Auth for signup/signin and API keys · Multi-tenancy for the isolation model.