Every table in the Data Model generates a REST query surface. This page covers the read patterns you’ll use most. The examples assume aDocumentation Index
Fetch the complete documentation index at: https://archie.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
students table with fields like firstName, email, age, isActive, createdAt, and a city relationship.
Single record
GET /api/rest/<table>/<id> returns one record by primary key.
404 with a Problem Details body — see Error handling.
Tables with composite primary keys don’t expose /<id> routes. Use the filtered list or the advanced query endpoint instead.
Record list
GET /api/rest/<table> returns up to 100 records by default, wrapped in a data array with pagination metadata.
Prefer: count=exact (slower) or count=estimated (fast, approximate).
Filtered list
Filters are query parameters in the form?fieldName=operator.value.
Comparison operators
| Operator | Example |
|---|---|
equals | ?status=equals.active |
not_equals | ?status=not_equals.archived |
gt / gte | ?age=gte.18 |
lt / lte | ?price=lt.100 |
contains | ?name=contains.john |
starts_with / ends_with | ?email=starts_with.admin |
in | ?status=in.(active,pending) |
not_in | ?status=not_in.(archived,deleted) |
between | ?age=between.18,30 |
is_empty / is_not_empty | ?bio=is_empty.true |
Date helpers
| Operator | Example |
|---|---|
today | ?createdAt=today.true |
yesterday | ?createdAt=yesterday.true |
this_week | ?createdAt=this_week.true |
this_month | ?createdAt=this_month.true |
last_days | ?createdAt=last_days.7 |
last_weeks | ?createdAt=last_weeks.2 |
last_months | ?createdAt=last_months.3 |
Logical groups
or=(...), and=(...), and not=(...) compose conditions. They can nest.
Filtering across relations
Use dot notation to filter a parent by a related table’s field.Field name conflicts
If a field name collides with a reserved query parameter (select, sort, limit), prefix with col.:
Sorted list
?sort=field sorts ascending. Prefix with - for descending. Comma-separate for multi-column sort.
sort=createdAt.desc,firstName.asc) is also supported.
Paginated list
Two pagination styles, both work on the same endpoint.Offset-based
| Parameter | Alias | Default | Max |
|---|---|---|---|
limit | first | 100 | 1000 |
offset | skip | 0 | — |
hasNextPage and hasPreviousPage in the response tell you when to stop.
Cursor-based
For stable pagination on frequently-changing tables, use cursors. Cursor values are opaque, base64-encoded strings — read them from the response, don’t construct them yourself.Field selection
?select=field1,field2 returns only the listed fields.
Embedding related records
Parentheses embed fields from related tables — equivalent to a SQL JOIN or a nested GraphQL query.400 Bad Request.
Field aliases
UseoriginalName:alias to rename fields in the response.
Aggregation
GET /api/rest/<table>/_aggregate computes counts, sums, averages, and other aggregates.
| Function | Example |
|---|---|
count | fn=count |
sum | fn=sum:total |
avg | fn=avg:total |
min | fn=min:total |
max | fn=max:total |
count_distinct | fn=count_distinct:status |
Group by
Filter groups with having
Sort and filter aggregates
sort=-count sorts groups by an aggregate alias. Filters from a regular list query (createdAt=this_month.true) apply before aggregation.
Advanced query endpoint
For deeply nested filter logic that’s awkward to express as URL params,POST /api/rest/<table>/_query accepts a structured JSON body. It’s a POST for ergonomics — but the operation is read-only, no data is modified.
| Use case | Why |
|---|---|
| Deeply nested AND/OR/NOT logic | Easier to express in JSON than URL params. |
| Dynamic queries built in code | Constructing JSON is cleaner than URL string concatenation. |
| Filters exceed URL length limits | Long URL filters can hit gateway/proxy limits; bodies don’t. |
GET form is still better — it’s cacheable and bookmarkable.
Permissions
All query endpoints enforce the per-role permissions in Role-Based Access. Records and fields a role can’t read are silently filtered out of responses.FAQ
What's the maximum number of records per request?
What's the maximum number of records per request?
1000 with
limit=1000. The default is 100. For larger result sets, paginate.Why doesn't `totalCount` appear in my response?
Why doesn't `totalCount` appear in my response?
By default it’s omitted for performance. Send
Prefer: count=exact for the precise total or count=estimated for a fast approximation.Should I use offset or cursor pagination?
Should I use offset or cursor pagination?
Offset is simpler — works for jump-to-page UIs. Cursor is more stable when records are being inserted or deleted between page fetches. Use cursor for infinite-scroll feeds; use offset for paginated tables.
When should I use `_query` vs URL params?
When should I use `_query` vs URL params?
URL params for simple, browser-friendly, cacheable queries.
_query for complex nested logic, dynamic query construction, or filters that exceed URL length limits.How do I select fields from a relation that's many-to-many?
How do I select fields from a relation that's many-to-many?
Embed the join table — for example,
?select=id,name,enrollments(course(name)) — and the parentheses traverse the relationship. Up to 3 levels deep.