Skip to main content
Adopt

IPA-104: Get

In REST APIs, it is customary to make a GET request to a resource's URI (for example, /projects/{projectId}/tasks/{taskId}) to retrieve that resource.

Guidance

  1. APIs must provide a Get method for resources.

    paths:
    /projects/{projectId}:
    get:
    operationId: getProject
    responses:
    "200":
    description: OK
    Why:

    The single-resource path exposes a GET, so the resource can be read back.

    paths:
    /projects/{projectId}:
    put:
    operationId: updateProject
    delete:
    operationId: deleteProject
    Why:

    The resource can be updated and deleted but never retrieved, so callers have no way to read its current state.

    1. Enumerate every entry under paths and group the paths by the resource they address.

    2. For each resource that has a single-resource path (a path ending in a resource identifier) or is a singleton, confirm that path defines a get operation.

    3. Report any resource whose single-resource or singleton path has no get operation.

  2. A Get method must return data from a single resource, not a collection or a paginated list.

    paths:
    /projects/{projectId}:
    get:
    operationId: getProject
    responses:
    "200":
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/ProjectResponse"
    Why:

    The response is a single object describing one project.

    paths:
    /projects/{projectId}:
    get:
    operationId: getProject
    responses:
    "200":
    content:
    application/json:
    schema:
    type: array
    items:
    $ref: "#/components/schemas/ProjectResponse"
    Why:

    An array response describes a collection, which belongs on the List method, not the Get method for one resource.

    1. Collect every get operation defined on a single-resource path or a singleton.

    2. For each 2xx response, follow the content schema (resolving any $ref) and inspect its type.

    3. Confirm the schema is a single object and not an array or a paginated envelope (a wrapper carrying results, totalCount, or similar list metadata).

    4. Report any Get response whose schema is an array or a paginated result.

  3. The HTTP verb must be GET.

    paths:
    /orders/{orderId}:
    get:
    operationId: getOrder
    Why:

    Retrieving a resource uses the GET verb, the safe, read-only HTTP method.

    paths:
    /orders/{orderId}:
    post:
    operationId: getOrder
    Why:

    POST is not safe and not idempotent, so it cannot stand in for a read of a single resource.

    1. For each operation whose intent is to retrieve a single resource, read the HTTP method it is defined under in the path item.

    2. Confirm the method is get and not post, put, patch, or delete.

    3. Report any single-resource read modeled under a verb other than get.

  4. The Get method must not cause side effects.

    paths:
    /orders/{orderId}:
    get:
    operationId: getOrder
    description: Returns the order. Does not modify any stored state.
    Why:

    A Get only reads, so repeated calls return the same data and leave server state unchanged.

    paths:
    /orders/{orderId}:
    get:
    operationId: getOrder
    description:
    Returns the order and marks it as viewed, decrementing its TTL.
    Why:

    Mutating state on read makes the call unsafe, so caching, retries, and prefetching can silently change data.

    1. Collect every get operation on a single resource or singleton.

    2. Read the operation description and, where available, the handler source it maps to, since whether a read mutates state cannot be told from the contract alone.

    3. Determine whether the operation writes to storage, enqueues work, sends notifications, or otherwise changes observable state.

    4. Report any Get whose implementation produces a side effect, per IPA-103.

  5. The request must not include a body.

    paths:
    /orders/{orderId}:
    get:
    operationId: getOrder
    parameters:
    - name: orderId
    in: path
    required: true
    schema:
    type: string
    Why:

    The resource is identified entirely by the path, so no request body is defined.

    paths:
    /orders/{orderId}:
    get:
    operationId: getOrder
    requestBody:
    content:
    application/json:
    schema:
    type: object
    Why:

    A GET body is ignored by many clients, proxies, and caches, so any input carried there cannot be relied on.

  6. A Get method should return a Response suffixed object.

    paths:
    /orders/{orderId}:
    get:
    operationId: getOrder
    responses:
    "200":
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/OrderResponse"
    Why:

    The response references a named, reusable OrderResponse schema, which keeps output models distinct from input models.

    paths:
    /orders/{orderId}:
    get:
    operationId: getOrder
    responses:
    "200":
    content:
    application/json:
    schema:
    type: object
    properties:
    id:
    type: string
    Why:

    An inline, unnamed schema cannot be referenced or named consistently, so the output model is not reusable across operations.

  7. A Response object must not include fields available only on creation or update — in OpenAPI, fields marked writeOnly: true.

    components:
    schemas:
    OrderResponse:
    type: object
    properties:
    id:
    type: string
    total:
    type: number
    Why:

    Every property is readable, so the response carries only fields a reader can see.

    components:
    schemas:
    OrderResponse:
    type: object
    properties:
    id:
    type: string
    cardNumber:
    type: string
    writeOnly: true
    Why:

    A writeOnly field is only meaningful on input, so its presence in a response schema advertises a value that is never returned.

  8. The response status code must be 200 OK.

    paths:
    /orders/{orderId}:
    get:
    operationId: getOrder
    responses:
    "200":
    description: OK
    Why:

    A successful read returns 200 OK, the standard status for a retrieved resource.

    paths:
    /orders/{orderId}:
    get:
    operationId: getOrder
    responses:
    "201":
    description: Created
    Why:

    201 Created signals that a resource was created, which never happens on a read.

Example

GET /projects/${projectId}/tasks/${taskId}

Naming

  1. Operation ID must be unique.

    paths:
    /projects/{projectId}:
    get:
    operationId: getProject
    /orders/{orderId}:
    get:
    operationId: getOrder
    Why:

    Each operation has a distinct operationId, so generated method names do not collide.

    paths:
    /projects/{projectId}:
    get:
    operationId: get
    /orders/{orderId}:
    get:
    operationId: get
    Why:

    Two operations share the operationId get, so one overwrites the other in generated clients.

    1. Collect the operationId of every operation across all paths and methods.

    2. Compare the values and find any operationId that appears more than once.

    3. Report each duplicated operationId, naming the operations that share it.

  2. Operation ID must be in camelCase.

    paths:
    /projects/{projectId}:
    get:
    operationId: getProject
    Why:

    getProject is camelCase, matching the casing every generated client expects.

    paths:
    /projects/{projectId}:
    get:
    operationId: get_project
    Why:

    get_project is snake_case, so generated method names are inconsistent with the rest of the API.

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

    paths:
    /projects/{projectId}:
    get:
    operationId: getProject
    Why:

    The operationId begins with get, matching the read semantics of the method.

    paths:
    /projects/{projectId}:
    get:
    operationId: fetchProject
    Why:

    fetchProject uses a different verb, so the name no longer signals a Get method consistently across the API.

  4. Operation ID should be followed by a noun or compound noun, and that noun should be the collection identifiers from the resource identifier in singular form.

    paths:
    /projects/{projectId}/tasks/{taskId}:
    get:
    operationId: getProjectTask
    Why:

    ProjectTask is the collection identifiers projects and tasks in singular form, so the name mirrors the resource path.

    paths:
    /projects/{projectId}/tasks/{taskId}:
    get:
    operationId: getProjectsTasks
    Why:

    The plural collection names do not match the singular-resource read, so the name describes a collection rather than the single task being retrieved.

  5. If the resource is a singleton, the last noun may be the plural form of the collection identifier.

Examples:

Resource IdentifierOperation ID
/projects/${projectId}/tasks/${taskId}getProjectTask
(Singleton) /projects/${projectId}/settingsgetProjectSettings

Error Handling

See IPA-114: Errors for guidance on error handling and documentation, in particular Authentication, Authorization and Not Found.