AIP-4210

Client library generators

API guidelines exist in order to promote simple, intuitive, and consistent APIs. Users familiar with APIs that generally adhere to AIP guidance are able to take what they learn in prior APIs and apply it to new ones.

Client libraries provide a mechanism for users to get started with APIs more quickly, by simplifying common concerns (such as auth) and by a language-native way to call API endpoints and receive language-native responses. However, for these libraries to provide the most value, they also must be simple, intuitive, and consistent. Code generators provide a means for producing consistent client libraries at scale.

Code generators following the standards in these AIPs are known as "generated API client generators", or GAPIC generators for short. The resulting libraries are colloquially called GAPICs.

Note: Because this AIP describes guidance and requirements in a language-neutral way, it uses generic terminology which may be imprecise or inappropriate in certain languages or environments (for example, the use of the term class even though languages such as Go do not have classes). This AIP's particular use of vocabulary is best understood as an explanation of principles, and precise adherence to exact vocabulary in this AIP is not an expectation.

Guidance

Protobuf plugins

Code generators must be implemented as plugins to protoc, the protocol buffer compiler. The plugin system allows plugins to be written in any language, and plugins should ordinarily be written in the language being targeted, in order to take advantage of in-language tooling, and to ensure that experts in the target environment are able to meaningfully contribute.

  • protoc expects plugins to be an executable in $PATH, and named protoc-gen-{plugin_name}, corresponding to the --{plugin_name}_out option sent to the protoc executable.
    • For a plugin creating client libraries for a specific language, the option name should follow the convention --{lang}_gapic_out (meaning the corresponding plugin executable is named protoc-gen-{lang}_gapic).
  • Plugins must accept a serialized CodeGeneratorRequest object (defined in plugin.proto) on stdin; the bulk of this is a series of FileDescriptorProto messages (defined in descriptor.proto).
  • Plugins must emit a serialized CodeGeneratorResponse object (defined in plugin.proto) on stdout.

CLI options

Code generators should be able to run without any options or flags if at all possible, and be able to generate a valid library from only the protos. If options are required, protoc allows them to be passed as --{plugin_name}_opt, and the string provided here becomes set as the parameter string on the CodeGeneratorRequest.

Code generators must not rely on environment variables for configuration.

Expected behavior

This section outlines the expected behavioral attributes of the output of the client library generator (in other words: the libraries that the generators write). Client libraries must implement these concepts in order to be considered complete.

Services and methods

Each of the service and rpc directives in the requested protos must be represented in the client library output, unless the language or transport is unable to support it.

Note: While how to accomplish this may vary from language to language, in most classical languages it is probably a class for each service, containing methods for each RPC.

  • The classes generated for each service directive must honor the google.api.default_host annotation if it is provided, and use that host as the default hostname. These classes should provide a mechanism for the end user to override the hostname.
    • If the google.api.default_host annotation is not present on the service directive, then the generated class should require a hostname when it is instantiated.
  • Additionally, if the classes generated for each service support using OAuth and service credentials, they must honor the google.api.oauth_scopes annotation (if it is provided), and use these scopes by default.
  • Services that have set the deprecated protobuf option to true should have an equivalent deprecation tag generated in the generated class. If applicable, this tag may include a comment that specifies when the service will be removed, which is typically the next major version update. Similarly, RPCs with this option set to true should have their generated language method(s) marked as deprecated.
  • Finally, service classes must also accept credentials, which are used appropriately when requests are made. (Accepting a custom gRPC channel satisfies this requirement.)

Long-running operations

An RPC is considered to be a "long-running" RPC if (and only if) the RPC's return type is google.longrunning.Operation. Any API which has one or more RPCs returning an Operation is expected to implement the Operations service.

Because the response and metadata fields in Operation are of the type google.protobuf.Any, it is necessary to know what message to use to deserialize them. This is annotated on the RPC using the google.longrunning.operation_info annotation.

Note: The values in this struct are strings, not message objects; the code generator uses the string to determine the appropriate message to use. Strings with no period (.) character refer to a message in the same proto package.

Code generators should fail with an error if a type is provided in the operation_info annotation which was not imported, or if no response type or metadata type is provided. Code generators should fail with an error if either the response_type or metadata_type keys are omitted.

Client libraries must honor the LRO interface; if an RPC has an Operation as its return type, the generated method must intercept it and return an appropriate idiomatic object for resolving the LRO (such as a Future or Promise bound to the underlying Operation object).

Streaming

Client libraries must implement streaming to the extent that their supporting transports allow. An RPC is considered to be streaming if the stream keyword is present on the argument or response type. This is present in the MethodDescriptorProto message using the client_streaming and server_streaming keys.