Search code examples
api-gatewaykrakend

krakend api gateway panic: "X in new path conflicts with existing wildcard Y in existing prefix Z"


I have two webservices, and I'd like to manage both endpoints separated by the prefix using krakend API gateway.

Below is my configuration:

{
  "version": 2,
  "name": "My API Gateway",
  "port": 8080,
  "host": [],
  "endpoints": [
    {
      "endpoint": "/api/entity/{entityID}",
      "output_encoding": "no-op",
      "method": "POST",
      "backend": [
        {
          "url_pattern": "/api/entity/{entityID}",
          "encoding": "no-op",
          "host": [
            "http://987.654.32.1"
          ]
        }
      ]
    },
    {
      "endpoint": "/api/entity/member/assign/{userID}",
      "output_encoding": "no-op",
      "method": "GET",
      "backend": [
        {
          "url_pattern": "/api/entity/member/assign/{userID}",
          "encoding": "no-op",
          "host": [
            "http://123.456.789.0"
          ]
        }
      ]
    }
  ]
}

when I run it, error occurs:

panic: 'member' in new path '/api/entity/member/assign/:userID' conflicts with existing wildcard ':entityID' in existing prefix '/api/entity/:entityID'

As far as I understand, it seems the {entityID} on the first endpoint is conflicting with /member/ in the second endpoint. Is this error expected behaviour or is there any problem with my configuration file?


Solution

  • This is a known limitation of the Gin library that KrakenD uses internally, you can reproduce this behavior directly in the library with this go code, which will reproduce exactly the same issue:

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        r := gin.New()
        r.GET("/ping", handler)
        r.GET("/ping/foo", handler)
        r.GET("/ping/:a", handler)
        r.GET("/ping/:a/bar", handler)
    }
    
    func handler(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    }
    

    See the code in this issue.

    The solution is to declare endpoint paths that are not colliding subsets of other endpoints. In your configuration the endpoint /api/entity/member/assign/{userID} is a subset of /api/entity/{entityID}.

    Notice that {placeholders} are like using wildcards, so your first endpoint could be expressed in other systems like /api/entity/*, and therefore /api/entity/member/assign/{userID} is a positive match.

    Any slight change in your configuration where the wildcard does not collide will fix this situation. As an example, the following two endpoints would work for you:

    /api/entity/find/{entityID}
    /api/entity/member/assign/{userID}