CRUD pattern#
All resources share the same set of actions.
π΄ Parameters go in the URL#
The request body is not read at all for CRUD actions
Neither form-encoded nor JSON. Everything β including the data to be saved β
goes in the query string in PHP array notation:
data[title]=β¦&data[category][id]=3.
The only exception is the authentication endpoints, which conversely read a form body and do not read JSON.
The HTTP method does not matter#
GET /api/β¦/delete?id=5 really does delete
The action comes from the path, not the method. Do not put API addresses into prefetching or anywhere a crawler will follow them.
Actions#
| Action | Purpose |
|---|---|
get |
one record by id |
get-by |
one record by filter |
get-all |
a listing |
save |
insert and update alike |
update-columns |
changing selected fields |
delete |
deletion |
delete-all |
deletion by filter |
Read parameters#
| Parameter | Meaning |
|---|---|
id |
identifier |
filters |
filters, see below |
orderBys |
ordering |
offset, limit |
pagination |
columns |
narrowing the fields returned |
includes |
loading relations |
computeTotalCount |
1 computes the total |
Ordering#
The direction is a truthy value, not asc / desc
orderBys[name]=desc is a non-empty string, therefore true β it sorts
ascending. Use 1 and 0.
Relations#
includes=* pulls in every relation
An easy route to a huge response and a slow query. List what you need.
Filters#
A filter is a triple [type, field, value]:
?filters[0][0]=equal&filters[0][1]=activated&filters[0][2]=1
&filters[1][0]=like&filters[1][1]=title&filters[1][2]=notebook&filters[1][matchType]=both
&filters[2][0]=in&filters[2][1]=id&filters[2][2][]=1&filters[2][2][]=2
Filters combine with AND.
| Type | Extra keys |
|---|---|
equal, notEqual |
|
like, notLike |
matchType: both, start, end |
in, notIn |
|
isNull, isNotNull |
|
between |
from, to |
range |
inclusive |
greaterThan, greaterOrEqual |
short forms gt, gte |
lessThan, lessOrEqual |
short forms lt, lte |
comparison |
operator |
date, dateBetween, dateAfter, dateBefore |
operator, format |
group, or, and |
filters, logic |
fulltext |
mode, minScore |
An unknown filter type returns a 500
Not a validation message. A typo in a filter name brings the request down.
Saving#
One endpoint for insert and update. They are distinguished by the presence of
data[id].
Part of your data is silently discarded
The response reports success but the value is not saved. Discarded are:
metaβ entirely, for most resources,- fields marked read-only from outside (over forty across the system),
- fields reserved for administrators,
- nested collections the resource does not permit.
The only trace is in the server log. After writing, read the data back and check.
The server fills in the owner
On insert the owner comes from the sign-in. Any user identifier you send is ignored.
Empty data returns code 602.
Changing selected fields#
From outside this does not work on most resources
The list of permitted columns defaults to empty, so the call ends with code
607. It works only for columns with an explicit exception.
No partial writes
One non-permitted column brings the whole call down.
Deleting#
Bulk deletion never passes the ownership check
For the owner role it is always denied.
Tree resources#
Categories and menus additionally offer move-up, move-down, move-subtree,
move-subtree-to, insert-tree-node and delete-node.
delete-node deletes the whole subtree
Not just the node.
Events#
register-event increments a counter by one:
Permitted counter names are restricted by a list.
π΄ Many operations are unreachable from outside#
A resource may exist and still return 404
The system contains many operations used only internally by the website that have no HTTP wrapper. These include the cart, order checkout, "my" listings, availability and current prices.
You recognise it because the action returns 404 with HTML even though the resource and the permission both exist.