How to Use gRPC and REST Together in Spring Boot

Microservices architecture often requires flexibility to cater to both internal and external client needs. While gRPC offers unparalleled advantages for fast, efficient communication between microservices, REST remains a versatile option with near-universal compatibility for external systems and third-party integrations. The good news? You don’t have to choose one over the other. By combining gRPC and REST in a Spring Boot application, you can leverage the strengths of both protocols.

This guide will explore how to use gRPC and REST together in Spring Boot, covering topics such as running APIs on different ports, sharing business logic, and seamless integration techniques. By the end of this article, you’ll have a clear understanding of how to get the best out of both worlds.

Table of Contents

  1. When You Need Both gRPC and REST APIs
  2. Running gRPC and REST on Different Ports
  3. Sharing Business Logic Between Layers
  4. Converting Protobuf Messages to DTOs
  5. Using gRPC for Internal and REST for External APIs
  6. Implementing a REST Controller Calling a gRPC Client
  7. Creating a Dockerfile for Dual Protocol Support
  8. Kubernetes YAML Example for gRPC and REST
  9. Integrating Swagger/OpenAPI with gRPC Reflection
  10. FAQs
  11. Summary

When You Need Both gRPC and REST APIs

There are specific scenarios where using both gRPC and REST together is beneficial:

  1. Internal vs. External Communication
    • gRPC is optimized for high-speed, low-latency communication between microservices.
    • REST excels in providing browser compatibility and simplified connectivity for external users or third-party systems.
  1. Backward Compatibility

Enterprises transitioning to gRPC can retain their existing REST endpoints for legacy clients while leveraging gRPC for new microservices.

  1. Client Constraints

gRPC is not natively supported in browsers, making REST essential for public-facing web applications unless you use grpc-web.

By combining the two protocols, you adopt a hybrid approach that balances efficiency with accessibility.

Learn more about gRPC vs REST


Running gRPC and REST on Different Ports

Spring Boot makes it easy to configure both gRPC and REST APIs to run on separate ports to avoid conflicts.

Configuration Example:

  1. Define your REST port in application.yml:
   server:
     port: 8080
  1. Configure your gRPC port using a library like grpc-spring-boot-starter:
   grpc:
     server:
       port: 9090

Running gRPC and REST on different ports alongside a single business application architecture ensures operational independence without affecting performance.

See grpc-spring-boot-starter documentation


Sharing Business Logic Between Layers

Instead of duplicating functionality across gRPC and REST implementations, centralize your business logic into reusable service layers.

Example:

Create a shared service:

   @Service
   public class UserService {
       public User getUserById(String id) {
           // Business logic to retrieve user
           return new User(id, "John Doe", "[email protected]");
       }
   }

Use this service in both your REST and gRPC controllers:

   @RestController
   public class UserRestController {
       @Autowired
       private UserService userService;

       @GetMapping("/api/users/{id}")
       public User getUser(@PathVariable String id) {
           return userService.getUserById(id);
       }
   }

   @GrpcService
   public class UserGrpcService extends UserServiceGrpc.UserServiceImplBase {
       @Override
       public void getUser(UserRequest request, StreamObserver<UserResponse> responseObserver) {
           User user = userService.getUserById(request.getId());
           UserResponse response = UserResponse.newBuilder()
               .setId(user.getId())
               .setName(user.getName())
               .setEmail(user.getEmail())
               .build();
           responseObserver.onNext(response);
           responseObserver.onCompleted();
       }
   }

This approach minimizes code duplication and ensures consistent behavior across both protocols.


Converting Protobuf Messages to DTOs

Protobuf messages generated by gRPC’s .proto files often need to be converted into DTOs (Data Transfer Objects) used by REST APIs.

