Skip to main content
Adopt

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

  1. APIs should provide an update method for resources unless it is not valuable for users to do so

    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 cluster is 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.

    1. 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.

    2. For each mutable resource, confirm there is an Update method (PATCH or PUT) on the resource URI.

    3. Flag any mutable resource that has no Update method, unless changing it is genuinely not valuable to users.

  2. The HTTP verb should be PATCH and support partial resource update

    • The HTTP verb may be PUT if the method will only ever support full resource replacement
    • PUT is 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:

    PATCH allows 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. PUT would force a full replacement, so a newly added field would be unset by every existing client.

    1. For each Update operation, read the HTTP verb on the resource path.

    2. Prefer PATCH with partial-update semantics. PUT is acceptable only when the method will only ever support full resource replacement.

    3. Flag PUT used where partial update is expected, and note that adding fields to a PUT resource is a backward-incompatible change.

  3. 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 UpdateRequest suffixed object
      • A UpdateRequest object must include only input fields
        • In OpenAPI, this means that the UpdateRequest object must not include fields with readOnly: true
    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: integer
    Why:

    The request body is the cluster resource, expressed as a ClusterUpdateRequest object that carries only writable fields. Server-owned fields like id or createdAt (which would be readOnly: true on the resource) are absent, so clients can't try to set values the server controls.

    1. For each Update operation, read the request body schema.

    2. Confirm the body is the resource (or parts of it) as returned by the Get method, ideally modeled as an UpdateRequest-suffixed object.

    3. Walk the request schema's properties and flag any field that is readOnly: true on the resource — an UpdateRequest object must include only input fields.

  4. 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 full Cluster returned by Get. The client cannot observe the resource's complete updated state and must issue a follow-up Get to reconcile.

    1. For each Update operation, read the success response schema.

    2. Confirm it is the same resource schema returned by the Get method, and that it is the complete resource rather than a partial projection.

    3. Flag Update operations that return no body, a partial body, or a schema that differs from the Get resource.

  5. 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 null for 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 null for a field that is not nullable and cannot be unset
    # 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. Only diskSizeGB appears in the body, so only diskSizeGB changes. Fields the client did not send are left untouched, which is exactly what a partial update promises.

    1. Determine whether the operation is a partial update (PATCH) or a full replacement (PUT).

    2. 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.

    3. Check null handling: an explicit null on an optional field should unset it or reset it to its default, and a null on a non-nullable field that cannot be unset should yield a validation error.

    4. Flag any field whose ownership is not respected, cross-checking client-owned fields against IPA-111.

  6. 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 restart query 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.

  7. 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 OK with the updated resource in the body. An empty partial-update request is still answered with 200 and the resource left unchanged, so clients get a consistent success code whether or not anything actually changed.

  8. 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.

    1. For each resource, collect all operations that modify it via PATCH or PUT on the resource URI.

    2. Confirm there is a single canonical Update method for the resource.

    3. 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

  1. Operation ID must be unique

    paths:
    /groups/{groupId}/clusters/{clusterName}:
    patch:
    operationId: updateGroupCluster
    /groups/{groupId}/settings:
    patch:
    operationId: updateGroupSettings
    Why:

    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.

  2. Operation ID must be in camelCase

    paths:
    /groups/{groupId}/clusters/{clusterName}:
    patch:
    operationId: updateGroupCluster
    Why:

    updateGroupCluster is camelCase: a lowercase first letter and each subsequent word capitalized. This is the form code generators expect when turning operation IDs into method names.

  3. Operation ID must start with the verb "update"

    paths:
    /groups/{groupId}/clusters/{clusterName}:
    patch:
    operationId: updateGroupCluster
    Why:

    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.

  4. 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: updateGroupSettings
    Why:

    updateGroupCluster follows 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.

    1. For each Update operation, take the operation ID and strip the leading update verb to isolate the noun(s).

    2. Derive the expected noun(s) from the resource identifier's collection segments in singular form (e.g. groups/clustersGroupCluster).

    3. Compare. For singleton resources, allow the last noun to be the plural form of the collection identifier.

    4. Flag operation IDs whose nouns don't match the resource's collection identifiers.

Examples:

Resource IdentifierOperation ID
/groups/${groupId}/clusters/${clusterName}updateGroupCluster
(Singleton) /groups/${groupId}/settingsupdateGroupSettings