Skip to main content

Working with gRPC-Gateway

Last updated on December 18, 2023
note

Extend is in Open Beta for AGS Premium Clients! This means that Extend is available for you to try on your development environment. You can submit your feedback here.

note

Extend is currently available for AGS Starter Closed Beta partners only.

Overview

This guide is designed to provide in-depth coverage on the implementation of gRPC-Gateway. The primary focus is on providing insights into what gRPC-Gateway is, its core components, and its workflow within a service.

Why gRPC-Gateway?

  1. Versatility: Enables your service to be accessed both via HTTP/REST and gRPC.
  2. Type Safety: Leverages gRPC's strong typing. While also generate OpenAPI spec.
  3. Efficiency: Takes advantage of HTTP/2 for better performance.

Prerequisites

This guide assumes to be used for:

Workflow with sample app

Write the API definition in .proto file

import "google/api/annotations.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "permission.proto";


service GuildService {

rpc CreateOrUpdateGuildProgress (CreateOrUpdateGuildProgressRequest) returns (CreateOrUpdateGuildProgressResponse) {
option (permission.action) = CREATE;
option (permission.resource) = "ADMIN:NAMESPACE:{namespace}:CLOUDSAVE:RECORD";
option (google.api.http) = {
post: "/v1/admin/namespace/{namespace}/progress"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Update Guild progression"
description: "Update Guild progression if not existed yet will create a new one"
security: {
security_requirement: {
key: "Bearer"
value: {}
}
}
};
}

message CreateOrUpdateGuildProgressRequest {
string namespace = 1;
GuildProgress guild_progress = 2;
}

message CreateOrUpdateGuildProgressResponse {
GuildProgress guild_progress = 1;
}

}

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
info: {
title: "Guild Service API";
version: "1.0";
};
schemes: HTTP;
schemes: HTTPS;
base_path: "/guild";

security_definitions: {
security: {
key: "Bearer";
value: {
type: TYPE_API_KEY;
in: IN_HEADER;
name: "Authorization";
}
}
};
};

Explanation

  • Import statements

    1. google/api/annotations.proto: Required for Google's HTTP annotations, such as google.api.http.
    2. protoc-gen-openapiv2/options/annotations.proto: Required for OpenAPI annotations.
    3. permission.proto: Custom import, likely used to define scope permission.
  • Service definition

    This part defines a gRPC service with one RPC method named CreateOrUpdateGuildProgress.

    service GuildService {

    rpc CreateOrUpdateGuildProgress (CreateOrUpdateGuildProgressRequest) returns (CreateOrUpdateGuildProgressResponse) {
    // Various annotation here
    }

    message CreateOrUpdateGuildProgressRequest {
    string namespace = 1;
    GuildProgress guild_progress = 2;
    }

    message CreateOrUpdateGuildProgressResponse {
    GuildProgress guild_progress = 1;
    }

    }
  • Swagger configuration

    This section of the .proto file is used to configure the Swagger or OpenAPI output:


    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
    info: {
    title: "Guild Service API";
    version: "1.0";
    };

    // The rest of configuration

    }

    1. info: Sets the title and version of the API.
    2. schemes: Specifies the supported schemes.
    3. base_path: Sets the base path for all API endpoints.
    4. security_definitions: Configures the security scheme (Bearer token in this case).
    • Permission control via permission.proto Annotations for fine-grained access control:

      • permission.action: it can be either READ, CREATE, UPDATE, DELETE
      • permission.resource: Defines scope-based access control (e.g., ADMIN:NAMESPACE:{namespace}:CLOUDSAVE:RECORD).

    These permission annotations allow for more granular control over access to specific parts of your service.

    To learn more about how permission is structured, see Introduction to Authorization.

    Tricky Part: base_path

    If base_path is set, note that it doesn't alter the paths generated in the Swagger file. Your actual API paths in google.api.http remain unchanged. If you're using base_path, you'll need to manually adjust the BasePath in the custom service.

    For more details, please refer to the Change API base path section in theREADME.md file in the custom service repository.

    And for more details about working with guild service proto file, please refer to Creating a New Endpoint readme file in the custom service repository.

Generate the code from the proto file

Run this command to generate the code from the proto file

make proto

Integrate the generated stub

To set up our generate stub into the service, we'll first create a structure that embeds the UnimplementedGuildServiceServer. This stub was named by the protobuf code generator.

import pb "extend-custom-guild-service/pkg/pb"

type GuildServiceServerImpl struct {
pb.UnimplementedGuildServiceServer
// Other fields
}


func (g GuildServiceServerImpl) CreateOrUpdateGuildProgress(
ctx context.Context, req *pb.CreateOrUpdateGuildProgressRequest,
) (*pb.CreateOrUpdateGuildProgressResponse, error) {

// Your implementation

}