Enterprise Java

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

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.

Eleftheria Drosopoulou

Eleftheria is an Experienced Business Analyst with a robust background in the computer software industry. Proficient in Computer Software Training, Digital Marketing, HTML Scripting, and Microsoft Office, they bring a wealth of technical skills to the table. Additionally, she has a love for writing articles on various tech subjects, showcasing a talent for translating complex concepts into accessible content.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button