AIP-135
Standard methods: Delete
In REST APIs, it is customary to make a DELETE
request to a resource's URI
(for example, /v1/publishers/{publisher}/books/{book}
) in order to delete
that resource.
Resource-oriented design (AIP-121) honors this pattern through the Delete
method. These RPCs accept the URI representing that resource and usually return
an empty response.
Guidance
APIs should generally provide a delete method for resources unless it is not valuable for users to do so.
Delete methods are specified using the following pattern:
rpc DeleteBook(DeleteBookRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
delete: "/v1/{name=publishers/*/books/*}"
};
option (google.api.method_signature) = "name";
}
- The RPC's name must begin with the word
Delete
. 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 should be
google.protobuf.Empty
.- If the resource is soft deleted, the response message should be the resource itself.
- If the delete RPC is long-running, the response
message must be a
google.longrunning.Operation
which resolves to the correct response.
- The HTTP verb must be
DELETE
. - The request message field receiving the resource name should map to the
URI path.
- This field should be called
name
. - The
name
field should be the only variable in the URI path. All remaining parameters should map to URI query parameters.
- This field should be called
- There must not be a
body
key in thegoogle.api.http
annotation. - There should be exactly one
google.api.method_signature
annotation, with a value of"name"
. If an etag or force field are used, they may be included in the signature.
The Delete method should succeed if and only if a resource was present and
was successfully deleted. If the resource did not exist, the method should
send a NOT_FOUND
error.
Request message
Delete methods implement a common request message pattern:
message DeleteBookRequest {
// The name of the book to delete.
// Format: publishers/{publisher}/books/{book}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
type: "library.googleapis.com/Book"
}];
}
- A
name
field must be included. It should be calledname
.- The field should be annotated as required.
- The field must identify the resource type 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 AIP.
Soft delete
Note: This material was moved into its own document to provide a more comprehensive treatment: AIP-164.
Long-running delete
Some resources take longer to delete a resource than is reasonable for a regular API request. In this situation, the API should use a long-running operation instead:
rpc DeleteBook(DeleteBookRequest) returns (google.longrunning.Operation) {
option (google.api.http) = {
delete: "/v1/{name=publishers/*/books/*}"
};
option (google.longrunning.operation_info) = {
response_type: "google.protobuf.Empty"
metadata_type: "OperationMetadata"
};
}
- The response type must be set to the appropriate return type if the RPC
was not long-running:
google.protobuf.Empty
for most Delete RPCs, or the resource itself for soft delete (AIP-164). - Both the
response_type
andmetadata_type
fields must be specified (even if they aregoogle.protobuf.Empty
).
Cascading delete
Sometimes, it may be necessary for users to be able to delete a resource as well as all applicable child resources. However, since deletion is usually permanent, it is also important that users not do so accidentally, as reconstructing wiped-out child resources may be quite difficult.
If an API allows deletion of a resource that may have child resources, the API
should provide a bool force
field on the request, which the user sets to
explicitly opt in to a cascading delete.
message DeletePublisherRequest {
// The name of the publisher to delete.
// Format: publishers/{publisher}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
type: "library.googleapis.com/Publisher"
}];
// 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.
Protected delete
Sometimes, it may be necessary for users to ensure that no changes have been made to a resource that is being deleted. If a resource provides an etag, the delete request may accept the etag (as either required or optional):
message DeleteBookRequest {
// The name of the book to delete.
// Format: publishers/{publisher}/books/{book}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
type: "library.googleapis.com/Book"
}];
// Optional. The etag of the book.
// If this is provided, it must match the server's etag.
string etag = 2;
}
If the etag is provided and does not match the server-computed etag, the
request must fail with a ABORTED
error code.
Note: Declarative-friendly resources (AIP-128) must provide the etag
field for Delete requests.
Delete if existing
If the service uses client-assigned resource names, Delete
methods may
expose a bool allow_missing
field, which will cause the method to succeed in
the event that the user attempts to delete a resource that is not present (in
which case the request is a no-op):
message DeleteBookRequest {
// The book to delete.
// Format: publishers/{publisher}/books/{book}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference).type = "library.googleapis.com/Book"
];
// If set to true, and the book is not found, the request will succeed
// but no action will be taken on the server
bool allow_missing = 2;
}
More specifically, the allow_missing
flag triggers the following behavior:
- If the method call is on a resource that does not exist, the request is a
no-op.
- The
etag
field is ignored.
- The
- If the method call is on a resource that already exists, the resource is deleted (subject to other checks).
Note: Declarative-friendly resources (AIP-128) should expose the
bool allow_missing
field.
Errors
If the user does not have permission to access the resource, regardless of
whether or not it exists, the service must error with PERMISSION_DENIED
(HTTP 403). Permission must be checked prior to checking if the resource
exists.
If the user does have proper permission, but the requested resource does not
exist, the service must error with NOT_FOUND
(HTTP 404) unless
allow_missing
is set to true
.
Further reading
- For soft delete and undelete, see AIP-164.
- For bulk deleting large numbers of resources based on a filter, see AIP-165.
Changelog
- 2022-06-02: Changed suffix descriptions to eliminate superfluous "-".
- 2022-02-02: Changed eTag error from
FAILED_PRECONDITION
toABORTED
making it consistent with change to AIP-154 & AIP-134 on 2021-03-05. - 2020-10-06: Added guidance for declarative-friendly resources.
- 2020-10-06: Added guidance for allowing no-op delete for missing resources.
- 2020-10-06: Moved soft delete and undelete guidance into a new AIP-164.
- 2020-06-08: Added guidance for
Get
of soft-deleted resources. - 2020-02-03: Added guidance for error cases.
- 2019-10-18: Added guidance on annotations.
- 2019-08-01: Changed the examples from "shelves" to "publishers", to present a better example of resource ownership.
- 2019-06-10: Added guidance for long-running delete.
- 2019-05-29: Added an explicit prohibition on arbitrary fields in standard methods.