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 URL 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.
  • 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 "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-example.googleapis.com/Book"
    }];
}
  • A name field must be included. It should be called name.
    • The field should be annotated as required.
    • The field should 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

APIs may support the ability to “undelete”, to allow for situations where users mistakenly delete resources and need the ability to recover.

If a resource needs to support undelete, the Delete method should simply mark the resource as having been deleted, but not completely remove it from the system. If the method behaves this way, it should return the updated resource (not google.protobuf.Empty), with its state set to DELETED.

Soft-deleted resources should not show up in list requests by default (unless bool show_deleted is true).

The undelete functionality should be implemented through an “undelete” custom method.

APIs that soft delete resources may choose a reasonable strategy for purging those resources, including automatic purging after a reasonable time (such as 30 days), allowing users to set an expiry time, or retaining the resources indefinitely. Regardless of what strategy is selected, the API should document when soft deleted resources will be completely removed.

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.
  • Both the response_type and metadata_type fields must be specified (even if they are google.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;

  // 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;

  // 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 FAILED_PRECONDITION error code.

Changelog

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