IPA-107: Update
In REST APIs, it is customary to make a PATCH or PUT request to a resource's
URI (for example, /groups/{groupId}/clusters/{clusterName}) to update that
resource.
Guidance
APIs should provide an update method for resources unless it is not valuable for users to do so
- Read-only resources must not have an Update method
- Read-only singleton resources must not have an Update method
- The purpose of the Update method is to make changes to the resources without causing side effects
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/ClusterUpdateRequest"
responses:
"200":
description: The updated cluster.
content:
application/json:
schema:
$ref: "#/components/schemas/Cluster"Why:A
clusteris a mutable resource, so consumers need a way to change it. An Update method on the resource URI lets them do that directly, without a custom verb or a side-effecting workaround.Identify each mutable resource — one whose state consumers are expected to change after creation. Read-only resources and read-only singleton resources are out of scope and must not have an Update method.
For each mutable resource, confirm there is an Update method (
PATCHorPUT) on the resource URI.Flag any mutable resource that has no Update method, unless changing it is genuinely not valuable to users.
The HTTP verb should be
PATCHand support partial resource update- The HTTP verb may be
PUTif the method will only ever support full resource replacement PUTis strongly discouraged because it becomes a backward-incompatible change to add fields to the resource
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/ClusterUpdateRequest"
responses:
"200":
description: The updated cluster.Why:PATCHallows the client to send only the fields it wants to change. Adding a new field to the resource later is backward-compatible, because existing clients simply omit it.PUTwould force a full replacement, so a newly added field would be unset by every existing client.For each Update operation, read the HTTP verb on the resource path.
Prefer
PATCHwith partial-update semantics.PUTis acceptable only when the method will only ever support full resource replacement.Flag
PUTused where partial update is expected, and note that adding fields to aPUTresource is a backward-incompatible change.
- The HTTP verb may be
The request body must contain the resource being updated, i.e. the resource or parts of the resource returned by the Get method
- API producers should implement as a
UpdateRequestsuffixed object- A
UpdateRequestobject must include only input fields- In OpenAPI, this means that the
UpdateRequestobject must not include fields withreadOnly: true
- In OpenAPI, this means that the
- A
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/ClusterUpdateRequest"
components:
schemas:
ClusterUpdateRequest:
type: object
properties:
instanceSize:
type: string
diskSizeGB:
type: integerWhy:The request body is the cluster resource, expressed as a
ClusterUpdateRequestobject that carries only writable fields. Server-owned fields likeidorcreatedAt(which would bereadOnly: trueon the resource) are absent, so clients can't try to set values the server controls.For each Update operation, read the request body schema.
Confirm the body is the resource (or parts of it) as returned by the Get method, ideally modeled as an
UpdateRequest-suffixed object.Walk the request schema's properties and flag any field that is
readOnly: trueon the resource — anUpdateRequestobject must include only input fields.
- API producers should implement as a
The response body must be the same resource returned by the Get method
- The Update method must return the complete resource to avoid complexities for clients
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
responses:
"200":
description: The updated cluster.
content:
application/json:
schema:
$ref: "#/components/schemas/Cluster"Why:The Update response returns the full
Cluster, the same schema the Get method returns. The client sees the resource's complete current state in one round trip and does not have to issue a follow-up Get to learn the result of its change.paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
responses:
"200":
description: The updated cluster.
content:
application/json:
schema:
$ref: "#/components/schemas/ClusterUpdateResult"Why:The Update response returns
ClusterUpdateResult, a partial projection rather than the fullClusterreturned by Get. The client cannot observe the resource's complete updated state and must issue a follow-up Get to reconcile.For each Update operation, read the success response schema.
Confirm it is the same resource schema returned by the Get method, and that it is the complete resource rather than a partial projection.
Flag Update operations that return no body, a partial body, or a schema that differs from the Get resource.
The Update method must respect the client provided values, or lack thereof, for all fields in the request (see Client-owned fields)
- For partial resource updates, the Update method must only update fields included in the request body and leave all other fields unchanged
- For full resource replacements, the Update method must update the full
resource to match the request
- Any fields not included in the request body must be unset or set to their default value(s)
- If the client explicitly provides
nullfor an optional field in the request, the server should unset the field or reset it to its default value- The server should return a
validation error if the client provides
nullfor a field that is not nullable and cannot be unset
- The server should return a
validation error if the client provides
# PATCH /groups/{groupId}/clusters/{clusterName}
# Request body:
{ "diskSizeGB": 100 }
# Only diskSizeGB changes; instanceSize and every other field
# keep their existing values.Why:This is a partial (
PATCH) update. OnlydiskSizeGBappears in the body, so onlydiskSizeGBchanges. Fields the client did not send are left untouched, which is exactly what a partial update promises.Determine whether the operation is a partial update (
PATCH) or a full replacement (PUT).For partial updates, confirm fields absent from the request body are left unchanged. For full replacements, confirm absent fields are unset or reset to their defaults.
Check
nullhandling: an explicitnullon an optional field should unset it or reset it to its default, and anullon a non-nullable field that cannot be unset should yield a validation error.Flag any field whose ownership is not respected, cross-checking client-owned fields against IPA-111.
Update operations must not accept query parameters
- Query parameters are usually a sign of a side effect that standard methods must not cause
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
parameters:
- name: groupId
in: path
required: true
schema:
type: string
- name: clusterName
in: path
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/ClusterUpdateRequest"
responses:
"200":
description: The updated cluster.Why:Every parameter is a path parameter that identifies the resource. There are no query parameters, so the operation can't smuggle in flags that trigger side effects — it does one thing, update the cluster.
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
parameters:
- name: groupId
in: path
required: true
schema:
type: string
- name: clusterName
in: path
required: true
schema:
type: string
- name: restart
in: query
schema:
type: boolean
responses:
"200":
description: The updated cluster.Why:The
restartquery parameter turns a plain Update into something that also reboots the cluster — a side effect a standard method must not cause. Query parameters on an Update are the usual signature of exactly this kind of hidden behavior.The response status code should be
200 OK- If the request for a partial update is empty, the Update method should return a 200 response with no change to the resource
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
responses:
"200":
description: The updated cluster.
content:
application/json:
schema:
$ref: "#/components/schemas/Cluster"Why:The Update returns
200 OKwith the updated resource in the body. An empty partial-update request is still answered with200and the resource left unchanged, so clients get a consistent success code whether or not anything actually changed.Resources should provide a single canonical update operation
- If a resource has multiple Update methods, it's possible one, or all of them, may be custom methods
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
responses:
"200":
description: The updated cluster.Why:The cluster has exactly one canonical Update —
PATCH /groups/{groupId} /clusters/{clusterName}. Clients have a single, predictable way to change the resource, and there is no ambiguity about which operation performs an ordinary update.For each resource, collect all operations that modify it via
PATCHorPUTon the resource URI.Confirm there is a single canonical Update method for the resource.
If multiple Update-like methods exist, check whether the non-canonical ones are properly modeled as custom methods rather than competing standard Updates, and flag any ambiguity.
Example
PATCH /groups/${groupId}/clusters/{clusterName}
Error Handling
See IPA-114: Errors for guidance on error handling and documentation.
Naming
Operation ID must be unique
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
/groups/{groupId}/settings:
patch:
operationId: updateGroupSettingsWhy:Each operation has a distinct
operationId. Generated SDKs produce one method per operation, so unique IDs are required for the generated method names not to collide.Operation ID must be in
camelCasepaths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupClusterWhy:updateGroupClusteriscamelCase: a lowercase first letter and each subsequent word capitalized. This is the form code generators expect when turning operation IDs into method names.Operation ID must start with the verb "update"
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupClusterWhy:The ID begins with
update, which marks the operation as the resource's standard Update method. The verb prefix makes the operation's role obvious in generated clients and documentation.Operation ID should be followed by a noun or compound noun
- The noun(s) in the Operation ID should be the collection identifiers from
the resource identifier in singular form
- If the resource is a singleton resource, the last noun may be the plural form of the collection identifier
paths:
/groups/{groupId}/clusters/{clusterName}:
patch:
operationId: updateGroupCluster
/groups/{groupId}/settings:
patch:
operationId: updateGroupSettingsWhy:updateGroupClusterfollows the verb with the collection identifiers (groups,clusters) in singular form. For the singleton/groups/{groupId} /settings, the last noun stays plural (Settings), which the singleton exception allows.For each Update operation, take the operation ID and strip the leading
updateverb to isolate the noun(s).Derive the expected noun(s) from the resource identifier's collection segments in singular form (e.g.
groups/clusters→GroupCluster).Compare. For singleton resources, allow the last noun to be the plural form of the collection identifier.
Flag operation IDs whose nouns don't match the resource's collection identifiers.
- The noun(s) in the Operation ID should be the collection identifiers from
the resource identifier in singular form
Examples:
| Resource Identifier | Operation ID |
|---|---|
/groups/${groupId}/clusters/${clusterName} | updateGroupCluster |
(Singleton) /groups/${groupId}/settings | updateGroupSettings |