Micronaut API Gateway using HttpFilter and Consul Service Discovery

Piotr Macha
Owlsy.pl
Published in
3 min readFeb 18, 2021

Micronaut is a great JVM framework for creating lightweight microservices but it doesn’t provide any library for API Gateway like Spring Cloud Gateway. The simplest approach to make such Gateway would be using Micronaut’s HTTP Client for calling services form our gateway project but then we would have to define each endpoint manually and update them later in two places.

Better solution is to use some Micronaut’s tools to create automatic API Gateway that will resolve target service and forward the request as it is. We will use HttpFilter to create global HTTP interceptor and Consul as a Service Discovery.

Consul and Micronaut

To register our services in Consul instance we must install discovery client dependency:

io.micronaut.discovery:micronaut-discovery-client

Then we can create src/main/resources/bootstrap.yml and put Consul configuration there:

micronaut:
application:
name: gateway
config-client:
enabled: true
consul:
client:
defaultZone: ${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}

Each service should also enable Consul registration in src/main/resources/application.yml:

consul.client.registration.enabled: true

I assume that you have a running Consul server already but if not, please follow the official guide https://learn.hashicorp.com/tutorials/consul/get-started-agent. Default Consul settings in development mode are enough to connect our microservices.

API Gateway Specification

Diagram of our microservices

Before we can start development we should write down the rules for API Gateway to follow. In my case the Gateway will follow these steps:

  1. Receive HTTP request with path /{service-id}/{path…}
  2. Resolve correct ServiceInstance using {service-id} parameter
  3. Transform the original request to remove {service-id} and keep it as /{path…}
  4. Proxy the transformed request to the resolved ServiceInstance

Creating the LoadBalancers for our services

To get ServiceInstances we must create a LoadBalancer that will use our DiscoveryClient. I decided to keep all available service IDs as configuration properties so we can create an object for them.

We should also remember to include the service IDs in application properties.

With this next step is to create a Bean factory for our LoadBalancers. I’m creating a map with service-id as a key and a LoadBalancer created by DiscoveryClientLoadBalancerFactory as the map value.

We are also using Collections.unmodifiableMap function to make it immutable.

Creating the global HttpFilter

The last thing to do is our global filter that will intercept each request made to the gateway. We create it by extending OncePerRequestHttpServerFilter class and annotating it with @Filter("/**") .

We also inject our LoadBalancer map and ProxyHttpClient that will take care of passing the request forward. All the gateway magic is executed by doFilterOnce().

We extract the service-id from request, lookup for a matching LoadBalancer and if we found one then we can query it for available ServiceInstance. With the ServiceInstant we can mutate original request to include new host and port and give it to ProxyHttpClient that will send the final request to target service.

And that’s all. We’ve made a fully functional API Gateway that will forward requests to our microservices. Thank you for reading and I hope this post have helped you create your own API Gateway using Micronaut.

If you would like to make the Gateway even more autonomous you could actually use ConsulClient directly and query Consul for all available services. Then you could remove gateway.services[] from application.yml and instead create the LoadBalancers dynamically.

--

--