Building APIs with gRPC
This document discusses how to build a simple, one endpoint gRPC API service with Protocol Buffers, and prepare its client-side and server-side code with gRPC tools. It is a part of the Build API Services: A Beginner’s Guide tutorial series.
Note: This tutorial uses Python 3. gRPC, of course, supports a variety of programming languages.
About the API service
In this tutorial you will build an API service using gRPC where users can get their profiles. It has one resource, User
, and one method (endpoint) only:
Before you begin
- Set up your Python development environment. For this tutorial you do not need to install Google Cloud SDK and Google Cloud Client Library for Python.
- Install gRPC and Protocol Buffers:
pip install grpcio grpcio-tools
grpcio
is the gRPC package for Python. grpcio-tools
, the gRPC tools package, includesProtocol Buffers compiler with gRPC plug-in.
- Download the source code. Open
/grpc/getting_started
.
Understanding the code
Essentially, an API call to a method (endpoint) is nothing more than an input (request), an output (response), and some magic that associates the request with the response. Input offers all the parameters the method requires, and output is what the method returns.
In an gRPC API service, inputs (requests) and outputs (responses) are Protocol Buffers messages of specific types, defined in one or more .proto
files using Protocol Buffers language. Service definitions in the .proto
files associate the input message type with the output message type, and Protocol Buffers compiler compiles the .proto
file(s) into code artifacts. You may then use these artifacts to build your API service and its client libraries.
Resources and their fields
This gRPC API service features one resource: User
.
The resource name of User
is of the format //myapiservice.com/users/USER-ID
. User features 3 fields:
Note that for simplicity reasons, Protocol Buffers does not have built-in support for reserved and required fields. All the fields are optional. Developers must validate the data themselves on the server side (and client side, if necessary)
Writing protocol buffers
example.proto
(grpc/getting_started/example.proto
) is the Protocol Buffers specification of this API service. It comprises 3 parts: syntax version, message types, and service definitions:
Syntax version
syntax = “proto3”;
, declares the version of Protocol Buffers language (.proto
file syntax) you would like to use. In most cases it is recommended that developers use version proto3
; the default value is proto2
.
Message types
The .proto
file features two messages types: User
and GetUserRequest
. User
has three string type fields: name
, display_name
, and email
. GetUserRequest
has one string type field, name
.
The number after each field is the field number. Protocol Buffer messages use the field number, instead of the field name, to uniquely identify the field.
Service definition
The .proto
file features one service definition, ExampleUserService
, which consists of one method (endpoint), GetUser
. It takes a message of the GetUserRequest
type and returns another message of the User
type.
Preparing the code
Protocol Buffers compiler can now prepare the server-end and the client-end artifacts:
python -m grpc_tools.protoc -I. --python_out=codegen/ --grpc_python_out=codegen/ example.proto
The compiler generates two files: codegen/example_pb2.py
and codegen/example_pb2_grpc.py
. example_pb2.py
specifies how the message types in example.proto
should look like in Python. example_pb2_grpc.py
consists of two classes, ExampleServiceServicer
and ExampleServiceStub
, which you will use to build your own server-end and client-end code respectively.
Override ExampleServiceServicer
to create your own gRPC API service, as showcased in server.py
:
gRPC will invoke the overridden GetUser
method in the ExampleServiceServicer
class automatically when a client accesses the GetUser
endpoint. The framework automatically parses the GetUserRequest
Protocol Buffers message into a GetUserRequest
Python class, which you can manipulate idiomatically; it then takes a User
Python class, serializes it into a User
Protocol Buffers message, and return it to the client. Note that both GetUserRequest
and User
Python classes are defined in example_pb2.py
.
Next, use ExampleServiceStub
to create a client for the gRPC API service, as showcased in client.py
:
The client will connect to the gRPC API service automatically when running. When customers invoke the get_user
method with a name
parameter, you will prepare it into a GetUserRequest
Python class, and pass it to gRPC via the stub. gRPC then parses the GetUserRequest
Python class into a GetUserRequest
Protocol Buffers message, and send it to the server; the response from the server, a User
Protocol Buffers message, is then parsed into a User
Python class, and printed out.
If helpful, think of the .proto
file as a contract between the server and its clients, gRPC the courier, and Protocol Buffers the arbitrator/translator. Protocol Buffers enforces the contract, and translates what clients and server speak from/into a universal language, with gRPC carrying the communications around in HTTP/2. gRPC + Protocol Buffers perform all the administrative tasks behind the scenes (transportation, serialization, etc.) so your server and clients can focus on what is truly important: the business logic of your app.
Give it a Try
Run server.py
in the background:
python server.py
The server listens at localhost:8080. Use the client to connect to the server; run the following Python script:
import client
client = client.ExampleServiceClient()
client.get_user(‘//myapiservice.com/users/1’)
You should see the following outputs:
User fetched.
name: “//myapiservice.com/users/1”
display_name: “Example User”
email: “user@example.com”
What’s next
See Building APIs with gRPC: Continued for recommended practices and patterns in gRPC API services.