What this is about
A relation connects two models: an employee belongs to a company, a company has many employees. In CDMS, a relation is always a pair of fields: one field on each side, and they point at each other. In the hub, you create one side. The other side is created automatically.
On each side you define two things:
- the relation type: How many objects are on each side?
- the recursive flags: What may CDMS do with the connected objects across this relation?
The four relation types
flowchart LR
subgraph one ["1:1 ONETOONE"]
P[Person] --- T[Main phone]
end
subgraph many ["1:n ONETOMANY / n:1 MANYTOONE"]
C[Company] -->|employees| E1[Employee]
C --> E2[Employee]
E1 -.->|company| C
end
subgraph mm ["n:m MANYTOMANY"]
G1[Group A] --- R1[Role 1]
G1 --- R2[Role 2]
G2[Group B] --- R2
end
| Type | Hub | meaning | other side | in JSON |
|---|---|---|---|---|
ONETOONE | 1:1 | exactly one object on each side | ONETOONE | one object: "mainPhone": { "id": … } |
ONETOMANY | 1:n | this object has many | MANYTOONE | a list: "employees": [ … ] |
MANYTOONE | n:1 | many of these belong to one | ONETOMANY | one object: "company": { "id": … } |
MANYTOMANY | n:m | many on both sides | MANYTOMANY | a list: "roles": [ … ] |
In the database, for 1:n and n:1 there is a foreign key in the table of the n side. For 1:1 it is in one of the two tables. For n:m it is in a separate join table. The generator decides which table that is. For the client it does not matter: it writes and reads the relation the same way from both sides. See Both sides of a relation and Many-to-many through a join table.
What this looks like in the model
# Company – the "one" side
- name: "employees"
relationship: "ONETOMANY"
reference:
id: "m-employee" # target model
name: "company" # field on the other side
recursive:
create: true
update: true
delete: true
# Employee – the "many" side
- name: "company"
relationship: "MANYTOONE"
reference:
id: "m-company"
name: "employees"
The flags belong to one side. Here the company may create, update and delete its employees. The other way round, an employee may only link its company, because company has no flags. In the hub, you set the flags in the Recursion tab of the relation field. If you set nothing, all three are off.
The three flags
| CREATE | UPDATE | DELETE | |
|---|---|---|---|
| applies to | a child without id in the request | a child with id and other fields | a child that drops out of the relation, and all children when the parent object is deleted |
| without the flag | 400 recursive-create-not-allowed|<feld> | the child is only linked, its fields stay untouched | the child is only detached and keeps existing |
| role that is checked | create role of the child model | update role of the child model | delete role of the child model |
When: You create a company with a new employee.
-
1Client→CDMSsends
"employees": [{ "firstname": "Anna", "lastname": "Schmidt" }], so a child withoutid -
2CDMSFlag CREATE on
employees? -
3CDMS→Clientno → 400
recursive-create-not-allowed|employees -
4CDMS→Databaseyes → creates the employee and connects it to the company
Result: The employee gets its own id, its own default values and its own _createdOn.
When: You change the name of an existing employee through the company.
-
1Client→CDMSsends
"employees": [{ "id": "k1…", "firstname": "Anna", "lastname": "Meyer" }] -
2CDMSFlag UPDATE on
employees? -
3CDMSno → only links
k1…,lastnamestays as it was -
4CDMS→Databaseyes → updates the employee too, following the rules of the verb (PUT replaces, PATCH changes)
Result: See The four cases in nested writing.
When: An employee drops out of the list, or the company is deleted.
-
1Client→CDMSsends the list
employeeswithout Ben, orDELETE /company/delete/{id} -
2CDMSFlag DELETE on
employees? -
3CDMS→Databaseno → Ben keeps existing, his field
companybecomes empty -
4CDMS→Databaseyes → Ben is deleted, with his DELETE hooks and his own dependent children
Result: A child with the DELETE flag is called a dependent child: it only lives together with its parent object. See Lists as target state and Dependent objects (cascades).
And reading?
There is no flag for reading. Whether you may read a relation depends on the read role of the target model, or on a field role on the relation field. Which connected objects appear in the response is defined by your response. See Expanding references and lists.
Which roles apply to a child
Every child that CDMS creates, updates or deletes across a relation is checked with the roles of its own model. So if you create employees through the company, you also need the right to create employees.
Instead of the role of the child model, a field role on the relation field is also enough, for example company-employees-create. It allows the action only on this path, through the company. See Permissions on relations (field roles).
| Action on the child | Role of the child model | Field role on the relation field | Result |
|---|---|---|---|
| only link (POST, PUT) or detach | – | – | allowed, no role of the child needed |
| create, update, delete | yes | – | allowed |
| create, update, delete | no | yes | allowed, only on this path |
| create, update, delete | no | no | 403 missing-permission|<rolle>, the whole request fails |
What the flags change in the payload
The flags also decide which fields the client can send for a child at all. The generator builds the payload classes accordingly:
| Flag on the relation | Child in POST /create | Child in PUT /update |
|---|---|---|
| none | only id and @type | only id and @type |
| CREATE | all fields of the child | only id and @type |
| UPDATE | only id and @type | all fields of the child |
| CREATE and UPDATE | all fields | all fields |
If you send more fields on a relation without the matching flag, they are dropped when the request is read. PATCH reads data without a fixed class. There the rules from the four cases apply. So the OpenAPI description shows you exactly what is possible on which relation.
Pitfalls
What comes next
- What exactly happens with id and flag: The four cases in nested writing
- Lists in the payload: Lists as target state
- Creating relations in the hub: Modeling in the hub