Home | Send Feedback | Share on Bluesky |

Integrate gRPC with Spring Boot

Published: 6. March 2026  •  java, spring

In this post, we will explore how to integrate gRPC into a Spring Boot application with the Spring gRPC project.

gRPC is a high-performance, open-source universal RPC framework that can run in any environment. It enables client and server applications to communicate transparently and makes it easier to build connected systems. In this example, both the server and client are implemented in Java, but gRPC supports many languages, so you could have a client in Python, Go, or any other supported language communicating with a Spring Boot gRPC server.

gRPC uses Protocol Buffers (Protobuf) as its interface definition language and as its underlying message interchange format. Protobuf allows you to define your service methods and message types in a .proto file. The protobuf compiler then generates code in your chosen language, which contains the serialization/deserialization logic and the gRPC stubs for client and server.

Protobuf is a binary format, which makes it more compact and faster to serialize/deserialize than text-based formats like JSON or XML. This can lead to improved performance, especially in high-throughput scenarios.

Protobuf definition

For this example, I wrote a simple Protobuf file that models an IoT anomaly detection service.

The gRPC part is defined by the service block, which declares two RPC methods: EvaluateReading (a unary call) and SubscribeAlerts (a server-streaming call).

service IotAnomalyService {
  rpc EvaluateReading (SensorReadingRequest) returns (ReadingAssessment);
  rpc SubscribeAlerts (AlertSubscriptionRequest) returns (stream AnomalyAlert);
}

iot-anomaly.proto

The messages define the structure of the requests and responses for these methods.

message SensorReadingRequest {
  string sensor_id = 1;
  string site_id = 2;
  string metric_type = 3;
  double value = 4;
  double baseline = 5;
  int64 captured_at_epoch_ms = 6;
}

message ReadingAssessment {
  string sensor_id = 1;
  bool anomaly = 2;
  double z_score = 3;
  string severity = 4;
  string summary = 5;
  int64 recommended_check_after_seconds = 6;
}

message AlertSubscriptionRequest {
  string site_id = 1;
  string device_group = 2;
  int32 max_events = 3;
}

message AnomalyAlert {
  string alert_id = 1;
  string sensor_id = 2;
  string site_id = 3;
  string metric_type = 4;
  double observed_value = 5;
  double threshold = 6;
  string severity = 7;
  string message = 8;
  int64 detected_at_epoch_ms = 9;
}

iot-anomaly.proto

This Protobuf file serves as the single source of truth for both the server and client implementations. This is also one of the benefits of using gRPC and Protobuf: you can define your service and message contracts in a language-agnostic way, and then generate code for any supported language from that definition. This helps ensure consistency between the server and client and reduces the likelihood of serialization/deserialization errors due to mismatched data structures.

Dependencies

