Building Reactive REST APIs with Spring WebFlux and Protobuf Payloads
Spring WebFlux is a powerful framework for building reactive, non-blocking APIs on the JVM. While JSON is the default payload format for REST APIs, Protocol Buffers (Protobuf) provide a highly efficient, compact binary serialization format ideal for performance-critical applications.
In this guide, you’ll learn how to send and receive Protobuf-encoded data over HTTP using Spring WebFlux, complete with example code and links to relevant resources.
Why Use Protobuf Instead of JSON?
Before we dive into implementation, let’s briefly look at why you’d choose Protobuf:
✅ Smaller Payload Size – Data is serialized in a compact binary format, reducing network bandwidth.
✅ Faster Serialization – Encoding and decoding are typically faster than JSON parsers.
✅ Strongly Typed Schema – The .proto files define your data models with clear contracts.
If you’re building APIs in performance-sensitive environments (e.g., IoT, mobile backends), Protobuf is an excellent choice.
Learn more: Protocol Buffers Overview
Setting Up the Project
You’ll need:
- Java 17+
- Maven or Gradle
- Spring Boot 3.x
Dependencies:
For Maven, add to your pom.xml:
<dependencies>
<!-- Spring WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Protobuf support -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.25.1</version>
</dependency>
</dependencies>
If you use Gradle, add to build.gradle.kts:
dependencies {
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("com.google.protobuf:protobuf-java:3.25.1")
}
Also configure the protobuf plugin to generate Java classes from .proto files.
Reference: Protobuf Maven Plugin
Defining Your Protobuf Message
Create a .proto file describing your model:
user.proto:
syntax = "proto3";
option java_package = "com.example.protobuf";
option java_outer_classname = "UserProto";
message User {
int64 id = 1;
string name = 2;
string email = 3;
}
Run the Protobuf compiler to generate the Java classes:
protoc --java_out=src/main/java user.proto
This generates UserProto.java containing the User class.
Creating a Reactive Controller
Next, let’s set up a Spring WebFlux REST controller that:
- Receives Protobuf-encoded requests
- Returns Protobuf-encoded responses
Here’s a simple example:
package com.example.demo;
import com.example.protobuf.UserProto;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping(
consumes = "application/x-protobuf",
produces = "application/x-protobuf"
)
public Mono<UserProto.User> createUser(@RequestBody Mono<UserProto.User> userMono) {
return userMono.map(user -> {
// Simulate storing the user and assigning an ID
return user.toBuilder()
.setId(System.currentTimeMillis())
.build();
});
}
@GetMapping(
value = "/{id}",
produces = "application/x-protobuf"
)
public Mono<UserProto.User> getUser(@PathVariable long id) {
UserProto.User user = UserProto.User.newBuilder()
.setId(id)
.setName("John Doe")
.setEmail("john.doe@example.com")
.build();
return Mono.just(user);
}
}
Key Points:
- Consumes/Produces
application/x-protobuf: This tells Spring to use Protobuf serialization. - Mono<User>: Fully reactive, non-blocking pipeline.
Configuring Protobuf HttpMessageReader/Writer
Spring Boot auto-configures message converters for Protobuf if it finds the Protobuf library in the classpath. But to be explicit, you can register it yourself:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.codec.protobuf.ProtobufDecoder;
import org.springframework.http.codec.protobuf.ProtobufEncoder;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.config.DelegatingWebFluxConfiguration;
@Configuration
public class WebFluxConfig extends DelegatingWebFluxConfiguration {
@Bean
public ProtobufDecoder protobufDecoder() {
return new ProtobufDecoder();
}
@Bean
public ProtobufEncoder protobufEncoder() {
return new ProtobufEncoder();
}
}
Testing Your API
You can test the endpoints with curl or tools like Postman, but remember:
✅ You must set the Content-Type and Accept headers to application/x-protobuf.
✅ You must send raw binary data, not JSON.
Example curl request:
First, generate a Protobuf binary file with protoc:
protoc --encode=com.example.protobuf.User < user.proto > user.bin
Then POST the binary:
curl -X POST \
-H "Content-Type: application/x-protobuf" \
-H "Accept: application/x-protobuf" \
--data-binary @user.bin \
http://localhost:8080/api/users
Example Client Using WebClient
If you prefer Java clients, use WebClient:
WebClient client = WebClient.builder()
.baseUrl("http://localhost:8080")
.build();
Mono<UserProto.User> response = client.post()
.uri("/api/users")
.contentType(MediaType.APPLICATION_X_PROTOBUF)
.accept(MediaType.APPLICATION_X_PROTOBUF)
.bodyValue(user)
.retrieve()
.bodyToMono(UserProto.User.class);
response.subscribe(createdUser -> {
System.out.println("Created User: " + createdUser);
});
Best Practices
✅ Version Your Protobuf Schemas
Use reserved fields to avoid compatibility issues.
Read: Protobuf Versioning
✅ Document Your API Clearly
Since Protobuf payloads are binary, developers need your .proto definitions to consume the API.
✅ Use Content Negotiation
Allow clients to request JSON if needed (you can support both formats via produces and consumes).
Related Resources
- Spring WebFlux Documentation
- Protocol Buffers Official Site
- Spring Web Reactive Protobuf Support
- Protobuf Gradle Plugin
Conclusion
With Spring WebFlux and Protobuf, you can build high-performance, reactive APIs that exchange compact binary data over HTTP. This setup is well-suited for microservices or low-latency systems where payload size and speed matter.




