Absent collections default to empty, and an update touches only what it sends
An update is partial. What a caller does not send, it does not change, which is what lets an editor save one part of a workflow without holding the whole of it.
The rule
- On create,
nodes,edgesandmetadatadefault to the empty list when absent, and a workflow that declares nointerfacedeclares no ports. - On update, a key the request omits is left as stored;
nameis the exception, required on update as on create. - An
interfacepresent on update rewrites both port lists from it even when only one side is supplied, so a missing or emptyinputsoroutputsclears that side. - The
metadatapublished on read need not be identical to themetadataas stored: an implementation may fold envelope fields such asformatandschemaVersionback into it on read.
What it means
An update is partial by default: a key the request does not send is left as
stored, which is what lets a caller save one part of a workflow without
holding the whole of it in hand. interface is the one key where "sent"
does not mean "sent in full" — sending it at all rewrites both the inputs
and the outputs it describes, even if only one of them is included. A side
left out of a present interface, or sent as an empty list, is cleared, not
left alone. Absent and empty are the same signal there that they are not
everywhere else in this rule: absent preserves, present (even partially or
emptily) replaces.
The metadata a caller reads back is not necessarily the metadata it
would get by reading the stored document directly: an implementation may
fold housekeeping fields such as a format marker or a schema version into
what it publishes on read, without those fields having been part of what was
stored.
Example
Three requests to the same door, none of them touching interface the same
way.
POST /api/flowdrop/workflows
{"id": "bare_wf", "name": "Bare"}PUT /api/flowdrop/workflows/{workflow}
{"name": "Partial renamed", "nodes": [ … ]}PUT /api/flowdrop/workflows/{workflow}
{"name": "Clear", "interface": []}The first leaves nodes, edges and metadata all as empty lists — nothing
was sent for them, so there is nothing to default from but empty. The second
leaves the edges and metadata a prior request had set untouched, because
this request never mentions them. The third is the one that reads like the
second but is not: interface was sent, so both its sides are rewritten, and
an empty list on either side clears it rather than leaving it as it was.