Working with gRPC-Gateway
Extend is in Open Beta for AGS Premium Clients! This means that the Extend add-on is available for you to try in your development environment. You can submit your feedback via our Extend Open Beta feedback form.
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.
- Python 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 Service {
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;
}
}
// OpenAPI options for the entire API.
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
info: {
title: "Service API";
version: "1.0";
};
schemes: HTTP;
schemes: HTTPS;
base_path: "/service";
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 Service {
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: "Service API";
version: "1.0";
};
// The rest of configuration
}info
: Sets the title and version of the API.schemes
: Specifies the supported schemes.security_definitions
: Configures the security scheme, which is the 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.
Generate the code from the proto file
- Go
- C#
- Java
- Python
Run this command to generate the code from the proto file:
make proto # Generate protobuf code, gateway code, and swagger JSON
Run this command to generate the code from the proto file:
make proto # Generate gateway code and swagger JSON
make build # Some protobuf code is generated on-the-fly during build
Run this command to generate the code from the proto file:
make proto # Generate gateway code, and swagger JSON
make build # Some protobuf code is generated on-the-fly during build
Run this command to generate the code from the proto file:
make proto # Generate protobuf code, gateway code, and swagger JSON
Integrate the generated stub
- Go
- C#
- Java
- Python
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 MyServiceServerImpl struct {
pb.UnimplementedGuildServiceServer
// Other fields
}
func (g MyServiceServerImpl) CreateOrUpdateGuildProgress(
ctx context.Context, req *pb.CreateOrUpdateGuildProgressRequest,
) (*pb.CreateOrUpdateGuildProgressResponse, error) {
// Your implementation
}
To set up the guild service, create a class derived from Service.ServiceBase
. This class will act as the service implementation.
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Grpc.Core;
using AccelByte.Sdk.Api;
using AccelByte.Extend.ServiceExtension.Server.Model;
namespace AccelByte.Extend.ServiceExtension.Server.Services
{
public class MyService : Service.ServiceBase
{
public MyService()
{
}
// 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 the guild service, create a class derived from ServiceGrpc.ServiceImplBase
. This class will act as the service implementation.
import lombok.extern.slf4j.Slf4j;
import net.accelbyte.extend.serviceextension.*;
import net.accelbyte.sdk.core.AccelByteSDK;
import org.lognet.springboot.grpc.GRpcService;
@GRpcService
@Slf4j
public class MyService extends ServiceGrpc.ServiceImplBase {
// Implementation goes here
}
To implement the CreateOrUpdateGuildProgress
function, you can override the method as follows:
public void createOrUpdateGuildProgress(
CreateOrUpdateGuildProgressRequest request, StreamObserver<CreateOrUpdateGuildProgressResponse> responseObserver
) {
// Implementation goes here
}
You can do the same for the `GetGuildProgress`` function.
public void getGuildProgress(
GetGuildProgressRequest request, StreamObserver<GetGuildProgressResponse> responseObserver
) {
// Implementation goes here
}
To set up our guild service, we'll first create a class derived from ServiceServicer
.
This class will act as our service implementation.
from ..proto.service_pb2 import (
CreateOrUpdateGuildProgressRequest,
CreateOrUpdateGuildProgressResponse,
GetGuildProgressRequest,
GetGuildProgressResponse,
DESCRIPTOR,
)
from ..proto.service_pb2_grpc import ServiceServicer
class AsyncService(ServiceServicer):
full_name: str = DESCRIPTOR.services_by_name["Service"].full_name
# Implement your service logic in here.
To implement the CreateOrUpdateGuildProgress
function, you can override the method like this:
async def CreateOrUpdateGuildProgress(
self, request: CreateOrUpdateGuildProgressRequest, context: Any
) -> CreateOrUpdateGuildProgressResponse:
...
And similarly for the GetGuildProgress
function:
async def GetGuildProgress(
self, request: GetGuildProgressRequest, context: Any
) -> GetGuildProgressResponse:
...