What this is about
Every time you read data, you tell CDMS in the response list which fields you want back. You can name every field one by one. For models with many fields this is tedious, so there are two shortcuts, the wildcards:
| Character | How to remember it | returns |
|---|---|---|
+ | “plus the simple fields” | all simple fields: text, number, date, yes/no |
* | “star is more” | all simple fields and all references (lists too), but references only with their id |
The example model
All pictures on this page use the same model employee:
| Field | Kind | Example value |
|---|---|---|
id | simple | "7f3…" |
firstname | simple | "Daniel" |
lastname | simple | "Mertins" |
company | reference to company | { "id": "a1…" } |
department | reference to department | { "id": "d4…" } |
The two basic forms
| Request | Result |
|---|---|
["+"] | idfirstnamelastnamecompanydepartment Only the simple fields. The references are missing. |
["*"] | idfirstnamelastnamecompany (id only)department (id only) Simple fields and both references, the references only with their id. |
* as request and responsePOST /api/rest/hr/employee/read/7f3…
{ "response": ["*"] }{
"data": {
"id": "7f3…",
"firstname": "Daniel",
"lastname": "Mertins",
"company": { "id": "a1…", "companyname": null },
"department": { "id": "d4…", "name": null }
},
"meta": { "error": false }
}If you need more than the id of a reference, you expand it explicitly. This is explained under Expand references.
The partial wildcards
You can combine both characters with the start or the end of a word. The character goes either at the very front or at the very end:
| Form | reads as | matches |
|---|---|---|
name+ | “simple fields that start with name” | prefix |
+name | “simple fields that end with name” | suffix |
name* | like name+, plus references that start with name | prefix |
*name | like +name, plus references that end with name | suffix |
| Request | Result |
|---|---|
["+name"] | idfirstnamelastnamecompanydepartment Both end with name. |
["first+"] | idfirstnamelastnamecompanydepartment Only firstname starts with first. |
["*ment"] | idfirstnamelastnamecompanydepartment (id only) department is a reference and ends with ment. With * it is included, but only with id. |
["depart*"] | idfirstnamelastnamecompanydepartment (id only) The same result using the start of the word. |
["+pany"] | idfirstnamelastnamecompanydepartment company does match pany, but it is a reference, and + never includes references. Result: no field, but still 200. |
["first+name"] | idfirstnamelastnamecompanydepartment The character is in the middle of the word. That is not a wildcard and matches nothing, not even firstname. No error, just empty. |
How CDMS resolves a wildcard
A component in the REST layer, the Expander, does the resolving. For each entry of the response list it works like this:
flowchart TB
E["Entry from response"] --> Q1{"Is it an object<br/>{ field, response }?"}
Q1 -->|yes| R["Expand the reference explicitly<br/>(separate page)"]
Q1 -->|no| Q2{"Does the text contain<br/>+ or * at the start<br/>or at the end?"}
Q2 -->|no| F["take exactly this field"]
Q2 -->|"yes, +"| P["all simple fields<br/>whose name matches"]
Q2 -->|"yes, *"| S["all simple fields whose name matches<br/>+ all references whose name matches<br/>(only with id)"]
P --> X["subtract exclude"]
S --> X
F --> Z["Result: list of fields<br/>→ exactly these columns are read from the DB"]
X --> Z
Four rules you should remember:
- Start or end of a word, nothing else. There are no regular expressions, no character in the middle of a word, and never two characters in one entry.
- Upper and lower case matter.
+Namedoes not matchfirstname. - Several entries are added together.
["+name", "*ment"]returnsfirstname,lastnameanddepartment. - No match is not an error. A wildcard that matches nothing simply returns no fields, and the response is still 200.
Removing fields again: exclude
With exclude you remove single fields from the result of a wildcard:
{ "response": ["*"], "exclude": ["department"] }{ "data": { "id": "7f3…", "firstname": "Daniel",
"lastname": "Mertins", "company": { "id": "a1…" },
"department": null } }- on fields that were added by a wildcard
["*"]+exclude: ["department"]→departmentis missing
- on fields you requested by name
["department", "+"]+exclude: ["department"]→departmentis still there
All variants at a glance
When: You need all simple fields and no references.
-
1Client→CDMSsends
{ "response": ["+"] } -
2CDMSincludes all simple fields of the model
-
3CDMS→Databasereads exactly these columns, without a join
Result: All simple fields. References appear as null in the response.
When: You want a quick overview of everything, including the references.
-
1Client→CDMSsends
{ "response": ["*"] } -
2CDMSincludes all simple fields and all references
-
3CDMSchecks the read role of the referenced model for every reference
-
4CDMS→Databasereads the columns and fetches every reference with a LEFT JOIN, but only its
id
Result: All simple fields, plus every reference as { "id": … }.
When: You want a group of similarly named fields, for example all …Date fields.
Like + or *, except that the fields are filtered by name first. The character goes at the front (suffix search) or at the end (prefix search).
Result: Only the fields whose name matches. + never with references, * with matching references.
When: You take a quick look at an object, for example in the browser or with curl.
GET has no body, so it has no response either. CDMS then always uses ["*"].
Result: Same as *. Good for trying things out, bad for production, because you need more data and more permissions than necessary.
When: The user may read employee, but not company.
-
1Client→CDMSsends
{ "response": ["*"] } -
2CDMS
*includescompany, so the read role ofcompanymust be present -
3CDMSThe role is missing. The whole request fails, the reference is not simply left out.
Result: 403 with missing-permission|company-read
The two traps
Trap 1: null does not mean “empty”
The response always contains the whole object. Fields you did not request appear as null, they are not missing. So with firstname: null you do not know whether the field is empty or whether you just did not request it.
Trap 2: * needs the permissions of all references
| response | Role employee-read | Role company-read | Role for department | Response |
|---|---|---|---|---|
| ["+"] | yes | – | – | 200 – only simple fields, no reference is read |
| ["*"] | yes | no | yes | 403 missing-permission|company-read – the whole request fails |
| ["*"] | yes | yes | yes | 200 – everything is there |
| ["id","firstname"] | yes | – | – | 200 – requested by name, no reference involved |
A ["*"] that works for you can fail for a colleague who has fewer roles.
Why all this? The effect on the database
The response is not a filter that removes fields from the response afterwards. It already decides what is read from the database: CDMS builds a query that reads exactly the requested columns and attaches references with a LEFT JOIN. So a + on a model with 40 fields reads 40 columns, ["id", "name"] only two.
flowchart LR
A["response: ['id','firstname','*ment']"] --> B["Expander<br/>→ id, firstname, department.id"]
B --> C["SQL: SELECT e.id, e.firstname, d.id<br/>FROM employee e<br/>LEFT JOIN department d …"]
C --> D["Response with exactly these values"]