Working with gRPC-Gateway
The Extend Override and Events Handler are 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.
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?
- Versatility: Enables your service to be accessed both via HTTP/REST and gRPC.
- Type Safety: Leverages gRPC's strong typing. While also generate OpenAPI spec.
- Efficiency: Takes advantage of HTTP/2 for better performance.
Prerequisites
This guide assumes to be used for:
- Go Extend Extension Service sample app.
- C# Extend Extension Service sample app.
- Java Extend Extension Service sample app.
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
google/api/annotations.proto
: Required for Google's HTTP annotations, such asgoogle.api.http
.protoc-gen-openapiv2/options/annotations.proto
: Required for OpenAPI annotations.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
}info
: Sets the title and version of the API.schemes
: Specifies the supported schemes.base_path
: Sets the base path for all API endpoints.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, DELETEpermission.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 ingoogle.api.http
remain unchanged. If you're using base_path, you'll need to manually adjust theBasePath
in the custom service.- Go
- C#
- Java
For more details, please refer to the Change API base path section in the
README.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.
For more details, please refer to the Change API base path section in the
README.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.
For more details, please refer to the Change API base path section in the
README.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
- Go
- C#
- Java
Run this command to generate the code from the proto file
make proto
Build .NET project to update the generated code.
make build
And then, run this command to generate the grpc-gateway code from the proto file
make gen-gateway
Build Java project to update the generated code.
make build
And then, run this command to generate the grpc-gateway code from the proto file:
make gen-gateway
Integrate the generated stub
- Go
- C#
- Java
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
}
To set up the guild service, create a class derived from GuildService.GuildServiceBase
. This class will act as the service implementation.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Grpc.Core;
using AccelByte.Custom.Guild;
namespace AccelByte.PluginArch.ServiceExtension.Demo.Server.Services
{
public class SampleGuildService : GuildService.GuildServiceBase
{
public SampleGuildService()
{
}
//implement your service logic in here
}
}
To implement the CreateOrUpdateGuildProgress
function, you can override the method like this:
public override Task<CreateOrUpdateGuildProgressResponse> CreateOrUpdateGuildProgress(CreateOrUpdateGuildProgressRequest request, ServerCallContext context)
{
// Implementation goes here
}
And similarly for the `GetGuildProgress`` function:
public override Task<GetGuildProgressResponse> GetGuildProgress(GetGuildProgressRequest request, ServerCallContext context)
{
// Implementation goes here
}
To set up our guild service, we'll first create a class derived from GuildServiceGrpc.GuildServiceImplBase
. This class will act as our service implementation.
import lombok.extern.slf4j.Slf4j;
import net.accelbyte.custom.guild.*;
import net.accelbyte.sdk.core.AccelByteSDK;
import org.lognet.springboot.grpc.GRpcService;
@GRpcService
@Slf4j
public class GuildService extends GuildServiceGrpc.GuildServiceImplBase {
// Implementation goes here
}
To implement the CreateOrUpdateGuildProgress
function, you can override the method like this:
public void createOrUpdateGuildProgress(
CreateOrUpdateGuildProgressRequest request, StreamObserver<CreateOrUpdateGuildProgressResponse> responseObserver
) {
// Implementation goes here
}
And similarly for the `GetGuildProgress`` function:
public void getGuildProgress(
GetGuildProgressRequest request, StreamObserver<GetGuildProgressResponse> responseObserver
) {
// Implementation goes here
}