Intuitive CLIs for gRPC APIs
Noah Dietz
(@no_d_here)
curl https://my.api.net/post 
-H "Content-Type: application/json" 
-d '{"requird": "field"}'
curl https://my.api.net/post 
-H "Content-Type: application/json" 
-d '{"required": "field"}'
What it feels like today
Web Interface Command-line API
Perfect World
Web Interface Command-line API
Likely Reality
Web Interface Command-line APIAPI
Q: Why do we care about command line
consumption?
A: It’s a gateway for new developers and
consumers
Q: How can we improve the command line
API consumption experience?
A: Use structured definitions to intuitively
represent the interface
Protocol Buffers
Code-gen
Source
Protocol Buffers
IDL (Interface definition language)
Describe once and generate interfaces for any
language.
Data Model
Structure of the request and response.
Wire Format
Binary format for network transmission.
service RouteGuide {
rpc GetFeature(Point) returns (Feature);
rpc RouteChat(stream RouteNote) returns (stream RouteNote);
}
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
message Feature {
string name = 1;
Point location = 2;
}
message RouteNote {
Point location = 1;
string message = 2;
}
C/C++
Java is a registered trademark of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.
Cutting edge transport
Bespoke wire format
Consumed via generated code
How from CLI?!
State of the Art
● Server reflection-based CLIs
○ Similar to HTTP cURL
○ Favors flexibility over API
consumption experience
● github.com/grpc/grpc/.../grpc_cli
● github.com/fullstorydev/grpcurl
#grpcurl example
# listing available Services
> grpcurl my.api.net list
MyService
# listing Service RPCs
> grpccurl my.api.net list MyService
MethodA
MethodB
# describing specific Service RPC
> grpcurl my.api.net describe MyService MethodA
…
# describing specific Message
> grpcurl my.api.net describe MethodAInput
…
# invoking a Service RPC
> grpcurl -d '{"name": "Jane Doe"}' 
my.api.net 
MyService.MethodA
…
Handwritten CLIs?
github.com/googleapis/kiosk
“Why wasn’t this CLI generated?”
- @timburks
@no_d_here
Generated
Command Line
Interfaces
Generated Command Line Interfaces (gcli)
● Go lang
● Cobra CLI framework
● Uses Google generated API clients (GAPIC)
● github.com/googleapis/gapic-generator-go
Goals
● Generate a CLI tailored to a given gRPC
API
● Generate a more human-oriented
interface
● Unified experience across consumption
mediums
Non-Goals
● Provide a single, generic utility that can
invoke any gRPC API
● Replace gcloud SDK as the canonical
Google Cloud CLI
Generate a CLI tailored to
a given gRPC API
API Description → Command Structure
kctl display list-kiosks
Root command Service name Method name
API Description → Command Structure
$ kctl display
Top level command for Service: Display
Usage:
kctl display [command]
Available Commands:
create-kiosk
create-sign
delete-kiosk
delete-sign
get-kiosk
get-sign
list-kiosks
list-signs
...
Payload construction: Primitives
kctl display get-sign --id=<int32>
Field name Field type
kctl ... --kiosk_ids=<int32> --kiosk_ids=<int32>
Repeated field name Repeated field type
Payload construction: Repeated Primitives
kctl ... --size.width=<int32> --size.height=<int32>
Nested
field’s
name
Nested
message
field
name
Nested
message
field
type
Payload construction: Nested Messages
“Correct by construction”
- @timburks
Generate a more
human-oriented interface
Making machine things human: LRO
kctl display cycle-kiosks --id=123 --follow
Wait for LRO
to finish
kctl display cycle-kiosks-poll --operation=<id>
Operation IDExtra polling
helper
Making machine things human: Streaming
● stdin OR
--from_file=<path>
○ JSON Protobuf format
● stdout OR
--out_file=<path>
# JSON Protobuf input format
{"field": "a"}
{"field": "b"}
{"field": "c"}
{"field": "d"}
Unified experience
across consumption
mediums
Educate the consumer: --help
$ kctl display create-sign --help
Create a sign. This enrolls the sign for sign display.
Usage:
kctl display create-sign [flags]
Flags:
--from_file string Absolute path to JSON file containing request payload
-h, --help help for create-sign
--image bytesHex Data of image currently displayed on sign
--name string Required. Name of the sign
--text string Text to display on sign
...
Educate the consumer: bash autocompletion
$ kctl completion --help
Enable bash completion like so:
Linux:
source <(kctl completion)
Mac:
brew install bash-completion
kctl completion > $(brew --prefix)/etc/bash_completion.d/kctl
Usage:
kctl completion [flags]
Flags:
-h, --help help for completion
kctl/
create-kiosk.go
delete-sign.go
get-sign.go
list-kiosks.go
create-sign.go
display_service.go
list-signs.go
delete-kiosk.go
get-kiosk.go
root.go
Educate the consumer: code & structure
Service command
root command
Method command
# Input message to generated usage
message CreateKioskRequest →
var CreateKioskInput *pb.CreateKioskRequest
# RPC to generated cmd to usage
rpc CreateKiosk → var createKioskCmd →
kctl display create-kiosk
@no_d_here
Demo
@no_d_here
When a CLI won’t
help
Large, nested payloads
--consistency_selector.new_transaction.mode.read_only.consistency_selector.read_time.seconds
--consistency_selector.new_transaction.mode.read_only.consistency_selector.read_time.nanos
firestore.Firestore.BatchGetDocuments
dataproc.WorkflowTemplateService.CreateWorkflowTemplate
● Had > 50 flags
● Deeply nested fields
Unstructured “structured” APIs
curl https://my.api.net/post 
-H "Content-Type: application/json" 
-d '{"sql": "SELECT * FROM foo;"}'
@no_d_here
All this to say...
Wrapping it up
● CLI API consumption is a gateway & important tool
● Structured API definitions should guide the CLI experience
● Educate the consumer at every opportunity
● Not every API is meant for the command line
Thank you
Noah Dietz
(@no_d_here)

Intuitive CLIs for gRPC APIs