Customising path patterns for your Apiman Gateway

· apiman, gateway, manager, plugin, extensibility
Avatar for Marc Savy
Co-founder & maintainer of Apiman. Founded Black Parrot Labs to support enterprise Apiman users.
/ Black Parrot Labs /

One common request we hear is how to create custom URL patterns for the Apiman Gateway.

For example, this means allowing changing the (Public API) default:


To a custom alternative. As a simple example we’re going to hard-code an organisation in. We’ll assume that we’ve established a convention to always publish our APIs to a particular org. That will change the pattern to:

If you’re using the Vert.x Gateway you should use Apiman 1.4.3.Final or later as a bug prevented plugins from loading from static config.

Create a plugin skeleton

Check out our documentation for creating Apiman Plugins for more in-depth information.

One of Apiman’s handiest features is its plugin subsystem. This allows you to fully modularise custom code and distribute it through familiar channels like Maven repositories. It’s much more convenient than trying to play around with customising the class-path and messing around to make sure files are in the right place.

First, check out this plugin skeleton:

$ git clone
$ cd custom-path-parser-demo/

Have a look at It implements IApiRequestPathParser and takes the incoming path and headers; it returns an ApiRequestPathInfo that instructs Apiman what the orgId, apiId, apiVersion, and resource are, allowing Apiman to map the incoming request correctly.

public class CustomPathParserImpl implements IApiRequestPathParser {
    public ApiRequestPathInfo parseEndpoint(String path, HeaderMap headers) {
        // Your custom path logic

For reference the default implementation is io.apiman.gateway.engine.impl.DefaultRequestPathParser.

Creating our custom version

In our demonstration CustomPathParserImpl we will use a very simple parser that doesn’t inspect any headers. I recommend you check the default implementation to find utilities that handles alternative ways of capturing versioning info, such as accept headers, version headers, etc.

First we’ll add a constructor with a string map. This will allow us to pass in configuration information from our static config file ( or conf.json).

final String defaultOrgName;

public CustomPathParserImpl(Map<String, String> config) {
   // Pass in the defaultOrgName from static config or just use "DefaultOrg".
   this.defaultOrgName = config.getOrDefault("defaultOrgName", "DefaultOrg");

public ApiRequestPathInfo parseEndpoint(String path, HeaderMap headers) {
   String[] split = StringUtils.split(path, "/", 3);

   if (split == null || split.length < 2) {
      throw new IllegalArgumentException("Invalid path format, expected /apiId/apiVersion/<resource path>");

   ApiRequestPathInfo parsed = new ApiRequestPathInfo();
   // Let's set the org name manually as our configured `defaultOrgName`
   parsed.orgId = defaultOrgName;
   parsed.apiId = split[0];
   parsed.apiVersion = split[1];
   if (split.length > 2) {
      parsed.resource = "/" + split[2];
   } else {
      parsed.resource = "/";
   return parsed;

So now, if we have a path like this:


This will become:









Which would be the equivalent of the default style of:


Wiring it up

Change the versions as applicable.

Run mvn clean install at the demo project’s root. Then open your Apiman configuration file (either or conf.json).

For the Vert.x Gateway:

"request-parser": {
    "class": "plugin:io.apiman.plugins:custom-path-parser-plugin-demo:1.4.3.Final:war/io.apiman.plugins.demo.custompathparser.CustomPathParserImpl",
    "config": {
        "defaultOrgName": "Apiman" // Whatever you like

For the Servlet Gateway:

apiman-gateway.request-parser.defaultOrgName=Apiman // Whatever you like
$ curl 'http://localhost:8082/TheApi/2'
  "method" : "GET",
  "resource" : "/services/echo/foobar/",
  "uri" : "/services/echo/foobar/",
  "headers" : {
    "Accept" : "*/*",
    "Host" : "localhost:8080",
    "transfer-encoding" : "chunked",
    "User-Agent" : "curl/7.54.0"
  "bodyLength" : null,
  "bodySha1" : null,
  "counter" : 2
Double check that your plugin is structured correctly, and you have all the names right in your plugin URL. Otherwise, you will get errors complaining that your classes are not found.


We created a simple plugin to provide custom URL mappings to the Apiman Gateway and configured our gateways to use our code.

A future improvement extending IApiRequestPathParser will allow for the Apiman Gateway to report URLs back to the Apiman Manager (or whomever publishes) in a more granular fashion (rather than just using parseEndpoint).