Skip to content

Add gRPC-JSON transcoding prototype - #424

Open
AleksanderBrzozowski wants to merge 1 commit into
spring-projects:mainfrom
AleksanderBrzozowski:grpc-json-transcoding
Open

Add gRPC-JSON transcoding prototype#424
AleksanderBrzozowski wants to merge 1 commit into
spring-projects:mainfrom
AleksanderBrzozowski:grpc-json-transcoding

Conversation

@AleksanderBrzozowski

Copy link
Copy Markdown

Summary

Add an experimental spring-grpc-transcoding module that generates Spring MVC
controllers from supported google.api.http annotations and forwards HTTP/JSON
requests through the application's in-process gRPC server.

Resolves #339.

Motivation

Spring gRPC applications currently need to maintain a separate HTTP adapter when a
service must also be available to JSON clients. This prototype demonstrates a small,
strictly validated transcoding path that derives the adapter from the protobuf
contract while preserving the existing gRPC service implementation and server
interceptor chain.

Architecture

  • A JVM protoc plugin in the module reads CodeGeneratorRequest descriptors,
    resolves annotated unary methods, validates the supported HttpRule subset and
    emits one package-private Spring MVC controller per annotated service.
  • Generated controllers build the concrete protobuf request, create the service's
    blocking stub and call it over a named in-process channel.
  • GrpcTranscodingConfiguration is an explicit opt-in configuration that provides
    the channel and ProtobufJsonFormatHttpMessageConverter. The channel is created
    through GrpcChannelFactory, includes global client interceptors, uses the direct
    executor and remains lifecycle-owned by the factory.
  • Unsupported annotated contracts fail protobuf generation with file, service and
    method context; they are not silently skipped or reinterpreted.

Supported subset

  • Unary RPCs.
  • GET, POST, PUT and DELETE.
  • java_multiple_files = true.
  • Simple top-level singular protobuf string path variables.
  • Top-level singular protobuf string query fields when there is no request body.
  • No body, or body: "*" decoded as the concrete protobuf request.
  • Path values applied after body decoding, so the path wins on conflicts.
  • The complete protobuf response serialized as ProtoJSON.
  • Duplicate singular query parameters rejected with HTTP 400.

Deliberate deferrals

PATCH and custom verbs, named bodies, nested or constrained path templates,
additional_bindings, response_body, streaming RPCs, non-string or repeated
path/query fields, HttpBody, HTTP/gRPC metadata propagation and a library-defined
HTTP error response contract are outside this initial prototype.

Setup

Add spring-grpc-transcoding to the application and protoc plugin classpaths,
register its JAR as a jvm-maven protobuf plugin, import
GrpcTranscodingConfiguration, and configure the same in-process server name on
both sides (default: grpc-transcoding). The reference documentation and sample
contain complete Maven and protobuf examples.

Verification

  • Generated-source shape and contextual rejection tests.
  • Direct javac compilation of generated controllers.
  • Focused runtime configuration tests for channel target, interceptors and executor.
  • End-to-end sample tests for path/query binding, body: "*", path precedence,
    duplicate queries and interceptor execution.
  • Clean module install, clean sample test, documentation package and diff checks.

Generate Spring MVC endpoints from supported google.api.http
annotations and route requests through the in-process gRPC server.

[resolves spring-projects#339]

Signed-off-by: Aleksander Brzozowski <olek.brzozowski@gmail.com>
@dsyer

dsyer commented Aug 19, 2026

Copy link
Copy Markdown
Member

Thanks for doing this. I don't love the code generation approach to be honest. It should be possible to do everything in statically compiled code using functional endpoints.

Also not sure I like the in-process channel, but I understand why you did that. The main thing I dislike about that is the need for a GrpcTranscodingConfiguration, but again I can see why you did it that way. One of the issues that I also debated with myself when I made my prototype, is that I'm not sure all interceptors would apply equally to the grpc service and the JSON service (especially security where Spring Security already supports the HTTP side).

I guess supporting only unary rpc methods was just a proof of concept? There's no technical reason why streaming methods wouldn't work?

@AleksanderBrzozowski

AleksanderBrzozowski commented Aug 19, 2026

Copy link
Copy Markdown
Author

I don't love the code generation approach to be honest. It should be possible to do everything in statically compiled code using functional endpoints.

How do you see this? I thought that codegen allows us to fail compilation whenever some feature that is not supported is being used, or some other error happens that we can detect early (when compared to failing in runtime).

One of the issues that I also debated with myself when I made my prototype, is that I'm not sure all interceptors would apply equally to the grpc service and the JSON service (especially security where Spring Security already supports the HTTP side).

Yep, probably something that can be controlled. If we don't want to use the in-process channel, then what? Find the bean that implements the server and call the method directly?

I guess supporting only unary rpc methods was just a proof of concept? There's no technical reason why streaming methods wouldn't work?

Yep, exactly, I haven't even investigated how to support them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for gRPC-JSON Transcoding

2 participants