Design new API
The beginning and core of Sisyphus' workflow is the API design, around which everything revolves. Here Sisyphus strongly recommends referring to the Google API Improvement Proposals and following it to design API.
This guide contains Google's best practices for API design. When you encounter a problem with API design and feel uncertain about doing this or that, take a look at Google API Improvement Proposals。
Intellij Protobuf Plugin
Intellij Protobuf Plugin is a plugin for Intellij's Protobuf language plugin that helps you write Protobuf files quickly.
The plugin provides many features that the official plugin does not have, such as AIP integration, import optimization, etc., and also integrates some features of Sisyphus hold.
You can download and install the plugin by searching for protobuf
in Intellij's Preferences - Plugins
page.
Note that when you use IntelliJ IDEA Ultimate, an official Protocol Buffer plugin is bundled, Intellij Protobuf Plugin
is not compatible with this plugin, you need to disable the Protocol Buffer
plugin and the gRPC
plugin on the
plugins page.
Don't worry, because the Intellij Protobuf Plugin already integrates most of the features of the Protocol Buffer
plugin and the gRPC
plugin, and provides more and smarter features.
Designing the first API
Once the plugin is installed, a new proto file can be created in the src/main/proto
directory.
Before creating the file, you need to create a new package, refer to AIP-191.
In general, Java package names can be proto package names by simply dropping root domains like com
, net
, io
, etc.
Here we use bybutter.showcase.v1
as our package name and create a book.proto
under this package.
syntax = "proto3";
package bybutter.showcase.v1;
option java_package = "com.bybutter.showcase.v1";
option java_multiple_files = true;
For newly created proto files, it is always encouraged to use the proto3
syntax and configure the package name of the
resulting Kotlin file.
Resource-oriented design
AIP-121 introduced the concept of Resource Oriented Design, a pattern for RPC APIs that provides a more prescriptive and schematic approach to designing APIs.
Design almost all APIs as different actions against resources, and most of the actions can be described using HTTP methods, which is almost the same concept as Restful API.
Here we define a resource named Book
, which represents information about a book.
message Book {
// 标题
string title = 1;
// 作者
string author = 2;
}
Resource Name
AIP-122 defines a resource name specification that uniquely identifies a resource, which contains not only the resource ID.
It also contains information about the hierarchy between resources, which is useful for designing complex APIs.
message Book {
option (google.api.resource) = {
type: "showcase.bybutter.com/Book"
pattern: "publishers/{publisher}/books/{book}"
};
string name = 1;
// 标题
string title = 2;
// 作者
string author = 3;
}
A message can be defined as a resource using the resource
option, where we define Book resource name in the
format publishers/{publisher}/books/{book}
, where publisher
is the publisher ID and book
is the book ID.
Resource Type
AIP-123 defines the specification of the resource type, which consists of the domain in which the resource is located and the resource type.
showcase.bybutter.com/Book
is the resource type of Book
, showcase.bybutter.com
indicates the domain to which the
resource belongs, typically the domain name of the API server, and Book is the resource type, usually the message name.
Resource Reference
We can associate different resources with each other, and the resource_reference
option can do that.
message Book {
// 作者
string author = 3 [(google.api.resource_reference) = {
type: "showcase.bybutter.com/Author"
}];
}
API Deigning
AIP-131, AIP-132, AIP-133 , AIP-134, AIP-135 defines five standard interfaces on resources, which are Get, List, Create, Update, Delete.
This is what we often call the add, delete, and check interface, these interfaces have their own AIP specification, reference to these specifications can be designed to robust and expandable API.
Get Method
The Get method is used to get detailed information about a single resource by its name, which is often placed in the URI path in HTTP mappings.
service BookApi {
rpc GetBook (GetBookRequest) returns (Book) {
option (google.api.http) = {
get: "/v1/{name=publishers/*/books/*}"
};
option (google.api.method_signature) = "name";
}
}
message GetBookRequest {
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
type: "library.googleapis.com/Book"
}];
}
List Method
The List method is used to list resources, often with the parent resource name of the resource in the URI path in the
HTTP mapping, and with page_token
and page_size
for pagination.
Additional filter
(AIP-160) and order_by
resources can be designed to filter resources
and set rules for sorting resources in the list.
service BookApi {
rpc ListBooks (ListBooksRequest) returns (ListBooksResponse) {
option (google.api.http) = {
get: "/v1/{parent=publishers/*}/books"
};
option (google.api.method_signature) = "parent";
}
}
message ListBooksRequest {
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
child_type: "library.googleapis.com/Book"
}];
int32 page_size = 2;
// The page token obtained from the response to the previous ListBooks request, or from the first page if not specified.
string page_token = 3;
}
message ListBooksResponse {
repeated Book books = 1;
// The next page token, or null if there is no more data.
string next_page_token = 2;
}
Create Method
The Create method is used to create a resource, often putting the name of the resource it belongs to in the URI path in the HTTP mapping, and putting information about the resource itself in the request body, returning the created resource itself.
service BookApi {
rpc CreateBook (CreateBookRequest) returns (Book) {
option (google.api.http) = {
post: "/v1/{parent=publishers/*}/books"
body: "book"
};
option (google.api.method_signature) = "parent,book";
}
}
message CreateBookRequest {
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
child_type: "library.googleapis.com/Book"
}];
// The book to create.
Book book = 2 [(google.api.field_behavior) = REQUIRED];
}
Update Method
The Update method is used to update a resource, often placing the resource name in the URI path in the HTTP transcoding and placing information about the resource itself in the request body, returning the updated resource itself.
The Update method will also contain an update_mask
field to indicate which resources' fields need to be updated.
service BookApi {
rpc UpdateBook (UpdateBookRequest) returns (Book) {
option (google.api.http) = {
patch: "/v1/{book.name=publishers/*/books/*}"
body: "book"
};
option (google.api.method_signature) = "book,update_mask";
}
}
message UpdateBookRequest {
// The book to update.
//
// The book's `name` field is used to identify the book to update.
// Format: publishers/{publisher}/books/{book}
Book book = 1 [(google.api.field_behavior) = REQUIRED];
// The list of fields to update.
google.protobuf.FieldMask update_mask = 2;
}
Delete Method
The Delete method is used to delete a resource, and in HTTP transcoding often puts the resource name into the URI path, returning the empty object, and in a soft deleting scenario (AIP-164) may return the resource itself.
service BookApi {
rpc DeleteBook (DeleteBookRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
delete: "/v1/{name=publishers/*/books/*}"
};
option (google.api.method_signature) = "name";
}
}
message DeleteBookRequest {
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
type: "library.googleapis.com/Book"
}];
}
Custom Method
There are also many actions that cannot be simply implemented by the above 5 methods, in which case you can refer to AIP-136 for the implementation of the custom methods.
For batch request scenarios, you can also refer to AIP-231 , AIP-233 , AIP-234, AIP-235 to design the batch processing method.