diff --git a/aep/general/0062/aep.md b/aep/general/0062/aep.md index 14aea2e6..6338e2ed 100644 --- a/aep/general/0062/aep.md +++ b/aep/general/0062/aep.md @@ -133,7 +133,7 @@ Common uses for query parameters in APIs include: * Filtering: `?status=active&type=premium` * Pagination: `?page=2&limit=50` -* Sorting: `?sort=created_time&order=desc` +* Sorting: `?orderBy=createdTime` Query parameter keys are typically **case-sensitive**, though this behavior can vary by server implementation. To avoid ambiguity, assume parameter keys are case-sensitive unless explicitly documented otherwise. Also, maintain consistent diff --git a/aep/general/0065/aep.md b/aep/general/0065/aep.md index f5bf5f1a..9c2d6fac 100644 --- a/aep/general/0065/aep.md +++ b/aep/general/0065/aep.md @@ -1,7 +1,6 @@ # GET -In REST APIs, it is customary to make a `GET` request to a resource's URI (for example, -`/publishers/{publisher_id}/books/{book_id}`) in order to retrieve that resource. `GET` requests are used to read either +In REST APIs, it is customary to make a `GET` request to fetch data. `GET` requests are used to read either a single resource or a collection of resources. As defined in [RFC 9110 Section 9.3.1], `GET` is the primary mechanism for information retrieval in HTTP and should be [safe] and [idempotent]. @@ -14,7 +13,7 @@ collections. **Use `GET` when:** -* Retrieving a specific resource by its ID (e.g., "Get user 123"). +* Retrieving a specific resource by its ID (e.g., "Fetch user 123"). * Listing resources in a collection (e.g., "List all books"). * Searching or filtering data where the query parameters fit within the URL length limit. * The operation is read-only and safe to cache. @@ -28,9 +27,7 @@ collections. ### General requirements -* `GET` **must** be used to retrieve a representation of a resource. -* APIs **must** provide a `GET` method for resources unless there is a compelling reason not to do so. The purpose of - the `GET` method is to retrieve and return data about the resource. +* `GET` **must** be used to retrieve a representation of a resource or collection. * `GET` requests **must not** have a request body payload. * If a `GET` request contains a body, the body **must** be ignored, and **must not** cause an error. * Be aware that some HTTP clients, proxies, and intermediaries may drop the request body or reject the request @@ -39,36 +36,6 @@ collections. see [GET with body](#get-with-body). * `GET` requests **must** be [safe], meaning they **must not** modify server state or have side effects. * `GET` requests **must** be [idempotent], meaning multiple identical requests **must** produce the same result. -* Some resources take longer to be retrieved than is reasonable for a regular API request. In this situation, the - API **should** use a [long-running operation]. - -### Individual Resources - -`GET` requests for individual resources: - -* **must** use the resource's canonical [URI path] (e.g., `/publishers/{publisher_id}/books/{book_id}`). -* **must** return a [200 OK] with the resource representation in the response body when the resource exists. -* **must** return a [404 Not Found] if the resource does not exist. - * If the resource previously existed and has since been deleted (e.g., soft-deleted), the server **may** instead - respond with [410 Gone]. -* **may** support field masks or sparse fieldsets to allow clients to specify which fields they want returned, reducing - payload size and improving performance. See AEP-157 on partial responses for more details. - -### Collection Resources - -`GET` requests for collection resources: - -* **must** use the collection's [URI path] (e.g., `/publishers/{publisher_id}/books`). -* **must** return a `[200 OK] when resources are successfully retrieved. - * The response body **must** be a wrapper object containing the list of resources, not a raw JSON array. - * These results **must** be [paginated]. -* **must** return a [200 OK] with an empty array (inside the wrapper object) if the collection exists but contains no - resources. -* **should** return a [404 Not Found] if the parent resource does not exist (e.g., requesting - `/publishers/{invalid_id}/books` when the publisher doesn't exist). -* **should** implement sorting and [filtering] mechanisms to allow clients to sort and narrow results. - * The filters **must** follow the guidelines on [query parameters]. -* **must** ensure a deterministic default sort order to guarantee stable [pagination]. ### Caching @@ -94,21 +61,16 @@ in the query string rather than the request body. This allows pagination links t encodes page position and direction. The `cursor` may optionally include a hash of the applied filters to validate consistency across paginated requests. -[RFC 9110 Section 9.3.1]: https://datatracker.ietf.org/doc/html/rfc9110#section-9.3.1 - -[safe]: /130#common-method-properties +## Further Reading -[idempotent]: /130#common-method-properties +- [Standard Action: Fetch](/fetch) +- [Standard Action: List](/list) -[long-running operation]: /long-running-operations - -[URI path]: /paths - -[paginated]: /pagination +[RFC 9110 Section 9.3.1]: https://datatracker.ietf.org/doc/html/rfc9110#section-9.3.1 -[pagination]: /pagination +[safe]: /64#common-method-properties -[query parameters]: /query-parameters +[idempotent]: /64#common-method-properties [Cache-Control]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control @@ -122,16 +84,10 @@ consistency across paginated requests. [304 Not Modified]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/304 -[filtering]: /filtering - -[200 OK]: /63#200-ok - -[404 Not Found]: /63#404-not-found - -[410 Gone]: /63#410-gone - ## Changelog +* **2026-02-09**: Move this from AEP-131 to AEP-65. Extract `Individual Resources` section to `Fetch` action (new + AEP-131).Extract `Collection Resources` section to `List` action (new AEP-132). * **2026-01-21**: Standardize HTTP status code references. * **2025-11-12**: Initial AEP-131 for Thryv, adapted from [Google AIP-131][] and aep.dev [AEP-131][]. diff --git a/aep/general/0065/aep.yaml b/aep/general/0065/aep.yaml index 117de821..f2c6ac27 100644 --- a/aep/general/0065/aep.yaml +++ b/aep/general/0065/aep.yaml @@ -3,6 +3,6 @@ id: 65 state: approved slug: http-get created: 2025-11-12 -updated: 2026-01-21 +updated: 2026-02-10 placement: category: http diff --git a/aep/general/0131/aep.md b/aep/general/0131/aep.md index 28caf693..71bd9938 100644 --- a/aep/general/0131/aep.md +++ b/aep/general/0131/aep.md @@ -1,141 +1,107 @@ -# Get +# Fetch -In REST APIs, it is customary to make a `GET` request to a resource's URI (for +In REST APIs, it is customary to make a [GET] request to a resource's URI (for example, `/v1/publishers/{publisher_id}/books/{book_id}`) in order to retrieve -that resource. - -Resource-oriented design AEP-121 honors this pattern through the `Get` method. -These RPCs accept the URI representing that resource and return the resource. +that resource. Resource-oriented design AEP-121 honors this pattern through the `Fetch` action. ## Guidance -- APIs **must** provide a get method for resources. The purpose of the get - method is to return data from a single resource. +- APIs **must** provide a `Fetch` action for resources. The purpose of the `Fetch` + action is to return data from a single resource. - Some resources take longer to be retrieved than is reasonable for a regular API request. In this situation, the API should use a [long-running operation](/long-running-operations). ### Operation -{% tab proto %} - -{% sample '../example.proto', 'rpc GetBook' %} - -- The RPC's name **must** begin with the word `Get`. The remainder of the RPC - name **should** be the singular form of the resource's message name. -- The request message **must** match the RPC name, with a `Request` suffix. -- The response message **must** be the resource itself. (There is no - `GetBookResponse`.) -- The HTTP verb **must** be `GET`. -- The URI **should** contain a single variable field corresponding to the - resource path. - - This field **should** be called `path`. - - The URI **should** have a variable corresponding to this field. - - The `path` field **should** be the only variable in the URI path. All - remaining parameters **should** map to URI query parameters. -- There **must not** be a `body` key in the `google.api.http` annotation. -- There **should** be exactly one `google.api.method_signature` annotation, - with a value of `"path"`. - -{% tab oas %} - -`Get` operations **must** be made by sending a `GET` request to the resource's -URI: +`Fetch` operations **must** be made by sending a [GET] request to the resource's resource's canonical [URI path]: ```http -GET /v1/publishers/{publisher_id}/books/{book_id} HTTP/2 -Host: bookstore.example.com -Accept: application/json +GET /v1/publishers/{publisher_id}/books/{book_id} ``` - The URI **should** contain a variable for each resource in the resource hierarchy. - The path parameter for all resource IDs **must** be in the form - `{resource-singular}` (such as `book`). - -{% endtabs %} + `{resourceName}_id` (such as `book_id`), and path parameters representing + the ID of parent resources **must** end with `id`. ### Requests -{% tab proto %} - -{% sample '../example.proto', 'message GetBookRequest' %} - -- A resource path field **must** be included. It **should** be called `path`. - - The field **should** be annotated as `REQUIRED`. - - The field **should** identify the [resource type][aep-4] that it - references. -- The comment for the `path` field **should** document the resource pattern. -- The request message **must not** contain any other required fields, and - **should not** contain other optional fields except those described in - another AEP. +- The HTTP method **must** be [GET], and **must** follow the `GET` method guidelines in AEP-65. + - The request **must** be [safe] and **must not** have side effects. +- There **must not** be a request body. + - If a `GET` request contains a body, the body **must** be ignored, and + **must not** cause an error. +- The request **must not** _require_ any query parameters. + - Optional query parameters **may** be included (e.g., for [partial responses](/partial-responses)) -**Note:** The `path` field in the request object corresponds to the `path` -variable in the `google.api.http` annotation on the RPC. This causes the `path` -field in the request to be populated based on the value in the URL when the -REST/JSON interface is used. +{% tab proto %} {% tab oas %} {% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books/{book_id}.get.parameters' %} -- The HTTP method **must** be `GET`. - - The request **must** be safe and **must not** have side effects. -- There **must not** be a request body. - - If a `GET` request contains a body, the body **must** be ignored, and - **must not** cause an error. -- The request **must not** require any fields in the query string. The request - **should not** include optional fields in the query string unless described - in another AEP. - {% endtabs %} ### Responses -The response **should** usually include the fully-populated resource unless -there is a reason to return a partial response (see AEP-157). +#### Successful Response -{% tab proto %} +A Fetch action **must** return a [200 OK] status code with the resource representation in the response body when the +resource exists. + +The response content **must** be the resource itself (there is no `GetBookResponse`). The response **should** include +the fully populated resource unless there is a reason to return a partial response (see AEP-157). -{% sample '../example.proto', 'message Book' %} +{% tab proto %} {% tab oas %} {% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books/{book_id}.get.responses.200.content' %} -- The response content **must** be the resource itself. For example: - `#/components/schemas/Book`. The response **must** reference a schema with a - [`x-aep-resource` extension]. - -[`x-aep-resource` extension]: ./0004.md#annotating-resource-types - {% endtabs %} -### Errors +#### Error Responses -If the user does not have sufficient permission to know that the resource -exists, the service **should** reply with an HTTP 404 error, regardless of -whether or not the resource exists. Permission **must** be checked prior to -checking if the resource exists. +A Fetch action **must** return appropriate error responses. See [HTTP status codes] for a complete guide to error +handling. -If the user has sufficient permission to know that the resource exists, but is -unable to access it, the service **should** reply with an HTTP 403 error. +Most common error scenarios: -If the user does have proper permission, but the requested resource does not -exist, the service **must** reply with an HTTP 404 error. +- Return [404 Not Found] if the resource does not exist. + - If the resource previously existed and has since been deleted (e.g., soft-deleted), the server **may** instead + respond with [410 Gone], as described in AEP-164. +- See [authorization checks](/authorization) for details on responses based on permissions. ## Interface Definitions {% tab proto -%} -{% sample '../example.proto', 'rpc GetBook' %} - -{% sample '../example.proto', 'message GetBookRequest' %} - -{% sample '../example.proto', 'message Book' %} - {% tab oas %} {% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books/{book_id}.get' %} {% endtabs %} + +## Changelog + +* **2026-02-09**: Initial AEP-131, adapted from [Google AIP-131][] and aep.dev [AEP-131][]. + +[Google AIP-131]: https://google.aip.dev/131 + +[AEP-131]: https://aep.dev/131 + +[GET]: /http-get + +[URI path]: /paths + +[safe]: /64#common-method-properties + +[HTTP status codes]: /status-codes + +[200 OK]: /63#200-ok + +[404 Not Found]: /63#404-not-found + +[410 Gone]: /63#410-gone diff --git a/aep/general/0131/aep.yaml b/aep/general/0131/aep.yaml index c907128f..40d6592a 100644 --- a/aep/general/0131/aep.yaml +++ b/aep/general/0131/aep.yaml @@ -2,7 +2,7 @@ id: 131 state: approved created: 2026-02-09 -updated: 2026-02-09 +updated: 2026-02-10 slug: fetch placement: category: actions diff --git a/aep/general/0132/aep.md b/aep/general/0132/aep.md index 473ddec5..38b9ff5e 100644 --- a/aep/general/0132/aep.md +++ b/aep/general/0132/aep.md @@ -1,92 +1,46 @@ # List -In REST APIs, it is customary to make a `GET` request to a resource +In REST APIs, it is customary to make a [GET] request to a resource collection's URI (for example, `/publishers/{publisher_id}/books`) in order to -retrieve a list of the resources within that collection. - -Resource-oriented design AEP-121 honors this pattern through the `List` method. -These RPCs accept a parent collection (if one exists), and return a list of -responses matching that input. +retrieve a list of the resources within that collection. Resource-oriented design AEP-121 honors this pattern through +the `List` action. ## Guidance -APIs **should** provide a `List` method for resource collections.The purpose of -the `List` method is to return data from a finite collection (generally +APIs **should** provide a `List` action for resource collections. The purpose of +the `List` action is to return data from a finite collection (generally singular unless the operation supports [reading across collections][]). -When the `GET` method is used on a URI ending in a resource collection, the +When the [GET] HTTP method is used on a URI ending in a resource collection, the result **must** be a list of resources. ### Operation -{% tab proto %} - -{% sample '../example.proto', 'rpc ListBooks' %} - -- The RPC's name **must** begin with the word `List`. The remainder of the RPC - name **should** be the plural form of the resource's message name. -- The request message **must** match the RPC name, with a `-Request` suffix. -- The response message **must** match the RPC name, with a `-Response` suffix. - - The response **should** include fully-populated resources unless there is a - reason to return a partial response (see AEP-157). -- The HTTP verb **must** be `GET`. -- If the collection has a parent resource, The URI **must** contain a field - corresponding to the collection parent's name. - - This field **must** be called `parent`. - - The URI **must** have a variable corresponding to this field. - - The `parent` field **must** be the only variable in the URI path. All - remaining parameters **must** map to URI query parameters. -- There **must not** be a `body` key in the `google.api.http` annotation. -- There **should** be exactly one `google.api.method_signature` annotation with - a value of `"parent"` if a parent exists, and an empty string otherwise. - -{% tab oas %} +`List` operations **must** be made by sending a [GET] request to the resource +collection's [URI path]: ```http -GET /v1/publishers/{publisher_id}/books HTTP/2 -Host: bookstore.example.com -Accept: application/json +GET /v1/publishers/{publisher_id}/books ``` -`List` operations **must** be made by sending a `GET` request to the resource -collection's URI: - -- The HTTP method **must** be `GET`. - - The request **must** be safe and **must not** have side effects. -- There **must not** be a request body. - - If a `GET` request contains a body, the body **must** be ignored, and - **must not** cause an error. -- The request **must not** require any fields in the query string. - - The query string **may** include fields for common design patterns relevant - to list methods, such as `string filter` and `string orderBy`. - The URI **should** contain a variable for each individual ID in the resource hierarchy. - The path parameter for all resource IDs **must** be in the form `{resourceName}_id` (such as `book_id`), and path parameters representing - the ID of parent resources **must** end with `Id`. - -{% endtabs %} + the ID of parent resources **must** end with `id`. +- `List` actions **should** implement sorting and [filtering] mechanisms to allow clients to sort and narrow results. ### Requests -{% tab proto %} - -{% sample '../example.proto', 'message ListBooksRequest' %} +- The HTTP method **must** be [GET], and **must** follow the `GET` method guidelines in AEP-65. + - The request **must** be [safe] and **must not** have side effects. +- There **must not** be a request body. + - If a `GET` request contains a body, the body **must** be ignored, and + **must not** cause an error. +- The request **must not** _require_ any query parameters. + - Optional query parameters **may** be included (e.g., for [pagination] or [filtering]) -- A `parent` field **must** be included unless the resource being listed is a - top-level resource. It **should** be called `parent`. - - The field **must** be [annotated as required][aep-203]. - - The field **must** identify the [resource type][] of the resource being - listed with a `aepi.api.field_info.resource_reference` annotation. - - The field **must** accept the parent [path, _not_ the id](./0122) -- The `max_page_size` and `page_token` fields, which support pagination, - **must** be specified on all list request messages. For more information, see - AEP-158. - -**Note:** The `parent` field in the request object corresponds to the `parent` -variable in the `google.api.http` annotation on the RPC. This causes the -`parent` field in the request to be populated based on the value in the URL -when the REST/JSON interface is used. +{% tab proto %} {% tab oas %} @@ -96,96 +50,74 @@ when the REST/JSON interface is used. ### Responses -`List` operations **must** return a page of results, with each individual -result being a resource. - -- The array of resources **must** be named `results` and contain resources with - no additional wrapping. -- The `string nextPageToken` field **must** be included in the list response - schema. It **must** be set if there are subsequent pages, and **must not** be - set if the response represents the final page. For more information, see - AEP-158. -- The response struct **may** include a `int32 total_size` (or - `int64 total_size`) field with the number of items in the collection. - - The value **may** be an estimate (the field **should** clearly document - this if so). - - If filtering is used, the `total_size` field **should** reflect the size of - the collection _after_ the filter is applied. -- The response message **must** include a field corresponding to the resources - being returned, named for the English plural term for the resource, and - **should not** include any other repeated fields. -- Fields providing metadata about the list request (such as - `string next_page_token` or `int32 total_size`) **must** be included on the - response (not as part of the resource itself). +#### Successful Response -{% tab proto %} +A `List` action **must** return [200 OK] when resources are successfully retrieved. -{% sample '../example.proto', 'message ListBooksResponse' %} +The response **must** be [paginated][pagination] and follow these requirements: -{% tab oas %} +* The field `results` **must** be an array of resources, with the schema as a reference to the resource (e.g., + `#/components/schemas/Book`). +* Fields providing metadata about the `List` request (such as page tokens) **must** be included in the response wrapper + object, not as part of the resource itself. +* The response **must** ensure a deterministic default sort order to guarantee stable pagination. + +If the collection exists but contains no resources, the response **must** return a [200 OK] with an empty `results` +array. -{% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books.get.responses.200' %} +{% tab proto %} + +{% tab oas %} -- The field "results" **must** be an array of resources, with the schema as a - reference to the resource (e.g. `#/components/schemas/Book`). -- The field "nextPageToken" **must** be a string that contains the token to - retrieve the next page. This **must not** be set if the response represents - the final page. +{% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books.get.responses.200.content' %} {% endtabs %} -**Note:** List methods **may** return the complete collection to any user with -permission to make a successful List request on the collection, _or_ **may** -return a collection only containing resources for which the user has read -permission. This behavior **should** be clearly documented either for each List -method or as a standard convention in service-level documentation. Permission -checks on individual resources may have a negative performance impact so should -be used only where absolutely necessary. +#### Error Responses -### Errors +A List action **must** return appropriate error responses. See [HTTP status codes] for a complete guide to error +handling. -If the user does not have sufficient permission to know that the collection -exists, the service **should** reply with an HTTP 404 error, regardless of -whether or not the collection exists. Permission **must** be checked prior to -checking whether the collection exists. +Most common error scenarios: -If the user does have proper permission, but the requested collection does not -exist (generally because the parent does not exist), the service **must** reply -with an HTTP 404 error. +* Return [404 Not Found] if the parent resource does not exist (e.g., requesting `/publishers/{invalid_id}/books` when + the publisher doesn't exist). +* See [authorization checks](/authorization) for details on responses based on permissions. -**Note:** An empty collection which the user has permission to access **must** -return `200 OK` with an empty results array, and not `404 Not Found`. +**Note:** `List` actions **may** return the complete collection to any user with +permission to make a successful `List` request on the collection, _or_ **may** +return a collection only containing resources for which the user has read +permission. This behavior **should** be clearly documented either for each `List` +action or as a standard convention in service-level documentation. Permission +checks on individual resources may have a negative performance impact so should +be used only where necessary. ### Advanced operations -`List` methods **may** allow an extended set of functionality to allow a user +`List` actions **may** allow an extended set of functionality to allow a user to specify the resources that are returned in a response. The following table summarizes the applicable AEPs, ordered by the precedence of the operation on the results. -| Operation | AEP | -| ------------------------------ | ---------------------------------- | -| filter | [AEP-160](/0160) | -| ordering (`order_by`) | [AEP-132](#ordering) | -| pagination (`next_page_token`) | [AEP-158](/0158) | -| skip | [AEP-158](/0158/#skipping-results) | +| Operation | AEP | +|------------|----------------------| +| filtering | [AEP-160](/160) | +| ordering | [AEP-132](#ordering) | +| pagination | [AEP-158](/158) | -For example, if both the `filter` and `skip` fields are specified, then the -filter would be applied first, then the resulting set would be the results that -skip N entries of the filtered result. +For example, if both a filter and pagination fields are specified, then the +filter would be applied first, then the resulting set would be paginated. ### Ordering -`List` methods **may** allow clients to specify sorting order; if they do, the -request message **should** contain a `string order_by` field. +`List` actions **may** allow clients to specify sorting order; if they do, the +order **must** bew specified in a query parameter which **must** be a `string` named `orderBy`. -- Values **should** be a comma separated list of fields. For example: - `"foo,bar"`. +- Values **should** be a comma-separated list of fields. For example: + `foo,bar`. - The default sorting order is ascending. To specify descending order for a - field, users append a `-` prefix; for example: `"foo,-bar"`, `"-foo,bar"`. -- Redundant space characters in the syntax are insignificant. `"foo, -bar"`, - `" foo , -bar "`, and `"foo,-bar"` are all equivalent. + field, users append a `-` prefix; for example: `foo,-bar`, `-foo,bar`. - Subfields are specified with a `.` character, such as `foo.bar` or `address.street`. @@ -198,9 +130,9 @@ Some APIs need to "[soft-delete][]" resources, marking them as deleted or pending deletion (and optionally purging them later). APIs that do this **should not** include deleted resources by default in list -requests. APIs with soft deletion of a resource **should** include a -`bool show_deleted` field in the list request that, if set, will cause -soft-deleted resources to be included. +requests. APIs with soft deletion of a resource **may** include a `boolean` field named +`showDeleted` in the list request that, if set, will cause soft-deleted resources to be included. +For more information, see AEP-164. ## Interface Definitions @@ -218,10 +150,30 @@ soft-deleted resources to be included. {% endtabs %} - +## Changelog + +* **2026-02-09**: Initial creation, adapted from [Google AIP-132][] and aep.dev [AEP-132][]. + +[Google AIP-132]: https://google.aip.dev/131 + +[AEP-132]: https://aep.dev/131 + [reading across collections]: ./0159 + [soft-delete]: ./0164 -[resource type]: ./0004 +[GET]: /http-get + +[URI path]: /paths + +[safe]: /64#common-method-properties + +[pagination]: /pagination + +[filtering]: /filtering + +[HTTP status codes]: /status-codes + +[200 OK]: /63#200-ok - +[404 Not Found]: /63#404-not-found diff --git a/aep/general/0132/aep.yaml b/aep/general/0132/aep.yaml index a6d269dc..3b9f42a5 100644 --- a/aep/general/0132/aep.yaml +++ b/aep/general/0132/aep.yaml @@ -2,7 +2,7 @@ id: 132 state: approved created: 2026-02-09 -updated: 2026-02-09 +updated: 2026-02-10 slug: list placement: category: actions diff --git a/aep/general/0135/aep.md b/aep/general/0135/aep.md index 5df6596e..d417e6e3 100644 --- a/aep/general/0135/aep.md +++ b/aep/general/0135/aep.md @@ -26,38 +26,6 @@ steady-state and reading resource state returns a consistent `404 Not found` Delete methods are specified using the following pattern: -{% tab proto %} - -{% sample '../example.proto', 'message DeleteBookRequest' %} - -- The method's name **must** begin with the word `Delete`. The remainder of the - method name **should** be the singular form of the resource's name. -- The request schema name **must** exactly match the method name, with a - `Request` suffix. -- The request message field receiving the resource path **should** map to the - URI path. - - This field **should** be called `path`. - - The `path` field **should** be the only variable in the URI path. All - remaining parameters **should** map to URI query parameters. -- There **must not** be a `body` key in the `google.api.http` annotation. -- There **should** be exactly one `google.api.method_signature` annotation, - with a value of `"path"`. If an etag or force field are used, they **may** be - included in the signature. - -{% tab oas %} - -```http -DELETE /v1/publishers/{publisher_id}/books/{book_id} HTTP/2 -Host: bookstore.example.com -Accept: application/json -``` - -- The response body **should** be omitted. -- The HTTP response code **should** be `204 No Content` if the delete was - successful. - -{% endtabs %} - ### Requests Delete methods implement a common request pattern: @@ -69,38 +37,6 @@ Delete methods implement a common request pattern: **should not** include optional fields in the query string unless described in another AEP. -{% tab proto %} - -{% sample '../example.proto', 'message DeleteBookRequest' %} - -- A `path` field **must** be included. It **should** be called `path`. - - The field **should** be [annotated as required][aep-203]. - - The field **must** identify the [resource type][aep-4] that it references. -- The comment for the field **should** document the resource pattern. -- The request message **must not** contain any other required fields, and - **should not** contain other optional fields except those described in this - or another AEP. - -{% tab oas %} - -{% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books/{book_id}.delete.parameters' %} - -{% endtabs %} - -### Responses - -{% tab proto %} - -- The response message **should** be `google.protobuf.Empty`. - -{% tab oas %} - -{% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books/{book_id}.delete.responses.204' %} - -- Delete methods **should** return `204 No Content` with no response body. - -{% endtabs %} - ### Errors If the user does not have permission to access the resource, regardless of @@ -127,48 +63,6 @@ If an API allows deletion of a resource that may have child resources, the API **must** provide a `bool force` field on the request, which the user sets to explicitly opt in to a cascading delete. -{% tab proto -%} - -```proto -message DeletePublisherRequest { - // The path of the publisher to delete. - // Format: publishers/{publisher_id} - string path = 1 [ - (aep.api.field_info) = { resource_reference: [ "library.example.com/publisher" ], field_behavior: [ FIELD_BEHAVIOR_REQUIRED ] } - ]; - - // If set to true, any books from this publisher will also be deleted. - // (Otherwise, the request will only work if the publisher has no books.) - bool force = 2; -} -``` - -The API **must** fail with a `FAILED_PRECONDITION` error if the `force` field -is `false` (or unset) and child resources are present. - -{% tab oas -%} - -{% sample 'cascading_delete.oas.yaml', '$.paths' %} - -The API **must** fail with a `409 Conflict` error if the `force` field is -`false` (or unset) and child resources are present. - -{% endtabs %} - -## Interface Definitions - -{% tab proto %} - -{% sample '../example.proto', 'rpc DeleteBook' %} - -{% sample '../example.proto', 'message DeleteBookRequest' %} - -{% tab oas %} - -{% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books/{book_id}.delete' %} - -{% endtabs %} - ## Further reading - For soft delete and undelete, see AEP-164. diff --git a/aep/general/0158/aep.md b/aep/general/0158/aep.md index 9695c5b2..19ba80d0 100644 --- a/aep/general/0158/aep.md +++ b/aep/general/0158/aep.md @@ -13,6 +13,8 @@ be paginated. * If using offset-based pagination, _new_ APIs **must** implement [token-based offset pagination](#token-based-offset-pagination). * Query parameters for pagination **must** follow the guidelines in AEP-106. +* The array of resources **must** be named `results` and contain resources with + no additional wrapping. ### Cursor-based pagination @@ -55,7 +57,7 @@ responds with: ```json { - "books": [ + "results": [ { "id": "456", "title": "Les Misérables", diff --git a/aep/general/0160/aep.md b/aep/general/0160/aep.md index e1a879ac..7b1769e5 100644 --- a/aep/general/0160/aep.md +++ b/aep/general/0160/aep.md @@ -7,9 +7,9 @@ category, or timestamps. Filtering applies deterministic, field-aware constraints that are index-friendly and predictable. Filtering is distinct from: -* Sorting (ordering results) +* [Sorting] (ordering results) * [Pagination] (windowing results) -* [Searching] (partial text matching, fuzzy queries, complex boolean logic) +* [Searching][search] (partial text matching, fuzzy queries, complex boolean logic) **Note:** Because filters are intended for a potentially non-technical audience, they sometimes borrow from patterns of colloquial speech rather than common patterns found in code. @@ -32,7 +32,7 @@ Examples: ```http request ### Get all books created after 2025-01-01 -GET /books?created_after=2025-01-01T00:00:00Z +GET /books?createdTimeAfter=2025-01-01T00:00:00Z ### Get all published books GET /books?state=published @@ -42,7 +42,8 @@ GET /books?state=published Different APIs have different resource models and user needs, so the set of filterable fields and supported operators will vary. These guidelines ensure consistency in _how_ filters are named and composed, while allowing each API to -decide _which_ filters to offer based on real use cases. +decide _which_ filters to offer based on real use cases. Just because an operator is listed here doesn't mean it needs +to be implemented. Instead, this is saying "if you need to implement this parameter, name it this". Filtering parameter names **must** be stable (do not rename lightly), specific, and self-explanatory. @@ -51,18 +52,21 @@ APIs **must** use the direct field name for exact matches (e.g. `state=active`, APIs **should** use the following prefix/suffix modifiers for common comparisons. Use consistent prefixes/suffixes and only introduce them when there is more than one plausible comparison. APIs **should** use a subset as appropriate. -| Operator | Meaning | Example | -|-----------|---------------------------------------------------------|-----------------------------| -| | Equals (direct field name) | `status=active` | -| `_ne` | Not equals | `status_ne=cancelled` | -| `_lt`, | Less than | `price_lt=100` | -| `_lte` | Less than or equal | `price_lte=100` | -| `_gt`, | Greater than | `price_gt=0` | -| `_gte` | Greater than or equal | `price_gte=50` | -| `_before` | Timestamp-specific `<` | `created_before=2025-01-01` | -| `_after` | Timestamp-specific `>` | `created_after=2025-01-01` | -| `is_` | Boolean checks | `is_active=true` | -| `has_` | [Nullability and existence](#nullability-and-existence) | `has_phone_number=true` | +| Operator | Meaning | Example | +|----------------------|---------------------------------------------------------|----------------------------------| +| (none) | Equals (direct field name) | `status=ACTIVE` | +| `NotEqual` | Not equals | `statusNotEqual=CANCELLED` | +| `LessThan` | Strictly less than `<` | `priceLessThan=100` | +| `LessThanOrEqual` | Less than or equal `<=` | `priceLessThanOrEqual=100` | +| `GreaterThan` | Strictly greater than `>` | `priceGreaterThan=100` | +| `GreaterThanOrEqual` | Greater than or equal `>=` | `priceGreaterThanOrEqual=100` | +| `Maximum` | Upper bound (inclusive) `<=` | `capacityMaximum=500` | +| `Minimum` | Lower bound (inclusive) `>=` | `capacityMinimum=50` | +| `Before` | Timestamp strictly before `<` | `createdTimeBefore=2025-01-01` | +| `Latest` | Timestamp at or before `<=` | `createdTimeLatest=2025-01-01` | +| `After` | Timestamp strictly after `>` | `createdTimeAfter=2025-01-01` | +| `Earliest` | Timestamp at or after `>=` | `createdTimeEarliest=2025-01-01` | +| `has` (prefix) | [Nullability and existence](#nullability-and-existence) | `hasPhoneNumber=true` | ### Combining filters (`AND`) @@ -106,12 +110,12 @@ If a real use case requires it, APIs **should** implement a specialized [search] Query parameters do not naturally express "field is missing" vs. "field is present but empty". APIs **should** avoid exposing "missing vs present" semantics unless necessary. If existence filtering is required, APIs **may** add -explicit parameters such as `has_{field}=true|false` +explicit parameters such as `has{field}=true|false` Example: ``` http request -GET /users?has_phone_number=true +GET /users?hasPhoneNumber=true ``` APIs **must** document exactly what "has" means for each field (non-null, non-empty string, non-empty array, etc.). The @@ -163,18 +167,18 @@ defeat standardization benefits. For an in-depth analysis of filtering strategie guidelines, see [ADR-001: REST API Filtering and Searching Strategy](https://github.com/infusionsoft/aeps/blob/main/docs/arch/adr-001.md). -[query parameters]: /query-parameters +[Sorting]: /132#ordering [Pagination]: /pagination -[Searching]: /searching - [search]: /searching [GET]: /http-get ## Changelog +* **2026-02-18**: Change format from underscore (`_`) to `camelCase`. Add to naming conventions table. Use full words + instead of abbreviations. * **2026-01-27**: Removed search guidance, it will be a separate AEP. Align guidance with [ADR-001]. * **2025-12-11**: Initial creation, adapted from [Google AIP-160][] and aep.dev [AEP-160][]. diff --git a/aep/general/0160/aep.yaml b/aep/general/0160/aep.yaml index a94809bf..3f6025d1 100644 --- a/aep/general/0160/aep.yaml +++ b/aep/general/0160/aep.yaml @@ -3,6 +3,6 @@ id: 160 state: approved slug: filtering created: 2025-12-11 -updated: 2026-01-27 +updated: 2026-02-18 placement: category: design-patterns diff --git a/aep/general/example.oas.yaml b/aep/general/example.oas.yaml index 6996db3c..71ac4c39 100644 --- a/aep/general/example.oas.yaml +++ b/aep/general/example.oas.yaml @@ -7,10 +7,8 @@ components: properties: familyName: type: string - x-aep-field-number: 2 givenName: type: string - x-aep-field-number: 1 type: object type: array edition: @@ -36,13 +34,6 @@ components: - price - published type: object - x-aep-resource: - parents: - - publisher - patterns: - - publishers/{publisher_id}/books/{book_id} - plural: books - singular: book book-edition: properties: displayName: @@ -55,13 +46,6 @@ components: required: - displayName type: object - x-aep-resource: - parents: - - book - patterns: - - publishers/{publisher_id}/books/{book_id}/editions/{book_edition_id} - plural: book-editions - singular: book-edition isbn: properties: path: @@ -70,11 +54,6 @@ components: the service. type: string type: object - x-aep-resource: - patterns: - - isbns/{isbn_id} - plural: isbns - singular: isbn item: properties: book: @@ -94,13 +73,6 @@ components: - condition - price type: object - x-aep-resource: - parents: - - store - patterns: - - stores/{store_id}/items/{item_id} - plural: items - singular: item publisher: properties: description: @@ -111,11 +83,6 @@ components: the service. type: string type: object - x-aep-resource: - patterns: - - publishers/{publisher_id} - plural: publishers - singular: publisher store: properties: description: @@ -130,11 +97,6 @@ components: required: - name type: object - x-aep-resource: - patterns: - - stores/{store_id} - plural: stores - singular: store info: contact: email: aepsupport@aep.dev @@ -283,7 +245,7 @@ paths: '204': content: application/json: - schema: {} + schema: { } description: Successful response get: description: Get method for publisher @@ -430,7 +392,7 @@ paths: '204': content: application/json: - schema: {} + schema: { } description: Successful response get: description: Get method for book @@ -599,7 +561,7 @@ paths: '204': content: application/json: - schema: {} + schema: { } description: Successful response get: description: Get method for book-edition @@ -655,13 +617,6 @@ paths: schema: $ref: https://aep.dev/json-schema/type/operation.json description: Long-running operation response - x-aep-long-running-operation: - response: - schema: - properties: - success: - type: boolean - type: object /stores: get: description: List method for store @@ -736,7 +691,7 @@ paths: '204': content: application/json: - schema: {} + schema: { } description: Successful response get: description: Get method for store @@ -861,7 +816,7 @@ paths: '204': content: application/json: - schema: {} + schema: { } description: Successful response get: description: Get method for item @@ -942,9 +897,5 @@ paths: schema: $ref: https://aep.dev/json-schema/type/operation.json description: Long-running operation response - x-aep-long-running-operation: - response: - schema: - type: object servers: - url: http://localhost:8081