Document updated on Feb 21, 2023
Dynamic Routing in KrakenD API Gateway
The dynamic routing extends the routing capabilities to add header and query string processing to assemble the final upstream URL you want to reach. In addition, it allows you to convert headers to query strings and enforce the existence of parameters on the request.
To enable dynamic routing, you don’t need to add any specific extra entry in the configuration. Instead, write directly in the url_pattern
the variables that inject the content provided by the user. For instance:
{
"endpoint": "/foo",
"backend":[
{
"url_pattern": "/{input_headers.X-Tenant}/foo"
}
]
}
The example above replaces the variable {input_headers.X-Tenant}
with the value provided under a hypothetical X-Tenant
header.
Dynamic routing declaration
There are three different types of input injections you can do in the backend URL, available with the following variables:
input_headers
: Header routinginput_query_strings
: Query string routingJWT
: JWT-claim routing
To declare dynamic routes based on the strategies above, you need to write the variables inside curly braces {}
and traverse the objects using a dot .
separator, including the index of arrays.
400 Bad Request
, as the final URL can’t be generated. Missing JWT claims will pass the request, but you can enforce them with security policiesRouting based on headers
The most typical scenario for dynamic routing is when you want to reach a service based on an input header. To access header values, use the above-defined variable {input_headers.}
.
As the dynamic routing component executes before the endpoint evaluation, you don’t need to add the accessed values under input_headers
unless you want these headers propagated to the backend.
For instance, let’s say that you want to pass a customer
identifier as a header and use it in a URL containing it:
{
"endpoints": [
{
"endpoint": "/user/{id}",
"backend": [{
"url_pattern": "/{input_headers.customer}/user/{id}"
}]
}
]
}
With a configuration like the above, the user would pass the customer information in the header, for instance:
Using routing based on headers
$curl -H'Customer: abcdef' http://krakend/user/1234
The upstream service receives /abcdef/user/1234
.
When multiple entries of the same header exist, you can specify the index of which one is in the variable. For instance, with /foo/{input_headers.customer.1}
, the backend would receive the second value of the header (indexes are zero-based). Not specifying an index always defaults to the first element of an array, so {input_headers.customer}
and {input_headers.customer.0}
both return the same value.
In addition, you can do header validation and other sophisticated checks using the security policies component. For instance, you could make sure that the header adheres to a specific format expressed by a regex:
{
"endpoints": [
{
"endpoint": "/user/{id}",
"backend": [
{
"url_pattern": "/{input_headers.customer}/user/{id_user}"
}
],
"extra_config": {
"security/policies": {
"req": {
"policies": [
"getHeader('Customer').matches('^/[a-z]{4}$')"
],
"error": {
"body": "Mailformed customer request",
"status": 400
}
}
}
}
}
]
}
Routing based on query strings
Similarly to headers, you can route a query string as a path following the same strategy. If you want to forward the query string to the backend as is, you only need to add it under the input_query_strings
list. If on the other side, you would like to convert a query string into a part of the path, then you can apply the variables as follows:
{
"endpoints": [
{
"endpoint": "/user",
"backend": [
{
"url_pattern": "/user/{input_query_strings.id_user}"
}
]
}
]
}
The example above takes a request /user?id_user=john
and converts it to /user/john
towards the backend.
When there are multiple entries of the exact query string, for instance, /foo?q=a&q=b
, you can specify the index in the variable. For example, with /bar/{input_query_strings.q.1}
, the backend would receive /bar/b
(zero-based indexes). Not specifying an index always defaults to the first element of an array, so {input_query_strings.q}
and {input_query_strings.q.0}
both evaluate to /bar/a
.
As the dynamic routing component executes before the endpoint evaluation, you don’t need to add the accessed values under input_query_strings
unless you want the query strings sent twice to the backend.
Routing based on a JWT claim
You can inject claims to the backend’s final URL if you are using the JWT validator in the same endpoint through the {JWT.}
variable. For instance, you could declare a url_pattern
making use of {JWT.sub}
, where sub
is a first-level claim of your JWT payload (you cannot traverse nested claims).
For instance, when your JWT payload is represented by something like this:
{
"sub": "1234567890",
"name": "Mr. KrakenD"
}
Then, when you use an "url_pattern": "/foo/{JWT.sub}"
, it translates into /foo/1234567890
.
If KrakenD can’t replace the claim’s content for any reason, the backend receives a request to the literal URL /foo/{JWT.sub}
, unlike the 400 error of the previous two variables.
Conversion of headers to query strings
You don’t need to use the variables in the url_pattern
as part of the path, and you can use them freely as long you form a valid url_pattern
. Yet, it is especially relevant when passing a header as a query string, where you would use the variable as a query string, not a path. E.g., "url_pattern": "/foo?query={input_headers.query}"
. Any additional query strings you allow to pass using the input_headers
list are appended to the URL pattern.