AIP-164

Soft delete

There are several reasons why a client could desire soft delete and undelete functionality, but one over-arching reason stands out: recovery from mistakes. A service that supports undelete makes it possible for users to recover resources that were deleted by accident.

Guidance

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 must 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 instead of google.protobuf.Empty.

Resources that support soft delete should have an expire_time field as described in AIP-148. Additionally, resources should include a DELETED state value if the resource includes a state field (AIP-216).

Undelete

A resource that supports soft delete should provide an Undelete method:

rpc UndeleteBook(UndeleteBookRequest) returns (Book) {
  option (google.api.http) = {
    post: "/v1/{name=publishers/*/books/*}:undelete"
    body: "*"
  };
}
  • The HTTP verb must be POST.
  • The body clause must be "*".
  • The response message must be the resource itself. There is no UndeleteBookResponse.
    • The response should include the fully-populated resource unless it is infeasible to do so.
    • If the undelete RPC is long-running, the response message must be a google.longrunning.Operation which resolves to the resource itself.

Undelete request message

Undelete methods implement a common request message pattern:

message UndeleteBookRequest {
  // The name of the deleted book.
  // 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 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.

Long-running undelete

Some resources take longer to undelete a resource than is reasonable for a regular API request. In this situation, the API should use a long-running operation (AIP-151) instead:

rpc UndeleteBook(UndeleteBookRequest) returns (google.longrunning.Operation) {
  option (google.api.http) = {
    post: "/v1/{name=publishers/*/books/*}:undelete"
    body: "*"
  };
  option (google.longrunning.operation_info) = {
    response_type: "Book"
    metadata_type: "OperationMetadata"
  };
}
  • The response type must be set to the resource (what the return type would be if the RPC was not long-running).
  • Both the response_type and metadata_type fields must be specified.

List and Get

Soft-deleted resources should not be returned in List (AIP-132) responses by default (unless bool show_deleted is true). Get (AIP-131) requests for soft-deleted resources should return the resource (rather than a NOT_FOUND error).

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 (AIP-214), or retaining the resources indefinitely. Regardless of what strategy is selected, the API should document when soft deleted resources will be completely removed.

Declarative-friendly resources

A resource that is declarative-friendly (AIP-128) should support soft delete and undelete.

Important: There is an ambiguity in declarative tooling between "create" and "undelete". When given an alias which was previously deleted and a directive to make it exist, tooling usually does not know if the intent is to restore the previously-deleted resource, or create a new one with the same alias. Declarative tools should resolve this ambiguity in favor of creating a new resource: the only way to undelete is to explicitly use the undelete RPC (an imperative operation), and declarative tools may elect not to map anything to undelete at all.

Declarative-friendly resources must use long-running operations for both soft delete and undelete. The service may return an LRO that is already set to done if the request is effectively immediate.

Declarative-friendly resources must include validate_only (AIP-163) and etag (AIP-154) in their Undelete methods.

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 (either it was never created or already expunged), the service must error with NOT_FOUND (HTTP 404).

If the user calling a soft Delete has proper permission, but the requested resource is already deleted, the service must succeed if allow_missing is true, and should error with NOT_FOUND (HTTP 404) if allow_missing is false.

If the user calling Undelete has proper permission, but the requested resource is not deleted, the service must respond with ALREADY_EXISTS (HTTP 409).

Further reading

  • For the Delete standard method, see AIP-135.
  • For long-running operations, see AIP-151.
  • For resource freshness validation (etag), see AIP-154.
  • For change validation (validate_only), see AIP-163.

Changelog

  • 2021-07-12: Added error behavior when soft deleting a deleted resource.
  • 2021-07-12: Clarified that ALREADY_EXISTS errors apply to Undelete.
  • 2021-07-12: Changed the expire_time field to "should" for consistency with AIP-148.
  • 2020-09-23: Soft delete material in AIP-135 migrated to this AIP.