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
The general flow of code generation for client libraries and supporting code in most supported languages is outlined below.
Note: Exceptions to this pattern are typically due to use of a unique stack
e.g. Node.js use of protobuf.js
and grpc-node
which don't have code
generation, or Python using a protobuf wrapper in proto-plus-python
, but the
general GAPIC flow remains the same.
digraph {
node [ style="filled,solid" shape=box fontname="Roboto" ];
splines=ortho;
nodesep=0.3;
center=true;
proto [ label="API Protobuf\nDescriptors" shape=rectangle fillcolor=aliceblue ];
subgraph cluster_code_generators {
rank = same;
style = filled;
fillcolor = lightgrey;
node [ shape=oval ];
protobuf [ label="protobuf\ngenerator" fillcolor=deepskyblue3 ];
grpc [ label="gRPC\ngenerator" fillcolor=gold3 ];
gapic [ label="GAPIC\ngenerator" fillcolor=darkseagreen ];
}
proto -> protobuf;
proto -> grpc;
proto -> gapic;
subgraph cluster_generated_code {
rank = same;
style = filled;
fillcolor = lightgrey;
node [ shape=rectangle ];
protobuf_output [ label="Message & Enum\nCode" fillcolor=deepskyblue3 ];
grpc_output [ label="Server & Client\nStubs" fillcolor=gold3 ];
gapic_output [ label="Google API\nClient" fillcolor=darkseagreen ];
}
protobuf -> protobuf_output;
grpc -> grpc_output;
gapic -> gapic_output;
assembly [ label="Package\nassembly" shape=oval fillcolor=aliceblue ];
protobuf_output -> assembly
grpc_output -> assembly
gapic_output -> assembly
assembled_package [ label="Package of\ngenerated code" fillcolor=aliceblue ];
assembly -> assembled_package
}
The following sections focus on the "GAPIC generator" in the above diagram.
Protobuf plugins
The protobuf compiler, protoc
, supports a plugin system for code
generation. The plugin system allows plugins to be written in and for any
language.
Code generators must be implemented as protoc
plugins. The following
rules apply to the implementation of a client library generator as a protoc
plugin:
- The plugin should be written in the language being targeted for generation.
protoc
expects plugins to be an executable in$PATH
, and namedprotoc-gen-{plugin_name}
, corresponding to the--{plugin_name}_out
option sent to theprotoc
executable. As such:- the plugin executable should be named
protoc-gen-{lang}_gapic
- the plugin option should follow the convention
--{lang}_gapic_out
- the plugin executable should be named
- The plugin must not leverage
protoc
"insertion points". Despite theprotoc
plugin documentation indicating the existence of insertion points, their use is unsupported and discouraged by the Protobuf team.
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
.
Important: The CodeGeneratorRequest.parameter
value is a comma-delimited
string of all associated plugin option values that appear at execution time.
This means that commas cannot be used to delimit list-like plugin option values.
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.
Messages and Enums
Client library generators should not generate code for message
or enum
descriptors which are already generated by the Protobuf-provided code
generators.
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 thegoogle.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 theservice
directive, then the generated class should require a hostname when it is instantiated.
- If the
- 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 totrue
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 totrue
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.)
- Code generators must not generate client stub classes, that would normally be generated by gRPC, in addition to client library classes.
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.
Changelog
- 2023-06-22: Added code gen diagram, message/enum guidance, and cleaned up plugin & option guidance.