What this is about
You delete an object with a single request:
DELETE /api/rest/company/delete/c4…200, empty bodyThe request has no body, and neither does the response. There is no response, because after the delete there is nothing left to read.
CDMS deletes hard: the row disappears from the database. There is no “deleted” field and no recycle bin. What is still left afterwards is described in What remains after a delete.
The stations
-
CDMSVisibilityDoes the row exist, and may you see it? Same filters as for reading: tenant, own data, attribute filters.↳ no 404
not-found|<Dto>|<id> -
CDMSDelete roleDo you have the delete role of the model, here
company-delete?↳ no 403missing-permission|company-delete -
CDMSCascadeDelete dependent children along, unlink all other relations. Every deleted child needs the delete role of its own model.↳ no 403
missing-permission|<role of the child> -
HookHooksBefore hooks of all affected objects. A hook can prevent the delete by throwing an error.↳ no Error of the hook
-
DatabaseDatabaseCan all rows be removed and all unlinked relations be saved?↳ no 400
constraint-violation, e.g. an unlinked child must not exist without its parent - 200 without body, the transaction is committed
If a station fails, CDMS rolls back the whole transaction. The object, its children and all unlinked connections stay as they were.
The flow in detail
sequenceDiagram
participant C as Client
participant D as CDMS
participant H as Hook
participant DB as Database
C->>D: DELETE /company/delete/c4…
D->>DB: counts the row with the read filters
DB-->>D: 1 (otherwise 404)
D->>DB: loads the full object
D->>D: check the company's delete role
D->>D: cascade: check and mark children, unlink relations
D->>H: before hooks (company and every child)
D->>DB: remove
D->>H: after hooks (company and every child)
D->>DB: flush, then commit
D-->>C: 200, empty body
Three things stand out:
- Visibility comes before the role. If the row is invisible to you, you get 404, even if you lack the delete role. This way CDMS does not reveal whether a foreign
idexists. See Why invisible objects return 404. - You do not need a read role. The visibility check uses the read filters, but not the read role.
order-deletealone is enough to delete a visible order. - Nothing is read back. Unlike create, PUT and PATCH, there is no read-back after a delete and no READ hook.
Hooks during a delete
For every object that gets deleted, the hooks run with the method DELETE: for the addressed object and for every dependent child, each with the hooks of its own model.
-
1CDMSwalks through the object and its dependent children and registers the DELETE hooks for each
-
2Hookbefore hooks run, the object still has all its own fieldsHere you can check whether the delete is allowed, or clean up dependent data outside of CDMS. If the hook throws an error, the request fails and the database stays unchanged.
-
3CDMS→Databaseremoves the rows
-
4Hookafter hooks run
-
5CDMS→Databasewrites everything to the database and commits
An object that is only unlinked (relation without the DELETE flag) is not deleted. No DELETE hooks run for it.
Variants
When: DELETE {basis}/delete/{id}
-
1Client→CDMSsends
DELETE /company/delete/c4…, without body -
2CDMSchecks visibility, then the delete role
-
3CDMS→Databasedeletes the object and its dependent children, and unlinks all other relations
Result: 200 without body.
When: DELETE {basis}/delete, without id
-
1CDMS→Databasefinds the one object itself
-
2CDMSnone there → nothing to do, 200
-
3CDMSexists → checks the delete role and deletes it like a regular object, with cascade and hooks
Result: Afterwards a new create is possible again. See Singletons.
When: DELETE {basis}/delete/{id} on the hub API of an abstract model
-
1Client→CDMSsends only the
id, no@type -
2CDMS→Databaselooks up the stored type for the
id -
3CDMSthe
iddoes not exist → 404 -
4CDMSpasses the request on to the API of the subtype: visibility, delete role, cascade and hooks are those of the subtype
Result: To delete a private customer through /crm/kunde/delete/{id}, you need the delete role of the private customer. See Abstract models.
Decision table
| Row visible to you | Delete role of the model | Delete role of every dependent child | Result |
|---|---|---|---|
| no | – | – | 404 not-found|<Dto>|<id>, nothing deleted |
| yes | no | – | 403 missing-permission|<role>, nothing deleted |
| yes | yes | missing for one child | 403 with the role of the child, nothing deleted |
| yes | yes | yes | 200, object and dependent children deleted |
With strict mode switched off, the key for a missing role is missing-delete-role, and the status stays 403. So a delete is never silently skipped.
Deleting twice
A deleted row no longer exists. A second DELETE with the same id finds nothing and returns 404. For a client that repeats a request because the first response never arrived, 404 on the second attempt usually means: the object is already gone. See May the client retry?
Pitfalls
What comes next
- When children are deleted along and when they are only unlinked: Dependent objects (cascades)
- Deleting without DELETE, just through PUT or PATCH: Deleting by changing
- Files: Deleting file models