Example Converter:

   public class UserConverter {
       public static User convertProtobufToDTO(UserResponse protobuf) {
           return new User(protobuf.getId(), protobuf.getName(), protobuf.getEmail());
       }

       public static UserResponse convertDTOToProtobuf(User dto) {
           return UserResponse.newBuilder()
               .setId(dto.getId())
               .setName(dto.getName())
               .setEmail(dto.getEmail())
               .build();
       }
   }

Maintain a clear separation between gRPC and REST layers by channeling conversions through such utility classes.

Learn about Protobuf’s Java classes


Using gRPC for Internal and REST for External APIs

A common strategy is to use gRPC for internal, microservice-to-microservice communication and REST for external, third-party communication.

  • gRPC Internal APIs: High-performance, compact, and highly efficient for service-to-service interactions.
  • REST External APIs: Ensures compatibility across external clients and integration flexibility.

This setup aligns with microservices’ goal of scalability while maintaining accessibility.

Read about when to choose gRPC


Implementing a REST Controller Calling a gRPC Client

You can implement a REST API that delegates tasks to a gRPC client for functionality.

Example REST Controller:

   @RestController
   public class CombinedController {
       private final UserServiceGrpc.UserServiceBlockingStub userServiceStub;

       @Autowired
       public CombinedController(ManagedChannel managedChannel) {
           this.userServiceStub = UserServiceGrpc.newBlockingStub(managedChannel);
       }

       @GetMapping("/api/users/{id}")
       public User getUser(@PathVariable String id) {
           UserRequest request = UserRequest.newBuilder().setId(id).build();
           UserResponse response = userServiceStub.getUser(request);
           return new User(response.getId(), response.getName(), response.getEmail());
       }
   }

This hybrid approach bridges the gap between protocols seamlessly.

Example of combining REST and gRPC


Creating a Dockerfile for Dual Protocol Support

Build and containerize your application with support for both protocols.

Dockerfile Example:

   FROM openjdk:17-jdk-alpine
   COPY target/spring-app.jar app.jar
   EXPOSE 8080 9090
   ENTRYPOINT ["java", "-jar", "app.jar"]

This configuration exposes both gRPC (port 9090) and REST (port 8080) services.

Read Docker best practices


Kubernetes YAML Example for gRPC and REST

Deploy your application with Kubernetes by exposing both endpoints through a single deployment.

Example YAML:

   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: app-deployment
   spec:
     replicas: 2
     selector:
       matchLabels:
         app: spring-app
     template:
       metadata:
         labels:
           app: spring-app
       spec:
         containers:
         - name: spring-app
           image: spring-app-image
           ports:
           - containerPort: 8080 # REST
           - containerPort: 9090 # gRPC

Learn Kubernetes deployment basics


Integrating Swagger/OpenAPI with gRPC Reflection

Rest APIs can use Swagger/OpenAPI for documentation, and gRPC can provide reflection for introspection tools.

  1. Enable Swagger for REST:
    • Add springdoc-openapi-ui dependency.
    • Access REST API docs at /swagger-ui.html.
  1. Use gRPC Reflection:
    • Add grpc-java reflection library.
    • Enable reflection service to support gRPC clients like grpcurl.

Swagger Integration | gRPC Reflection


FAQs

Why combine gRPC and REST?

It allows you to leverage the performance of gRPC while keeping REST for compatibility with external systems.

How do I test gRPC and REST together?

Use tools like Postman for REST APIs and grpcurl for gRPC services.

Can I run gRPC and REST on the same port?

No, they require separate configurations and ports due to protocol differences.

What are the deployment challenges?

Ensure proper firewall configurations to expose both gRPC and REST ports.


Summary

Using gRPC and REST together in Spring Boot unlocks a balanced approach to high-performance internal communication and universally compatible external APIs. By running APIs on separate ports, sharing business logic, leveraging Protobuf-based DTOs, and integrating tools like Docker and Kubernetes, you can build an architecture optimized for flexibility and scalability. Use this guide as your blueprint to implement both protocols cohesively, and feel free to explore the external documentation referenced here for deeper insights!

Similar Posts