WSO2 API Manager with message routing to different endpoints types

Introduction

The scenario depicted here is for a simple route data to endpoint, based on HTTP method and API URL. I show two aproaches. One simple using only HTTP method – PUT. And second advanced with specify URL routing. Data will be routed to different endpoint. As an example, of endpoint we use RabbitMQ message queue. How to configure endpoint for RabbitMQ I refer to this post[1]. And for more complex scenarios of routing messages I recommended to use WSO2 Enterprise Integrator instead of WSO2 API Manager.

Steps to start

To create simple routing messages in WSO2 API manager we modify in Runtime Configuratons our Message Mediation component.

As schown on image, we go to APIs -> Runtime Configurations -> Message Mediation. Before we can upload our routing mediation, we must create artifact as XML file like below. We called it rabbitmq.router.xml

Simple solution example

<?xml version="1.0" encoding="UTF-8"?><sequence xmlns="http://ws.apache.org/ns/synapse" name="rabbitmq.router" onError="fault">
  <filter source="$axis2:HTTP_METHOD" regex="PUT">
    <then>
      <property name="exchangeName" value="wso2.api" scope="default" type="STRING"/>
      <property name="routingKey" value="data.put" scope="default" type="STRING"/>
      <property name="OUT_ONLY" value="true" scope="default" type="STRING"/>
      <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2" type="STRING"/>
      <header xmlns:ns="http://org.apache.synapse/xsd" name="To" scope="default" expression="fn:concat('rabbitmq:/?rabbitmq.connection.factory=CachedRabbitMQConnectionFactory&rabbitmq.queue.delivery.mode=2&rabbitmq.exchange.name=',$ctx:exchangeName,'&rabbitmq.queue.routing.key=',$ctx:routingKey)"/>
      <send/>  
    </then>
    <else/>
  </filter>
</sequence>

In simple aproach, we use filter mediator based on $axis2:HTTP_METHOD. For PUT method we set exchangeName and routingKey. Based on this values we build header for sending to Rabbit MQ. All other http method will be passed to default endpoint, only PUT will be redirect to Rabbit MQ.

Advence solution example

In more advenced solution, we can additionaly route messages using pattern in our URI. For this example we have want to route to Rabbit MQ endpoints defined as:
/data/sample/{sampleId}/id/{id}/status
/data/complex/{complexId}/guid/{guid}

Using appropriate regex in switch mediator we filter our URI and set diffrent exchangeName and routingKey properties. Moreover we use dynamic URI elements to concatenate exchange name and routing key for more flexible usecases – filtering topics in Rabbit MQ. All other approach is the same as in simple example. This approach as XML is listed below.

<?xml version="1.0" encoding="UTF-8"?><sequence xmlns="http://ws.apache.org/ns/synapse" name="rabbitmq.router" onError="fault">
  <filter source="$axis2:HTTP_METHOD" regex="PUT">
    <then>
      <property name="exchangeName" value="null" scope="default" type="STRING"/>
      <property name="routingKey" value="null" scope="default" type="STRING"/>
      <switch source="get-property('To')">
        <case regex=".*\/data\/sample\/([^\/]*)\/id\/([^\/]*)\/status">
          <property name="routingKey" expression="concat('sample.',get-property('uri.var.sampleId'),'.id.',get-property('uri.var.id'))" scope="default" type="STRING"/>
          <property name="exchangeName" value="sample.api.data" scope="default" type="STRING"/>
        </case>
        <case regex=".*\/data\/complex\/([^\/]*)\/guid\/([^\/]*)">
          <property name="routingKey" expression="concat('complex.',get-property('uri.var.complexId'),'.id.',get-property('uri.var.guid'))" scope="default" type="STRING"/>
          <property name="exchangeName" value="complex.api.data" scope="default" type="STRING"/>
        </case>
      </switch>
      <filter xpath="$ctx:exchangeName != 'null' and $ctx:routingKey != 'null'">
        <then>
          <property name="OUT_ONLY" value="true" scope="default" type="STRING"/>
          <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2" type="STRING"/>
          <header xmlns:ns="http://org.apache.synapse/xsd" name="To" scope="default" expression="fn:concat('rabbitmq:/?rabbitmq.connection.factory=CachedRabbitMQConnectionFactory&rabbitmq.queue.delivery.mode=2&rabbitmq.exchange.name=',$ctx:exchangeName,'&rabbitmq.queue.routing.key=',$ctx:routingKey)"/>
          <send/>
        </then>
        <else/>
     </filter>
    </then>
    <else/>
  </filter>
</sequence>

Summary

In this article, I have described a fairly simple way to separate API traffic to different endpoints. It can be use for one API on front, with all benefits of WSO2 API Manager, and have sepparate backend endpoints. For example for bigger traffic. Or can be use in CQRS-style separating queres to backend and commands to queue system like Rabbit MQ.