How do you distinguish your routes, i.e. two APIs have a /users endpoint?

I’m running Kong on a Linux installation, and some of the APIs I manage have duplicate paths like/info, /health, /users, /groups, etc.

To get around this I tried using deck file namespace, which adds a unique prefix (workspace + app name) to all my routes, and a pre-function to strip that path out.

For example if I have an application named ‘security’ in the ‘inf’ workspace, its paths ends up looking like this: /inf/security/logon, /inf/security/users, /inf/security/groups.

The paths look like ~/inf/security/users$, and the upstream is /users, which works. by just using deck commands from start to finish - I generate the api spec programmatically, then I use openapi2kong and namespaceto convert to a kong spec and add the prefixes and pre-function plugin.

However, I also need to log the request body, which also uses a pre-function, and conflicts with the pre-function deck’s namespace command generates.

What’s the right way to strip out inf/security prefix so that all of my upstreams look like /users, /groups, etc. without using a pre-function? I feel like I’ve missed a basic concept.

@David_M Thanks for the question. I took your question and passed it to the Kong Developer AI Assistant to see what it would recommend (Near the top of https://developer.konghq.com/) . I have pasted the response below. I’m not sure the answer is sufficient, as you want to use deck openapi2kong so I don’t believe you can control the generated routes to use capture groups as suggested. I wanted to provide the feedback here but I’ll also see if there is a better answer. Feel free to experiment with the AI Assistant and test the results, I hope it helps.


The core concept you may be missing is using regex capture groups in your route paths combined with the Request Transformer plugin’s replace.uri to rewrite the upstream path. This is a native Kong approach that requires no pre-function at all.

How it works

Instead of relying on a pre-function to strip the namespace prefix, you define your route paths with a named capture group for the “real” path, then use the Request Transformer plugin to set replace.uri to just the captured portion.

For example, for the /inf/security/users case:

Route path:

~/inf/security/(?<path>[^#?/]+.*)

Request Transformer plugin config:

config:
  replace:
    uri: /$(uri_captures['path'])

This rewrites /inf/security/users/users upstream, with no pre-function involved. [dynamic path upstream] [route path transform]

About deck file namespace and pre-functions

The deck file namespace docs describe its prefix-removal logic in priority order:

  1. If the Route has strip_path=true, the added prefix is already stripped — no pre-function needed.

  2. If the Service has a path matching the prefix, the service path is updated.

  3. Otherwise, a pre-function plugin is added to strip the prefix.

[namespace command]

Since openapi2kong generates regex routes ending in $ (e.g., ~/inf/security/users$), strip_path=true on a regex route will strip the entire matched path, not just the prefix — so you’d lose /users entirely. That’s why deck file namespace falls back to a pre-function in your case.

Recommended approach

Rather than using deck file namespace for path stripping, integrate the Request Transformer plugin into your deck file pipeline using deck file add-plugins after the openapi2kong step. Configure routes with capture groups and use replace.uri with $(uri_captures['path']) to reconstruct the upstream path. [rewrite path example]

This way your logging pre-function remains the only pre-function, and path rewriting is handled cleanly by the Request Transformer plugin.

Thanks for the reply, I’ve been leaning toward this approach but haven’t quite wrapped my head around it. The one other issue is that some of the APIs have path parameters, and when I convert them to a kong spec, deck writes the route like this:

~/people/(?[^#?/]+)$

There could be dozens of these routes per API, so I’ll need to use yq or some other tool to modify them.

I’m going to try
~/inf/security(?/people/(?[^#?/]+))$

and see if advanced request transformer works as I hope (I don’t believe request transformer will handle regex).

Just a sidenote here, request transformer doesn’t support named capture groups like this. I ended up using a yq command to get it working, but it’s kind of awkward and I’m hoping to revisit this later.

In the end, this worked:
export NAMESPACE=<my-app>

yq -i ‘.services[].routes[].paths |= map(sub(“^~/([^$]+)”, “~/inf/” + env(NAMESPACE) + “(?/${1})”))’ merge.yaml

Then my transform works:


_format_version: "3.0"
plugins:
  - name: request-transformer
    service: api-cicd-components
    config:
      replace:
        uri: "$(uri_captures['path'])"

I’m just surprised that this functionality wasn’t built in, possibly with its own plugin.

Thanks for sharing @David_M . Out of curiosity, I’d be interested in hearing what the absolute happy path would be for your case. A built in / plugin / capability to go from 1-n number of specs with potentially overlapping paths, namespace them at the gateway route matching level, and strip the namespace before routing the request upstream? Can you describe how you’d like to configure / express that using the tools / configurations we have now?

Appreciate any further thoughts I can try and push some feedback.

Rick