API Improvement Proposals (AIPs)

repository·master·Indexed 23 days ago

https://github.com/aip-dev/google.aip.dev

A collection of design documents providing high-level, concise guidance for API development, modeled after Python's PEPs. The documentation includes general API guidance, a framework for adopting AIPs in an organization, and specific standards for Actions on Google, including Built-in Intents (BII), naming conventions for custom methods, and fulfillment API common types.

Tokens
104K
Snippets
173
Records
464
Agent score
82%

What's inside google.aip.dev

  1. What is an AIP and how are they used?

    master

    An API Improvement Proposal (AIP) is a design document that provides high-level, concise documentation for API development. AIPs serve as the source of truth for API-related documentation and are the primary means for discussing and reaching consensus on API guidance.

    There are two main types of AIPs:

    1. Guidance AIPs: Provide instructions for API producers to help them write simple, intuitive, and consistent APIs. They are used by reviewers as a basis for review comments.
    2. Process AIPs: Describe processes surrounding API design, often affecting the AIP process itself.
  2. What is resource-oriented design?

    master

    Resource-oriented design is a pattern for specifying RPC APIs based on three high-level principles:

    1. Resources (Nouns): The fundamental building blocks are individually-named resources and the relationships/hierarchies between them.
    2. Methods (Verbs): A small number of standard methods provide semantics for most operations, though custom methods are available for specialized logic.
    3. Stateless Protocol: Each interaction between client and server is independent. Resources are directly addressable without requiring a specific sequence of previous requests.

    This pattern borrows principles from REST but defines its own patterns for RPC environments.

  3. Overview of the Filtering syntax

    master

    When using List or Search methods, APIs may provide a string filter field to allow users to query collections. The syntax is designed to be accessible to non-technical audiences and often uses colloquial patterns.

    Key Characteristics:

    • Single Field: A request should have exactly one string filter field.
    • Fuzzy Matching: Filters often support fuzzy matching and result ranking. For deterministic evaluation, refer to [CEL].
    • Literals: A bare literal (e.g., "Hugo") matches against field values. If no field is specified, it typically matches anywhere in the object.
    • Whitespace: Literals separated by whitespace (e.g., Victor Hugo) are treated as having a fuzzy AND relationship (Victor AND Hugo).
  4. Implement criteria-based delete using the Purge pattern

    master

    When an API needs to delete a large number of resources (thousands or more) based on filter parameters rather than individual names, use the Purge pattern. This is a rare pattern reserved for cases where the standard Batch Delete ([AIP-235]) is insufficient.

    Implementation Requirements

    • RPC Naming: The RPC name must begin with Purge followed by the plural form of the resource (e.g., PurgeBooks).
    • HTTP Mapping: Use the POST verb. The URI path should represent the collection, and the body must be set to "*".
    • Long-Running Operations: The RPC must return a google.longrunning.Operation that resolves to a message named with the Response suffix (e.g., PurgeBooksResponse).

    Request Message Structure

    A PurgeRequest message must include:

    • string parent: The resource type being purged. Use "-" to support deletion across multiple parents.
    • string filter: A filter string following [AIP-160] semantics. A wildcard "*" may be supported to delete everything.
    • bool force: A mandatory field. If false, the API must return a count and a sample of resources that would be deleted without actually performing the deletion.
    rpc PurgeBooks(PurgeBooksRequest) returns (google.longrunning.Operation) {
      option (google.api.http) = {
        post: "/v1/{parent=publishers/*}/books:purge"
        body: "*"
      };
      option (google.longrunning.operation_info) = {
        response_type: "PurgeBooksResponse"
        metadata_type: "PurgeBooksMetadata"
      };
    }
  5. Implement Filtering in list and search methods (AIP-160)

    master

    To allow users to narrow down results when listing resources, implement the Filtering pattern defined in [AIP-160].

    Filtering involves providing a syntax (similar to Google search) that allows users to specify criteria to return only the subset of resources they are interested in. This pattern is typically applied to list and search methods within an API.

  6. Normalize Unicode values to Normalization Form C

    master

    To ensure consistency and avoid discrepancies in size or representation, all Unicode string values should be stored in Normalization Form C (NFC).

    This is critical for preventing issues where visually identical text (like accented characters) is represented by different byte sequences or code point combinations. For example, the character é can be represented as a single code point (U+00E9) or as a combination of e and an accent (U+0061 + U+0301). Using NFC provides a canonical, compact representation.

  7. Implement authorization checks with AIP-211

    master

    AIP-211 defines when authorization checks should occur during a request lifecycle and how to respond to failures.

    To prevent information leakage (security concerns regarding whether a resource exists), if an authorization check fails, the service must consistently return PERMISSION_DENIED. The error message should also indicate that the resource might not exist, regardless of its actual existence.

  8. Understand Policy Experiments for safe rollouts

    master

    A Policy Experiment is a nested resource used to preview changes to a live policy without affecting live traffic. Experiments are stored as a collection under a parent Policy resource.

    Resource Naming Convention:

    • Live Policy: projects/{project}/locations/{location}/policies/{policy}
    • Experiment: projects/{project}/locations/{location}/policies/{policy}/experiments/{experiment}
    • Experiment Resource Type: Must follow the pattern *RegularResourceType*Experiment (e.g., FirewallPolicyExperiment).

    Workflow:

    1. Create an Experiment: Define the intended new state in the policy field of the experiment.
    2. Start Preview: Use the startPreview method to begin generating logs for evaluation.
    3. Evaluate: Inspect logs using the system-generated log_prefix to compare experiment behavior against the live policy.
    4. Promote (Commit): Use the commit method (or manually copy the policy and delete the experiment) to apply the changes to the live policy.
    message PolicyExperiment {
      // google.api.resource, name, and other annotations and fields
    
      // The policy experiment. This Policy will be used to preview the effects of
      // the change but will not affect live traffic.
      Policy policy = 2;
    
      // The metadata associated with this policy experiment.
      PolicyPreviewMetadata preview_metadata = 3
          [(google.api.field_behavior) = OUTPUT_ONLY];
    
      // Allows clients to store small amounts of arbitrary data.
      map<string, string> annotations = 4;
    }
  9. Handle optional sensitive fields with a `_set` boolean

    master

    If sensitive information is optional within a resource, use an OUTPUT_ONLY boolean field with a _set postfix to indicate whether the sensitive information has been provided. This allows clients to know if the secret exists without exposing the secret itself.

    message Integration {
      string name = 1 [(google.api.field_behavior) = IDENTIFIER];
      string uri = 2;
    
      // A secret to be passed in the `Authorization` header of the webhook.
      string shared_secret = 3 [ 
        (google.api.field_behavior) = INPUT_ONLY];
    
      // True if a `shared_secret` has been set for this Integration.
      bool shared_secret_set = 4 [ 
        (google.api.field_behavior) = OUTPUT_ONLY];
    }
  10. Structure resource names for Firebase Projects and Apps

    master

    When designing Firebase APIs that use an App ID, the resource hierarchy must include a Project identifier in the parent path. A resource name should not start directly with the App ID; instead, it must follow the pattern projects/*/apps/*/items.

    Because App IDs are globally unique, Firebase APIs should support the unique resource lookup pattern. This allows using a - in place of a specific Project ID or Project Number to reference the app directly.

  11. Use REQUIRED for mandatory fields

    master

    The REQUIRED annotation indicates a field must be present and set to a non-empty (truthy) value on the request or resource.

    Definition of 'truthy':

    • Primitives: Values other than 0, 0.0, empty string/bytes, and false.
    • Repeated fields/maps: Values with at least one entry.
    • Messages: Any message with at least one "truthy" field.

    When to use:

    • On a resource: If the resource is only valid if the value is stored. During create, the value must be provided. During update, the user may omit it if it is also absent from the field mask (indicating no change).
    • On a request message: The value must be provided in the request; failure to do so must cause an error (typically INVALID_ARGUMENT).

    When NOT to use:

    • For fields that are always present in a response.
    • For conditionally required fields.
    • For fields that are never used as user input.
  12. Design opaque and secure Page Tokens

    master

    Page tokens are used to track pagination progress and must follow these security and design principles:

    • Opacity: Tokens must be opaque, URL-safe strings that are not user-parseable. This prevents users from relying on implementation details, which allows the API to change its pagination logic without breaking clients.
      • Note: Base-64 encoding a transparent token is not sufficient obfuscation.
      • Pattern: An API may define an internal protocol buffer message, serialize it, and then Base-64 encode it to create an opaque token.
    • Authorization: Page tokens must not provide authorization to resources. Authorization must be performed on every request as usual, regardless of whether a page_token is present.
    • Expiration: APIs may expire page tokens after a reasonable time (e.g., three days) to save storage. This behavior does not need to be documented.