Create JSON Web APIs from gRPC

By James Newton-King

Important

gRPC HTTP API is an experimental project, not a committed product. We want to:

  • Test that our approach to creating JSON Web APIs for gRPC services works.
  • Get feedback on if this approach is useful to .NET developers.

Please leave feedback to ensure we build something that developers like and are productive with.

gRPC is a modern way to communicate between apps. gRPC uses HTTP/2, streaming, Protobuf and message contracts to create high-performance, real-time services.

One limitation with gRPC is not every platform can use it. Browsers don't fully support HTTP/2, making REST and JSON the primary way to get data into browser apps. Even with the benefits that gRPC brings, REST and JSON have an important place in modern apps. Building gRPC and JSON Web APIs adds unwanted overhead to app development.

This document discusses how to create JSON Web APIs using gRPC services.

gRPC HTTP API

gRPC HTTP API is an experimental extension for ASP.NET Core that creates RESTful JSON APIs for gRPC services. Once configured, gRPC HTTP API allows apps to call gRPC services with familiar HTTP concepts:

  • HTTP verbs
  • URL parameter binding
  • JSON requests/responses

gRPC can still be used to call services.

Usage

  1. Add a package reference to Microsoft.AspNetCore.Grpc.HttpApi.
  2. Register services in Startup.cs with AddGrpcHttpApi.
  3. Add google/api/http.proto and google/api/annotations.proto files to your project.
  4. Annotate gRPC methods in your .proto files with HTTP bindings and routes:
syntax = "proto3";

import "google/api/annotations.proto";

package greet;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/greeter/{name}"
    };
  }
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

The SayHello gRPC method can now be invoked as gRPC+Protobuf and as an HTTP API:

  • Request: HTTP/1.1 GET /v1/greeter/world
  • Response: { "message": "Hello world" }

Server logs show that the HTTP call is executed by a gRPC service. gRPC HTTP API maps the incoming HTTP request to a gRPC message, and then converts the response message to JSON.

info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET https://localhost:5001/v1/greeter/world
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint 'gRPC - /v1/greeter/{name}'
info: Server.GreeterService[0]
      Sending hello to world
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint 'gRPC - /v1/greeter/{name}'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 1.996ms 200 application/json

This is a basic example. See HttpRule for more customization options.

Enable Swagger/OpenAPI support

Swagger (OpenAPI) is a language-agnostic specification for describing REST APIs. gRPC HTTP API can integrate with Swashbuckle to generate a Swagger endpoint for RESTful gRPC services. The Swagger endpoint can then be used with Swagger UI and other tooling.

To enable Swagger with gRPC HTTP API:

  1. Add a package reference to Microsoft.AspNetCore.Grpc.Swagger.
  2. Configure Swashbuckle in Startup.cs. The AddGrpcSwagger method configures Swashbuckle to include gRPC HTTP API endpoints.
public void ConfigureServices(IServiceCollection services)
{
    services.AddGrpc();
    services.AddGrpcHttpApi();
    
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
    });
    services.AddGrpcSwagger();
}

public void Configure(IApplicationBuilder app)
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
    });

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<GreeterService>();
    });
}

To confirm that Swashbuckle is generating Swagger for the RESTful gRPC services, start the app and navigate to the Swagger UI page:

Swagger UI

gRPC HTTP API vs gRPC-Web

Both gRPC HTTP API and gRPC-Web allow gRPC services to be called from a browser. However, the way each does this is different:

  • gRPC-Web lets browser apps call gRPC services from the browser with the gRPC-Web client and Protobuf. gRPC-Web requires the browser app generate a gRPC client, and has the advantage of sending small, fast Protobuf messages.
  • gRPC HTTP API allows browser apps to call gRPC services as if they were RESTful APIs with JSON. The browser app doesn't need to generate a gRPC client or know anything about gRPC.

No generated client is created for gRPC HTTP API. The previous Greeter service can be called using browser JavaScript APIs:

var name = nameInput.value;

fetch("/v1/greeter/" + name).then(function (response) {
  response.json().then(function (data) {
    console.log("Result: " + data.message);
  });
});

Experimental status

gRPC HTTP API is an experiment. It is not complete and it is not supported. We're interested in this technology, and the ability it gives app developers to quickly create gRPC and JSON services at the same time. There is no commitment to completing the gRPC HTTP API.

We want to gauge developer interest in gRPC HTTP API. If gRPC HTTP API is interesting to you then please give feedback.

grpc-gateway

grpc-gateway is another technology for creating RESTful JSON APIs from gRPC services. It uses the same .proto annotations to map HTTP concepts to gRPC services.

The biggest difference between grpc-gateway and gRPC HTTP API is grpc-gateway uses code generation to create a reverse-proxy server. The reverse-proxy translates RESTful calls into gRPC and then sends them on to the gRPC service.

For installation and usage of grpc-gateway, see the grpc-gateway README.

Additional resources