Main menu

When a set of API’s becomes even mildly complex, it is often more easily managed by separating the resource definitions into either their own RAML file or their own Mule ESB applications. In either instance, the difficulty shifts from managing RAML files to managing URL paths. In my experience, there are a few very easy steps to take in order to combat this problem. There very well could be a more elegant solution that I have not seen, so I will post my solution and let the more informed people tear me apart.

With Mule ESB, each HTTP listener needs a unique path in order to know which listener to invoke. It makes complete sense. The problem arises when you are trying to define a nice, consistent URL scheme throughout our API’s. For example, let’s say that you have three API definitions for three different interfaces, apples, oranges, and pears. Your goal is to expose these three separate interfaces with the same URL pattern:

  • http://api.somedomain.com/v1/apples
  • http://api.somedomain.com/v1/oranges
  • http://api.somedomain.com/v1/pears

Let’s also say that these are three separate Mule applications under a single Mule Domain called fruits. It’s easy enough to create a common listener in the fruits Domain application:

<http:listener-config name="HttpListener" host="0.0.0.0"
   port="80" basePath="/v1" doc:name="HTTP Listener Configuration"/>

Next, we need to configure each of our APIkit routers and APIkit listener flows. For brevity, I will stick with apples and you can assume that oranges and pears have a similar configuration:

<apikit:config name="ApplesApiRouter" raml="apples.raml" consoleEnabled="true" consolePath="console" doc:name="Router">
  <apikit:flow-mapping resource="/apples" action="get" flow-ref="apples-get"/>
</apikit:config>
<flow name="test-http-1Flow">
  <http:listener config-ref="HttpListener" path="/*" doc:name="HTTP"/>
  <apikit:router config-ref="ApplesApiRouter" doc:name="APIkit Router"/>
</flow>

Finally, we need to define our resources in the RAML file:

#%RAML 0.8
---
baseUri: http://api.somedomain.com:80/v1/apples
title: Apples API documentation:
- title: Home
  content: |     The request path for all of the interfaces listed below should begin with /v1/apples.
/apples:
 get:

This configuration works, if the only API that you run is the Apples API. Notice the path element of the HTTP listener. It is currently set to “/*”. This will not work when you try to startup Oranges and Pears, because their paths need to be unique. If you configure the path value to be “/apples/*”, then you will be stuck with a URL that looks like http://api.somedomain.com/v1/apples/apples.

There are a couple of solutions that we have, assuming that we don’t want to create a single APIkit configuration and listener for all of the interfaces.

  1. Create a unique path that doesn’t look as awkward, like http://api.somedomain.com/v1/applies-api/apples. It’s easily done by changing the path to “/applies-api/*”. Oranges would be “/oranges-api/*” and Pears would be “/pears-api/*”. That would allow the rest of the configuration to remain the same.

  2. Change the resources to start with the root resources. This is yet another easy change to the flow-mapping elements in the APIkit config and the RAML definition:

    <apikit:flow-mapping resource="/" action="get" flow-ref="apples-get"/> (in the Mule config file)

    and

    /: (in the RAML file)
     get:

    The problem here is that the APIkit Console Web application will display the root element as “/”, instead of “/apples”. If you can live with that, it will work.


I hope this gives you some helpful options. If you have a more elegant solution, feel free to post it.