The recommended approach is to use the Spring gRPC BOM (Bill of Materials) to manage the versions of related dependencies. This ensures that you have compatible versions of Spring gRPC, gRPC, and Protobuf libraries. In a Maven project, you can do this by adding the following to your pom.xml:

  <properties>
    <java.version>25</java.version>
    <grpc.version>1.77.1</grpc.version>
    <protobuf-java.version>4.33.4</protobuf-java.version>
    <spring-grpc.version>1.0.2</spring-grpc.version>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.grpc</groupId>
        <artifactId>spring-grpc-dependencies</artifactId>
        <version>${spring-grpc.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

pom.xml

As a dependency, you only need to add spring-grpc-spring-boot-starter. This brings in the core Spring gRPC integration and transitively includes the necessary gRPC and Protobuf dependencies.

Security is a common concern for gRPC services. The Spring gRPC project provides integration with Spring Security, allowing you to secure your gRPC endpoints using familiar Spring Security mechanisms.

    <dependency>
      <groupId>org.springframework.grpc</groupId>
      <artifactId>spring-grpc-spring-boot-starter</artifactId>
    </dependency>  
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

pom.xml

Code generation

gRPC and Protobuf work with generated code. You write your .proto file, then run the Protobuf compiler (protoc) to generate Java classes for your messages and gRPC stubs for your service.

For this purpose, I added the following plugin to the pom.xml. Make sure to configure the plugin to point to your .proto files and specify the correct versions of the Protobuf compiler and gRPC plugin.

      <plugin>
        <groupId>io.github.ascopes</groupId>
        <artifactId>protobuf-maven-plugin</artifactId>
        <version>5.0.1</version>
        <configuration>
          <protoc>${protobuf-java.version}</protoc>
          <sourceDirectories>
            <sourceDirectory>${project.basedir}/../proto</sourceDirectory>
          </sourceDirectories>
          <plugins>
            <plugin kind="binary-maven">
              <groupId>io.grpc</groupId>
              <artifactId>protoc-gen-grpc-java</artifactId>
              <version>${grpc.version}</version>
            </plugin>
          </plugins>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

pom.xml

You can find more information about the plugin configuration in the official documentation.

Server implementation

To implement the gRPC server, you need to create a class that extends the generated base class for your service. In this case, IotAnomalyServiceImplBase is the generated base class for the IotAnomalyService defined in the .proto file.

Apart from extending the base class, this is a normal Spring service class, so you can inject other Spring beans into it, use Spring's lifecycle annotations, and so on.

@Service
public class IotAnomalyGrpcService extends IotAnomalyServiceGrpc.IotAnomalyServiceImplBase {

IotAnomalyGrpcService.java

The evaluateReading method implements the EvaluateReading RPC defined in the .proto file. It takes a SensorReadingRequest as input and returns a ReadingAssessment. This method is a unary RPC, which means it receives a single request and sends back a single response. The responseObserver is used to send the response to the client with the onNext method, and then onCompleted is called to indicate that the response has been fully sent.

  @Override
  public void evaluateReading(
      SensorReadingRequest request,
      StreamObserver<ReadingAssessment> responseObserver) {

    AnomalyScoringService.ReadingScore score = this.anomalyScoringService.evaluate(
        request.getSensorId(),
        request.getMetricType(),
        request.getValue(),
        request.getBaseline(),
        request.getCapturedAtEpochMs());

    ReadingAssessment assessment = ReadingAssessment.newBuilder()
        .setSensorId(request.getSensorId())
        .setAnomaly(score.anomaly())
        .setZScore(score.zScore())
        .setSeverity(score.severity())
        .setSummary(score.summary())
        .setRecommendedCheckAfterSeconds(score.recommendedCheckAfterSeconds())
        .build();

    log.info("Evaluated reading for sensor={} severity={} anomaly={}",
        request.getSensorId(), assessment.getSeverity(), assessment.getAnomaly());

    responseObserver.onNext(assessment);
    responseObserver.onCompleted();
  }

IotAnomalyGrpcService.java


subscribeAlerts implements the SubscribeAlerts RPC, which is a server-streaming RPC. This means that the client sends a single request, but the server can send back multiple responses over time. The responseObserver is used to send AnomalyAlert messages to the client as they are generated. onNext sends a response, onCompleted is called when the server has finished sending responses, and onError signals an error condition to the client.

  @Override
  public void subscribeAlerts(
      AlertSubscriptionRequest request,
      StreamObserver<AnomalyAlert> responseObserver) {

    int maxEvents = request.getMaxEvents() <= 0 ? 8 : request.getMaxEvents();

    log.info("Starting alert stream: site={} group={} maxEvents={}",
        request.getSiteId(), request.getDeviceGroup(), maxEvents);

    try {
      for (int i = 1; i <= maxEvents; i++) {
        double threshold = 75.0;
        double observed = threshold + ThreadLocalRandom.current().nextDouble(-10.0, 22.0);
        String severity = observed >= 95.0 ? "CRITICAL" : (observed >= 85.0 ? "HIGH" : "MEDIUM");

        AnomalyAlert alert = AnomalyAlert.newBuilder()
            .setAlertId(UUID.randomUUID().toString())
            .setSensorId("sensor-" + String.format(Locale.ROOT, "%03d", i))
            .setSiteId(request.getSiteId())
            .setMetricType("temperature_celsius")
            .setObservedValue(observed)
            .setThreshold(threshold)
            .setSeverity(severity)
            .setMessage(String.format(Locale.ROOT,
                "%s anomaly: %.2f exceeds threshold %.2f at %s",
                severity,
                observed,
                threshold,
                Instant.now()))
            .setDetectedAtEpochMs(System.currentTimeMillis())
            .build();

        responseObserver.onNext(alert);
        Thread.sleep(700L);
      }

      responseObserver.onCompleted();
      log.info("Completed alert stream for site={}", request.getSiteId());
    }
    catch (InterruptedException ex) {
      Thread.currentThread().interrupt();
      responseObserver.onError(ex);
    }
  }

IotAnomalyGrpcService.java


Security

Spring gRPC provides integration with Spring Security, allowing you to secure your gRPC endpoints using familiar Spring Security mechanisms. To keep things simple, this example uses an in-memory user store with a single user and then secures the gRPC endpoints with HTTP Basic authentication.

@Configuration
public class GrpcSecurityConfiguration {

  @Bean
  UserDetailsService userDetailsService() {
    return new InMemoryUserDetailsManager(
        User.withUsername("iot-client")
            .password("{noop}iot-secret")
            .roles("USER")
            .build());
  }

  @Bean
  @GlobalServerInterceptor
  AuthenticationProcessInterceptor grpcAuthenticationInterceptor(GrpcSecurity grpc) throws Exception {
    return grpc
        .authorizeRequests(requests -> requests
            .methods("grpc.*/*").permitAll()
            .allRequests().authenticated())
        .httpBasic(withDefaults())
        .build();
  }
}

GrpcSecurityConfiguration.java

Do not use Basic authentication in production, as it is not secure. For production applications, consider using more robust authentication and authorization mechanisms, such as JWT tokens, OAuth2, or mTLS (mutual TLS) for securing gRPC endpoints.


Configuration

The gRPC server listens on a specific port for incoming requests. In this example, the server is configured to listen on port 9090. You can specify this in the application.yml file.

  grpc:
    server:
      port: 9090

application.yml

You can find a list of all available configuration properties in the official documentation.


That is all you need to set up a basic gRPC server with Spring Boot. Next, let us look at how to implement a gRPC client that can communicate with this server.

Client

On the client side, we do not need to implement any interfaces. We can simply use the generated gRPC stubs to call the server. The ClientGrpcConfig class creates beans for both the blocking and async stubs, which are configured to connect to the gRPC server and include a basic authentication interceptor with credentials.

Channels in gRPC are the abstraction that represents a connection to a gRPC server. They manage the underlying network connections and provide a way for clients to call remote methods on the server. When you create a channel, you specify the target address of the server and any options or interceptors that should be applied to the calls made through that channel.

@Configuration
public class ClientGrpcConfig {

  @Value("${app.grpc.username}")
  private String username;

  @Value("${app.grpc.password}")
  private String password;

  @Bean
  IotAnomalyServiceGrpc.IotAnomalyServiceBlockingStub anomalyBlockingStub(GrpcChannelFactory channels) {
    return IotAnomalyServiceGrpc.newBlockingStub(channels.createChannel("anomaly-server", channelOptions()));
  }

  @Bean
  IotAnomalyServiceGrpc.IotAnomalyServiceStub anomalyAsyncStub(GrpcChannelFactory channels) {
    return IotAnomalyServiceGrpc.newStub(channels.createChannel("anomaly-server", channelOptions()));
  }

  private ChannelBuilderOptions channelOptions() {
    return ChannelBuilderOptions.defaults()
        .withInterceptors(List.of(new BasicAuthenticationInterceptor(this.username, this.password)));
  }
}

ClientGrpcConfig.java

The server address is configured in the application.yml file. Make sure that the code references the same channel name as the one defined in the configuration (anomaly-server in this case).

  grpc:
    client:
      channels:
        anomaly-server:
          address: localhost:9090

application.yml


Calling the server

The application can then use these stubs to call the server. For example, the runUnaryCheck method builds a SensorReadingRequest, applies a deadline of 3 seconds, and calls evaluateReading on the blocking stub.

  private void runUnaryCheck() {
    SensorReadingRequest request = SensorReadingRequest.newBuilder()
        .setSensorId("sensor-441")
        .setSiteId("warehouse-eu-1")
        .setMetricType("temperature_celsius")
        .setValue(91.4)
        .setBaseline(73.0)
        .setCapturedAtEpochMs(System.currentTimeMillis())
        .build();

    ReadingAssessment assessment = this.blockingStub
        .withDeadlineAfter(Duration.ofSeconds(3))
        .evaluateReading(request);

IotAnomalyClientRunner.java


Streaming

The runAlertStream method demonstrates how to call a server-streaming RPC. It builds an AlertSubscriptionRequest and calls subscribeAlerts on the async stub. The method takes the request and a StreamObserver<AnomalyAlert> as parameters. The StreamObserver handles the incoming stream of AnomalyAlert messages from the server. The methods onNext, onError, and onCompleted are called by the gRPC framework as responses are received, when an error occurs, or when the stream completes.

  private void runAlertStream() throws InterruptedException {
    CountDownLatch done = new CountDownLatch(1);

    AlertSubscriptionRequest streamRequest = AlertSubscriptionRequest.newBuilder()
        .setSiteId("warehouse-eu-1")
        .setDeviceGroup("freezers")
        .setMaxEvents(6)
        .build();

    this.asyncStub.subscribeAlerts(streamRequest, new StreamObserver<>() {
      @Override
      public void onNext(AnomalyAlert alert) {
        log.info("Stream alert -> id={} sensor={} severity={} observed={} threshold={} msg={}",
            alert.getAlertId(),
            alert.getSensorId(),
            alert.getSeverity(),
            String.format("%.2f", alert.getObservedValue()),
            String.format("%.2f", alert.getThreshold()),
            alert.getMessage());
      }

      @Override
      public void onError(Throwable throwable) {
        log.error("Alert stream failed", throwable);
        done.countDown();
      }

      @Override
      public void onCompleted() {
        log.info("Alert stream completed");
        done.countDown();
      }
    });

    if (!done.await(20, TimeUnit.SECONDS)) {
      log.warn("Alert stream timeout reached");
    }
  }

IotAnomalyClientRunner.java

Wrapping up

The Spring gRPC project provides a convenient way to build and integrate gRPC services and clients within the Spring ecosystem. It also integrates with Spring Boot and Spring Security so you can secure your gRPC endpoints in a familiar way.

For more information and examples, refer to the official Spring gRPC documentation.