KrakenD cheatsheet
Configuration files
Configuration file structure
There are a large number of options you can put in this file. Let’s focus now only on the main structure:
{
"$schema": "https://www.krakend.io/schema/v3.json",
"version": 3,
"endpoints": [],
"extra_config": {}
}
- $schema: Optional, which JSON schema is used to validate your configuration. Is used by
krakend check --lint - version (mandatory): The version of the KrakenD file format.
- Version 3: Current version (since v2.0)
- Version 2: Deprecated in 2022, for versions between v0.4 and v1.4.1
- Version 1: Deprecated in 2016, for versions v0.3.9 and older.
- endpoints[ ]: An array of endpoint objects offered by the gateway and all the associated backends and configurations. This is your API definition.
- extra_config{ }: Components’ configuration. Whatever is not a core functionality of the Lura Project is declared in a unique namespace in the configuration, so that you can configure multiple elements without collisions.
Flexible configuration
- The Flexible Configuration component is included in the KrakenD API Gateway and allows you to split the configuration into multiple files while using variables and templates
- The only requirement to use the Flexible Configuration is to encode the configuration file in
JSONformat as the package does not support other formats just yet.
Usage
The activation of the package works via environment variables when running krakend, as follows:
- FC_ENABLE=1 to let KrakenD know that you are using Flexible Configuration. You can use 1 or any other value (but 0 won’t disable it!). The file passed with the -c flag is the base template.
- FC_SETTINGS=dirname: The path to the directory with all the settings files.
- FC_PARTIALS=dirname: The path to the directory with the partial files included in the configuration file. Partial files DON’T EVALUATE, they are only inserted in the placeholder.
- FC_TEMPLATES=dirname: The path to the directory with the sub-templates included in the configuration file. These are evaluated using the Go templating system.
- FC_OUT: For debugging purposes, saves the resulting configuration of processing the flexible configuration in the given filename. Otherwise, the final file is not visible.
.
└── config
├── krakend.json
├── partials
│ └── static_file.tmpl
├── templates
│ └── environment.tmpl
└── settings
├── prod
| └── db.json
└── dev
└── db.json
Then you can run KrakenD from the terminal with this command:
Enabling flexible configuration with your custom dirs
$FC_ENABLE=1 \
FC_SETTINGS="config/settings/prod" \
FC_PARTIALS="config/partials" \
FC_TEMPLATES="config/templates" \
krakend run -c "config/krakend.json"
Template syntax
The configuration file passed with the -c flag is treated as a Go template (documentation), and you can make use of all the power the template engine brings. In addition, it also loads Sprig functions. The data evaluations or control structures are easily recognized as they are surrounded by {{ and }}. Any other text outside the delimiters is copied to the output unchanged.
These are all the syntax possibilities:
{{ .file.key }}: Insert the value of a key in a settings file{{ marshal .file.key }}: Insert a JSON structure under a key in a settings file{{ include "file.txt" }}: Replace with the complete content of the file.txt{{ template "file.tmpl" context }}: Process the Go template file.tmpl passing in its dot ({{ . }}) the context
For instance, if you have a file db.json with the following content:
{
"host": "192.168.1.23",
"port": 8766,
"pass": "a-p4ssw0rd",
"label": "production"
}
You can access particular settings like using this syntax: {{ .db.host }}.
The first name after the dot is the name of the file, and then the element in the structure you want to access. The example would write 192.168.1.23 where you wrote the placeholder.
Insert structures from settings files
When instead of a single value you need to insert a JSON structure (several elements), you need to use marshal.
{{ marshal .db }}
The example would write the entire content of the db.json file.
Include an external file
To insert the content of an external partial file in-place use:
{{ include "partial_file_name.txt" }}
The content inside the partial template is not parsed, and is inserted as is in plain text. The file is assumed to live inside the directory defined in FC_PARTIALS and can have any name and extension.
Include and process a sub-template
While the include is only meant to paste the content of a plain text file, the template gives you all the power of Go templating (documentation). The syntax is as follows:
{{ template "template_name.tmpl" context }}
The template template_name.tmpl is executed and processed. The value of context is passed in the template as the
context, meaning that the sub-template can access it using the dot {{ . }}. This context variable could be an object,
such as {{ template "environment" .db.label }}, but it can also be another type, like a string: `{{ template
“environment” “production” }}.
krakend.json
Finally, introducing the base template. It inserts the content of other files using include, uses the variables declared in the settings files and writes json content with marshal.
Have a look at the highlighted lines:
{
"version": 3,
"port": {{ .service.port }},
"extra_config": {{ marshal .service.extra_config }},
"host": {{ marshal .service.default_hosts }},
"endpoints": [
{{ range $idx, $endpoint := .endpoint.example_group }}
{{if $idx}},{{end}}
{
"endpoint": "{{ $endpoint.endpoint }}",
"backend": [
{
"url_pattern": "{{ $endpoint.backend }}",
"extra_config": {
{{ include "rate_limit_backend.tmpl" }}
}
}
]}
{{ end }}
]
}
- The .service.port is taken from the service.json file.
- The extra_config in the third line is inserted as a JSON object using the marshal function from the service.json as well.
- A range iterates the array found under endpoint.json and key example_group. The variables inside the range are relative to the example_group content.
- An include in the extra_config inserts the content as is.
Also notice the little trick
{{if $idx}},{{end}} inside the loop. When it is not in the first element 0, it will add a comma to prevent breaking the JSON format. Notice that there is a {{ range }}. If you wanted to use it inside a template, and not the base file, you would need to include it inside a sub-template with{{ template "template.tmp" .endpoint.example_group }}.
KrakenD file supported formats
The expected configuration file format by default is json, but KrakenD can parse different formats if one of the following is found:
- krakend.json
- krakend.toml
- krakend.yaml
- krakend.yml
- krakend.properties
- krakend.props
- krakend.prop
- krakend.hcl
Overriding the configuration with environment vars
First level properties
You can override its value with an environment variable for each configuration value that isn’t nested (meaning first-level properties of the configuration).
All configuration parameters you want to set using environment variables, pass them with a prefix KRAKEND_. The variable name after the prefix must match the property in the configuration value using uppercase.
For instance, take the following krakend.json configuration as an example:
{
"version": 3,
"timeout": "3s",
"name": "Example gateway."
}
To replace values using env vars, run krakend with the following command:
Example: Override configuration with env vars
$KRAKEND_NAME="Build ABC0123" KRAKEND_TIMEOUT="500ms" KRAKEND_PORT=9000 krakend run -c krakend.json
The resulting configuration will be:
{
"version": 3,
"timeout": "500ms",
"name": "Build ABC0123"
}
Overriding properties in any nesting level
If you need to replace content using environment variables at any level, you have to use the flexible configuration. It includes a series of advanced functions including an env function that can write in the config any value.
{
"version": 3,
"name": "Configuration for {{ env "MY_POD_NAMESPACE" }}"
}
Service Settings
We call service settings (or the service layer) those parameters that allow you to change how KrakenD behaves globally (and not to a specific call). The service settings determine how you start the HTTP server, enforce security parameters, or define behavioral options like which reporting activities occur.
Examples of service settings are, the listening port, disabling keep alives, enabling metrics and traces, listening https, or enabling CORS to name a few.
The service settings are written directly in the root of the configuration file or in its corresponding extra_config. For instance:
{
"version": 3,
"port": 8080,
"extra_config": {}
}
Returning the gateway error message
The secure choice of KrakenD is that all errors generated at the gateway are not returned to the client in the body. By settting return_error_msg (boolean) to true, when there is an error in the gateway (such as a timeout, a non-200 status code, etc.) it returns to the client the reason for the failure. The error is written in the body as is.
{
"version": 3,
"extra_config": {
"router":{
"return_error_msg":true
}
}
Enabling Cross Origin Resource Sharing (CORS)
When KrakenD endpoints are consumed from a browser, you might need to enable the Cross-Origin Resource Sharing (CORS) module as browsers restrict cross-origin HTTP requests initiated from scripts.
Configuration
CORS configuration lives in the root of the file, as it’s a service component. Add the namespace security/cors under the global extra_config, as follows:
{
"version": 3,
"extra_config": {
"security/cors": {
"allow_origins": [
"*"
],
"allow_methods": [
"GET",
"HEAD",
"POST"
],
"expose_headers": [
"Content-Length",
"Content-Type"
],
"allow_headers": [
"Accept-Language"
],
"max_age": "12h",
"allow_credentials": false,
"debug": false
}
}
The configuration options of this component are as follows:
- allow_methods (list): The array of all HTTP methods accepted, in uppercase.
- allow_origins (list): An array with all the origins allowed, examples of values are https://example.com, or * (any origin).
- allow_headers (list): An array with the headers allowed. Missing headers in this list won’t be accepted.
- expose_headers (list): Headers that are safe to expose to the API of a CORS API specification
- max_age (string): For how long the response can be cached. The value needs to specify units. Valid time units are: ns, us, (or µs), ms, s, m, h E.g., 12h for 12 hours.
- allow_credentials (boolean): When requests can include user credentials like cookies, HTTP authentication or client side SSL certificates
- debug: (boolean): Show debugging information in the logger, to be used only during development (defaults to false)
Debugging configuration
The following configuration might help you debugging your CORS configuration. Check the inline @comments:
{
"endpoints":[
{
"@comment": "this will fail due to double CORS validation",
"endpoint":"/cors/no-op",
"input_headers":["*"],
"output_encoding": "no-op",
"backend":[
{
"url_pattern": "/__debug/cors",
"host": ["http://localhost:8080"],
"encoding": "no-op"
}
]
},
{
"@comment": "this won't fail because CORS preflight headers are removed from the request to the backend",
"endpoint":"/cors/no-op/martian",
"input_headers":["*"],
"output_encoding": "no-op",
"backend":[
{
"url_pattern": "/__debug/cors/martian",
"host": ["http://localhost:8080"],
"encoding": "no-op",
"extra_config":{
"modifier/martian": {
"fifo.Group": {
"scope": ["request", "response"],
"aggregateErrors": true,
"modifiers": [
{
"header.Blacklist": {
"scope": ["request"],
"names": [
"Access-Control-Request-Method",
"Sec-Fetch-Dest",
"Sec-Fetch-Mode",
"Sec-Fetch-Site",
"Origin"
]
}
}
]
}
}
}
}
]
},
{
"@comment": "this won't fail because no headers are added to the request to the backend",
"endpoint":"/cors/no-op/no-headers",
"output_encoding": "no-op",
"backend":[
{
"url_pattern": "/__debug/cors/no-headers",
"host": ["http://localhost:8080"],
"encoding": "no-op"
}
]
}
]}
Enabling TLS for HTTPS and HTTP/2
There are two different strategies when using TLS over KrakenD:
- Use TLS for HTTPS and HTTP/2 in KrakenD (this document)
- Use a balancer with TLS termination in front of KrakenD (e.g., ELB, HAproxy)
TLS Configuration
To start KrakenD with TLS you need to generate the certificate and provide both the public and the private key:
{
"version": 3,
"tls": {
"public_key": "/path/to/cert.pem",
"private_key": "/path/to/key.pem"
}
}
The mandatory options of the TLS configuration are:
- public_key: Absolute path to the public key, or relative to the current working directory
-
private_key: Absolute path to the private key, or relative to the current working directory Plus these optional:
-
disabled (boolean): A temporary flag to disable TLS (e.g: while in development)
-
min_version (string): Minimum TLS version (one of SSL3.0, TLS10, TLS11, TLS12 or TLS13)
-
max_version (string): Maximum TLS version (one of SSL3.0, TLS10, TLS11, TLS12 or TLS13)
-
enable_mtls (boolean): Whether to enable or not Mutual Authentication. When mTLS is enabled, all KrakenD endpoints require clients to provide a known client-side X.509 authentication certificate. KrakenD relies on the system’s CA to validate certificates. See Mutual Authentication
-
curve_preferences (integer array): The list of all the identifiers for the curve preferences (use 23 for CurveP256, 24 for CurveP384 or 25 for CurveP521)
-
prefer_server_cipher_suites (boolean): Enforces the use of one of the cipher suites offered by the server, instead of going with the suite proposed by the client.
-
cipher_suites (integer array): The list of cipher suites (see below). The list of cipher suites with its values is:
- 5: TLS_RSA_WITH_RC4_128_SHA
- 10: TLS_RSA_WITH_3DES_EDE_CBC_SHA
- 47: TLS_RSA_WITH_AES_128_CBC_SHA
- 53: TLS_RSA_WITH_AES_256_CBC_SHA ,etc
Generate a certificate
Example to generate a self-signed certificate from the command line:
$openssl req -newkey rsa:2048 -new -nodes -x509 -days 365 -out cert.pem -keyout key.pem -subj \"/C=US/ST=California/L=Mountain View/O=Your Organization/OU=Your Unit/CN=localhost\"
HTTP Security
KrakenD has implemented several security strategies, controlled via krakend-httpsecure. To enable them you only need to add its namespace security/http at the extra_config in the root level of the configuration.
The following configuration describes all possible options:
{
"version": 3,
"extra_config": {
"security/http": {
"allowed_hosts": [
"host.known.com:443"
],
"ssl_proxy_headers": {
"X-Forwarded-Proto": "https"
},
"host_proxy_headers":[
"X-Forwarded-Hosts"
],
"ssl_redirect": true,
"ssl_host": "ssl.host.domain",
"sts_seconds": 300,
"sts_include_subdomains": true,
"frame_deny": true,
"referrer_policy": "same-origin",
"custom_frame_options_value": "ALLOW-FROM https://example.com",
"hpkp_public_key": "pin-sha256=\"base64==\"; max-age=expireTime [; includeSubDomains][; report-uri=\"reportURI\"]",
"content_type_nosniff": true,
"browser_xss_filter": true,
"content_security_policy": "default-src 'self';",
"is_development": false
}
}
See below the different options described in this configuration file.
General security
- is_development (bool): This will cause the AllowedHosts, SSLRedirect, and STSSeconds/STSIncludeSubdomains options to be ignored during development. When deploying to production, be sure to set this to false.
- referrer_policy (string): Allows the Referrer-Policy header with the value to be set with a custom value. Default is “”.
- host_proxy_headers (list): A set of header keys that may hold a proxied hostname value for the request.
- ssl_proxy_headers (map): Header keys with associated values that would indicate a valid https request. Useful when using Nginx, e.g: “X-Forwarded-Proto”: “https”
- ssl_redirect (bool): Redirect any request that is not using HTTPS
- ssl.host.domain (string): When the ssl_redirect flag is set to true, the host where the user is redirected.
Endpoint configurations
The endpoint object
To create an endpoint, you only need to add an endpoint object under the endpoints collection. An endpoint object should contain at least the endpoint name and a backend section (to where it connects to). The defaults are taken if no further information is declared (e.g.: method will be a GET, and output_encoding as json).
An endpoints section might look like this:
{
"endpoints": [
{
"endpoint": "/v1/users/{user}",
"method": "GET",
"backend": [
{
"url_pattern": "/users/summary/{user}",
"method": "GET",
"host": [
"https://api.mybackend.com"
]
}
]
}
]
}
Endpoint configuration
The endpoint object accepts the following attributes. As you can see, most of them are optional:
- endpoint: The exact string resource URL you want to expose. You can use {placeholders} to use variables when needed. URLs do not support colons : in their definition.
- backend: List of all the backend objects queried for this endpoint.
- method (optional): Must be written in uppercase GET, POST, PUT, PATCH, DELETE. Defaults to GET.
- output_encoding (optional): See the supported encodings. Defaults to json.
- extra_config (optional): Configuration of components and middlewares that are executed with this endpoint.
- input_query_strings (optional): Recognized GET parameters. See parameter forwarding.
- input_headers (optional): Forwarded headers. See headers forwarding.
- concurrent_calls (optional): A technique to improve response times. See concurrent requests
- cache_ttl (optional): (time unit) The cache headers informing for how long the CDN can cache the request to this endpoint. Related: caching backend responses.
- timeout (optional): (time unit) Maximum time you’ll wait for the slowest backend response. Usually specified in seconds (s) or milliseconds (ms. E.g: 2000ms or 2s)
Valid time units are: ns, us, (or µs), ms, s, m, h E.g: 1s
Endpoints with multiple nesting levels
You might have envisioned KrakenD as a proxy and expected its endpoint declaration works as a prefix and listens to any path with an undetermined number of nesting levels. But KrakenD does not work like this by default. Instead, it expects you to declare every possible URL structure.
For instance, you declared and "endpoint": "/user/{id}" and you expected to resolve URLs like /user/john/profile/preferences, but you are getting a 404 instead. There are two solutions to this problem:
- 1 You declare all possible endpoints: /user/{id}, /user/{id}/{level2}, /user/{id}/{level2}/{level3}, etc.
- 2 You use a Wildcard (Enterprise only)
Endpoints listening to multiple methods
The method attribute defines the HTTP verb you can use with the endpoint. If you need to support multiple methods (e.g., GET, POST, DELETE) in the same endpoint, you will need to declare one endpoint object for each method. So if you want the same endpoint to listen to GET and POST requests, you need the following configuration:
{
"endpoints": [
{
"endpoint": "/v1/users/{user}",
"method": "GET",
"backend": [
{
"url_pattern": "/users/summary/{user}",
"method": "GET",
"host": [
"https://api.mybackend.com"
]
}
]
},
{
"endpoint": "/v1/users/{user}",
"method": "POST",
"backend": [
{
"url_pattern": "/users/summary/{user}",
"method": "POST",
"host": [
"https://api.mybackend.com"
]
}
]
}
]
}
Forwarding query strings and headers
KrakenD is an API Gateway with a zero-trust policy, and when it comes to forward query strings, cookies, and headers, you need to define what is allowed.
Configuration to enable parameter forwarding
You can change the default behavior according to your needs and define which elements can pass from the client to your backends. To do that, add the following configuration options under your endpoint definition:
- input_query_strings (array): Defines the exact list of query strings that are allowed to reach the backend when passed
- input_headers (array): Defines the list of all headers allowed to reach the backend when passed
A single star element (["*"]) as the value of the options above, forwards everything to the backend (it’s safer avoiding this option)
Example:
Send the query strings items and page to the backend, and also User-Agent and Accept headers:
{
"version": 3,
"endpoints": [
{
"endpoint": "/v1/foo",
"input_query_strings": [
"items",
"page"
],
"input_headers": [
"User-Agent",
"Accept"
],
"backend": [
{
"url_pattern": "/catalog",
"host": [
"http://some.api.com:9000"
]
}
]
}
]
}
Sending all client headers to the backends
While the default policy prevents forwarding unrecognized headers, setting an asterisk * as the parameter name makes the gateway to forward any header to the backends, including cookies:
{
"endpoint": "/foo",
"input_headers":[
"*"
]
}
Enabling the wildcard pollutes your backends, as any header sent by end-users or malicious attackers gets through the gateway and impacts the backends behind (a famous exploit is the Log4J vulnerability). We recommend letting the gateway know which headers are in the API contract and specify them in the list, even when the list is long try to not use the wildcard. If the decision is to go with the wildcard, make sure your backends can handle abuse attempts from clients.
Cookies forwarding
A cookie is just some content passing inside the Cookie header. If you want cookies to reach your backend, add the Cookie header under input_headers, just as you would do with any other header.
When doing this, all your cookies are sent to all backends inside the endpoint. Use this option wisely!
Example:
{
"version": 3,
"endpoints": [
{
"endpoint": "/v1/foo",
"input_headers": [
"Cookie"
],
"backend": [
{
"url_pattern": "/catalog",
"host": [
"http://some.api.com:9000"
]
}
]
}
]
}
Router Rate-limiting
The router rate limit feature allows you to set a number of maximum requests per second a KrakenD endpoint will accept. There are two different strategies to set limits that you can use separately or together:
- Endpoint rate-limiting: applies simultaneously to all your customers using the endpoint, sharing the same counter.
- User rate-limiting: applies to an individual user.
Configuration
The configuration allows you to use both types of rate limits at the same time:
{
"endpoint": "/limited-endpoint",
"extra_config": {
"qos/ratelimit/router": {
"max_rate": 50,
"client_max_rate": 5,
"strategy": "ip"
}
}
}
The following options are available to configure. You can use max_rate and client_max_rate together or separated.
- max_rate (integer): Sets the number of maximum requests the endpoint can handle per second. The absence of max_rate in the configuration or 0 is the equivalent to no limitation.
- client_max_rate (integer): Number of requests per second this endpoint will accept for each user (user quota). The client is defined by strategy. Instead of counting all the connections to the endpoint as the option above, the client_max_rate keeps a counter for every client and endpoint. Keep in mind that every KrakenD instance keeps its counters in memory for every single client.
- strategy (string): The strategy you will use to set client counters. One of ip or header. Only to be used in combination with client_max_rate.
- "strategy": "ip" When the restrictions apply to the client’s IP, and every IP is considered to be a different user. Optionally a key can be used to extract the IP from a custom header:
- E.g, set "key": "X-Original-Forwarded-For" to extract the IP from a header containing a list of space-separated IPs (will take the first one).
- "strategy": "header" When the criteria for identifying a user comes from the value of a key inside the header. With this strategy, the key must also be present.
- E.g., set "key": "X-TOKEN" to use the X-TOKEN header as the unique user identifier.
Merging
When you create KrakenD endpoints, if a specific endpoint feeds from 2 or more backend sources (APIs), they will be automatically merged in a single response to the client. For instance, imagine you have 3 different API services exposing the resources /a,/b, and /c and you want to expose them all together in the KrakenD endpoint /abc
Example:
Imagine an endpoint with the following configuration:
{
"endpoints": [
{
"endpoint": "/users/{user}",
"method": "GET",
"timeout": "800ms",
"backend": [
{
"url_pattern": "/users/{user}",
"host": [
"https://jsonplaceholder.typicode.com"
]
},
{
"url_pattern": "/posts/{user}",
"host": [
"https://jsonplaceholder.typicode.com"
]
}
]
}
]
}
When a user calls the endpoint /users/1, KrakenD will send two requests and, in the happy scenario, it will receive these responses:
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
}
and
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
With these ‘partial responses’ and the given configuration, KrakenD will return the following response:
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
},
"userId": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
Filtering
When you create a KrakenD endpoint you can decide to show only a subset of the fields coming from the response of your backends. There are a number of different reasons you might want to use this functionality, but we strongly encourage you to use it to save user’s bandwidth and increase load and render times.
There are two different strategies you can use to filter content:
- Deny list
- Allow list
KrakenD Status Codes
When consuming content through KrakenD, the status code returned to the client depends on the chosen configuration. Three different approaches impact status codes:
Use KrakenD regular endpoints to get the status codes as designed by KrakenD Return the status code as provided by your backend server (see the no-op encoding) Use custom logic to set specific status codes Default status codes of KrakenD endpoints Unless the no-op encoding is set, the following status codes are the default behavior of any KrakenD endpoint.
| Status Code | When |
|---|---|
| 200 OK | At least one backend returned a 200 or 201 status code on time. Completeness information provided by the X-Krakend-Completed header |
| 404 Not Found | The requested endpoint is not configured on KrakenD |
| 400 Bad Request | Client made a malformed request, i.e. json-schema validation failed |
| 401 Unauthorized | Client sent an invalid JWT token or its claims |
| 403 Forbidden | The user is allowed to use the API, but not the resource, e.g.: Insufficient JWT role, or bot detector banned it |
| 429 Too Many Requests | The client reached the rate limit for the endpoint |
| 503 Service Unavailable | All clients together reached the configured global rate limit for the endpoint |
| 500 Internal Server Error | Default error code, and in general, when backends return any status above 400 |
The /__debug/ endpoint
The /__debug/ endpoint is available when you start the server with the -d flag.
The endpoint can be used as a fake backend and is very useful to see the interaction between the gateway and the backends as its activity is printed in the log using the DEBUG log level. The endpoint returns this content:
{
"message": "pong"
}
Debug endpoint configuration example
The following configuration demonstrates how to test what headers and query string parameters are sent and received by the backends by using the /__debug/ endpoint.
We are going to test the following endpoints:
- /default-behavior: No client headers, query string or cookies forwarded.
- /optional-params: Forwards known parameters and headers
- Recognizes a and b as a query string
- Recognizes User-Agent and Accept as forwarded headers
- /mandatory/{variable}: The query string parameters taken from a variable in the endpoint or other query string parameters To test it right now, save the content of this file in a krakend-test.json and start the server with the -d flag:
{
"version": 3,
"port": 8080,
"host": ["http://127.0.0.1:8080"],
"endpoints": [
{
"endpoint": "/default-behavior",
"backend": [
{
"url_pattern": "/__debug/default"
}
]
},
{
"endpoint": "/optional-params",
"input_query_strings": [
"a",
"b"
],
"input_headers": [
"User-Agent",
"Accept"
],
"backend": [
{
"url_pattern": "/__debug/optional"
}
]
},
{
"endpoint": "/mandatory/{variable}",
"backend": [
{
"url_pattern": "/__debug/qs?mandatory={variable}"
}
]
}
]
}
Start the server:
Run KrakenD with debug mode
$krakend run -d -c krakend-test.json
Now we can test that the endpoints behave as expected:
Default behavior:
Ignore query strings by default
$curl -i 'http://localhost:8080/default-behavior?a=1&b=2&c=3'
In the KrakenD log, we can see that a, b, and c do not appear in the backend call, neither its headers. The curl command automatically sends the Accept and User-Agent headers but they are not in the backend call either, instead we see the KrakenD User-Agent as set by the gateway:
DEBUG: Method: GET
DEBUG: URL: /__debug/default
DEBUG: Query: map[]
DEBUG: Params: [{param /default}]
DEBUG: Headers: map[User-Agent:[KrakenD Version 2.0.6] X-Forwarded-For:[::1] Accept-Encoding:[gzip]]
DEBUG: Body:
[GIN] 2018/11/27 - 22:32:44 | 200 | 118.543µs | ::1 | GET /__debug/default
[GIN] 2018/11/27 - 22:32:44 | 200 | 565.971µs | ::1 | GET /default-behavior?a=1&b=2&c=3
Response content types
KrakenD supports sending responses back to the client using content types other than JSON. The list of supported content types depends on the router package used.
Supported encodings
The gateway can work with several content types, even allowing your clients to choose how to consume the content. The following output_encoding strategies are available to choose for every an endpoint:
- json: The endpoint always returns a JSON object to the client. This is the default encoding if none is declared.
- json-collection: The endpoint returns a JSON collection (array) to the client. The backend response must have an object named collection. This is always true when you use in the backend section the is_collection attribute set to true.
- xml: The endpoint returns an XML object.
- negotiate: Allows the client to choose by parsing its Accept header. KrakenD can return:
- JSON
- XML
- RSS
- YAML
- string: Treat the whole response as a simple string
- no-op: No operation (No encoding, and no decoding), for proxy only. See its documentation.
Each endpoint declaration is able to define which encoder should be used, as shown in this example. By default, when the output_encoding is omitted, KrakenD falls back to JSON:
{
"endpoints": [
{
"endpoint": "/foo",
"output_encoding": "negotiate",
"backend": [
{
"url_pattern": "/foo"
}
]
},
{
"endpoint": "/var",
"output_encoding": "string",
"backend": [
{
"url_pattern": "/var"
}
]
},
{
"endpoint": "/baz",
"backend": [
{
"url_pattern": "/baz"
}
]
}
]
}
The endpoint /baz will use the default encoding json as no encoding has been defined. Using other routers (Lura Project)
If instead of the KrakenD API Gateway, which uses internally the gin router, you decide to build your own custom gateway using the Lura Project, the following routers and output encodings are available:
Gin
The gin-based KrakenD router includes these output encodings:
- json
- string
- negotiate
- no-op
Mux-based
The mux based routers supported by Lura are:
- Mux
- Gorilla
- Negroni
- Chi
- httptreemux
-
and they include these output encodings:
-
json
- string
- no-op
Proxying directly to the backends with no-op
The key concepts of no-op are:
- The KrakenD endpoint works just like a regular proxy
- The router pipe functionalities are available (e.g., rate limiting the endpoint)
- The proxy pipe functionalities are disabled (aggregate/merge, filter, manipulations, body inspection, concurrency…)
- Headers passing to the backend still need to be declared under input_headers, as they hit the router layer first.
- Query strings passing to the backend still need to be declared under input_query_strings, as they hit the router layer first.
- Backend response and headers remain unchanged (including status codes)
- The body cannot be changed and is set solely by the backend
- 1:1 relationship between endpoint-backend (one backend per endpoint).
Example
The following snippet shows an endpoint that is passed to the backend as is. Notice that both the endpoint and the backend have a no-op encoding. The backend is using KrakenD’s debug endpoint to capture the request in the console:
{
"endpoint": "/auth/login",
"output_encoding": "no-op",
"backend": [
{
"encoding": "no-op",
"host": [ "localhost:8080" ],
"url_pattern": "/__debug/login"
}
]
}
Sequential Proxy
The best experience consumers can have with KrakenD API is by letting the system fetch all the data from the different backends concurrently at the same time. However, there are times when you need to delay a backend call until you can inject as input the result of a previous call.
The sequential proxy allows you to chain backend requests.
Chaining the requests
All you need to enable the sequential proxy is add in the endpoint definition the following configuration:
{
"endpoint": "/hotels/{id}",
"extra_config": {
"proxy": {
"sequential": true
}
}
}
When the sequential proxy is enabled, the url_pattern of every backend can use a new variable that references the response of a previous API call. The variable has the following construction:
{resp0_XXXX}
Where 0 is the index of the specific backend you want to access ( backend array), and where XXXX is the attribute name you want to inject from the previous call.
Example
It’s easier to understand with the example of the graph:
KrakenD calls a backend /hotels/{hotel_id} that returns data for the requested hotel. When we request for the hotel ID 25 the backend service responds with the hotel data, including a destination_id that is a relationship identifier. The output for GET /hotels/25 is like the following:
{
"hotel_id": 25,
"name": "Hotel California",
"destination_id": 1034
}
KrakenD waits for the response of the backend and looks for the field destination_id. And then injects the value in the next backend call to /destinations/{destination_id}. In this case the next call is GET /destinations/1034, and the response is:
{
"destination_id": 1034,
"destinations": [
"LAX",
"SFO",
"OAK"
]
}
Now KrakenD has both responses from the backends and can merge the data, returning the following object to the user:
{
"hotel_id": 25,
"name": "Hotel California",
"destination_id": 1034,
"destinations": [
"LAX",
"SFO",
"OAK"
]
}
The configuration needed for this example is:
{
"endpoint": "/hotel-destinations/{id}",
"backend": [
{ <--- Index 0
"host": [
"https://hotels.api"
],
"url_pattern": "/hotels/{id}"
},
{ <--- Index 1
"host": [
"https://destinations.api"
],
"url_pattern": "/destinations/{resp0_destination_id}"
}
],
"extra_config": {
"proxy": {
"sequential": true
}
}
}
The key here is the variable {resp0_destination_id} that refers to destination_id for the backend with index 0 (first in the list).
Static Proxy - Adding static/stub data
he static proxy is an aid to clients dealing with incomplete and other types of degraded responses. When enabled, the static proxy injects static data in the final response when the behavior of a backend falls in the selected strategy.
A typical scenario is when some backend fails and the endpoint becomes incomplete, but you prefer to provide a stub response for that part instead. When your application cannot handle well the degraded response, the static data comes handy.
Static response strategies
The supported strategies to inject static data are the following:
- always: Injects the static data in the response no matter what.
- success: Injects the data when all the backends did not fail.
- complete: Injects the data when there weren’t any errors, all backends gave a response, and the responses merged successfully
- errored: Injects the data when some backend failed, returning an explicit error.
- incomplete: When some backend did not reach the merge operation (timeout or another reason).
Pay attention to the different strategies as they might offer subtle differences. The code associated to these strategies is:
func staticAlwaysMatch(_ *Response, _ error) bool { return true }
func staticIfSuccessMatch(_ *Response, err error) bool { return err == nil }
func staticIfErroredMatch(_ *Response, err error) bool { return err != nil }
func staticIfCompleteMatch(r *Response, err error) bool { return err == nil && r != nil && r.IsComplete }
func staticIfIncompleteMatch(r *Response, _ error) bool { return r == nil || !r.IsComplete }
Adding static responses
To add a static response add under any endpoint an extra_config entry as follows:
{
"endpoint": "/static",
"extra_config": {
"proxy": {
"static": {
"strategy": "errored",
"data": {
YOUR STATIC JSON OBJECT GOES HERE
}
}
}
}
}
Static proxy example
The following /static endpoint returns {"errored": {"foo": 42, "bar": "foobar"} } when the backend returned errors.
Notice two things in the example trying to avoid collisions. First, each backend uses a group, so when the backend works correctly, its response is inside a key “foo” or “bar”. Using this strategy if “foo” and “bar” use the same keys there is no problem.
Secondly, when one of the 2 backends fail, it creates a new group “oh-snap” (see data).
{
"endpoints": [
{
"endpoint": "/static",
"backend": [
{
"host": ["http://your.backend"],
"url_pattern": "/foo",
"group": "foo"
},
{
"host": ["http://your.backend"],
"url_pattern": "/bar",
"group": "bar"
}
],
"extra_config": {
"proxy": {
"static": {
"strategy": "errored",
"data": {
"oh-snap": {
"id": 42,
"bar": "foobar"
}
}
}
}
}
}
]
}
Conditional request and response with CEL
Configuration
The CEL component goes inside the extra_config of your endpoints or your backend using the namespace validation/cel.
Depending on where you put the extra_config, the gateway will check the expressions at the endpoint level, or the backend level.
For instance, you might want to reject users that do not adhere to some criteria related to the content in their JWT token. There is no reason to delay this check, and you would place the examination at the endpoint level right before hitting any backend. In another scenario, you might want to ensure that the response of a specific backend contains a must-have field; that configuration would go under the backend section and be isolated from the rest of sibling backends under the same endpoint umbrella.
Finally, when combined with the sequential proxy, you can skip requesting a backend if a previous call didn’t fulfill your criteria.
The configuration is as follows:
{
"extra_config": {
"validation/cel": [
{
"check_expr": "CONDITION1 && CONDITION2"
}
]
}
}
Notice that the CEL object is an array. In this example, it contains one object.
check_expr: The expression that evaluates as a boolean, you can write here any conditional. If all stacked conditions are true the request continues, false, it fails to retrieve data from the token, the request, or the response. The expressions can use a set of additional variables, shown in the sections below.
A note on client headers
When client headers are needed, remember to add them under input_headers as KrakenD does not forward headers to the backends unless declared in the list.
Adding logic in the requests and responses.
There are three different ways to access the metadata of requests and responses to decide whether or not to continue serving the user command.
- Use a req_ type variable to access request data.
- Use a resp_ type variable to access response data.
- Use the JWT variable to access the payload of the JWT.
Variables for requests
You can use the following variables inside the check_expr:
- req_method: Returns the method of this endpoint, e.g.: GET
- req_path: The path used to access this endpoint, e.g: : /foo
- req_params: An object with all the placeholder {parameters} declared in the endpoint . All parameters capitalize the first letter. E.g.: An "endpoint": "/v1/users/{id_user}" will set a variable req_params.Id_user containing the value of the parameter passed in the request. When you use the sequential proxy you also have under req_params.RespX_field the response of a previous backend call (where X is the sequence number and field the object you want to retrieve.
- req_headers: An array with all the headers received. The value of the array is at the same time another array, as you can have a header declared multiple times (e.g., multiple cookies with Set-Cookie). You can access headers like this: req_headers['X-Forwarded-For'].
- req_querystring: An Object with all the query strings that the user passed to the endpoint (not anything you wrote on the backend url_pattern). Remember that no query strings pass unless they are in the input_query_strings list. Notice that querystrings, unlike req_params, are NOT capitalized. The req_querystring.foo will also return an array as a query string can contain multiple values (e.g: ?foo[]=1&foo[]=2).
- now: An object containing the current timestamp, e.g: timestamp(now).getDayOfWeek()
Variables for responses
You can use the following variables inside the check_expr:
- resp_completed: Boolean whether all the data has been successfully retrieved
- resp_metadata_status: Returns an integer with the StatusCode
- resp_metadata_headers: Returns an array with all the headers of the response
- resp_data: An object with all the data captured in the response. Using the dot notation, you can access its fields, e.g.:resp_data.user_id. If you use the group operator in the backend, then you need to add it to access the object, e.g., resp_data.mygroup.user_id
- now: An object containing the current timestamp
Lua scripting
Configuration
You can add your Lua scripts under the extra_config at the endpoint level or the backend level. You can choose three different namespaces (explained below):
- "modifier/lua-endpoint"
- "modifier/lua-proxy"
- "modifier/lua-backend"
The configuration options are:
{
"extra_config": {
"modifier/lua-proxy": {
"sources": [
"file1.lua"
],
"md5": {
"file1.lua": "49ae50f58e35f4821ad4550e1a4d1de0"
},
"pre": "lua code to execute for pre",
"post": "lua code to execute for post",
"live": false,
"allow_open_libs": false,
"skip_next": true
}
}
}
- sources: An array with all the external files that KrakenD will include in the first place. You can define the functions in external files and refer them on pre or post.
- md5: (optional) The md5sum of each Lua file. Used to make sure that a 3rd party has not modified the file.
- pre: The inline Lua code that is executed before performing the request.
- post: The inline Lua code that is execute after the request. Available when used in the backend section.
- live: Live reload of the script in every execution. Set to true if you intend to modify the Lua script while KrakenD is running (mostly during development)
- allow_open_libs: As an efficiency point, the regular Lua libraries are not open by default. But if you need to use the Lua libraries (for file io for example), then set this to true. If not present, the default value is false.
- skip_next: Only to be set when in a backend section, skips the query to the next backend.
Backend configuration
Backend/Upstream configuration
Inside the backend array, you need to create an object for each upstream service used by its declaring endpoint. The combination of host + url_patternset the full URL that KrakenD will use to fetch your upstream services. Most of the backends will require a simple configuration like:
{
"host": ["http://your-api"],
"url_pattern": "/url"
}
The options relative to the backend definition are:
- host (array - required): An array with all the available hosts to load balance requests, including the schema (when possible) schema://host:port. E.g.: https://my.users-ms.com. If you are in a platform where hosts or services are balanced (e.g., a K8S service), write a single name in the array with the service name/balancer address. Defaults to the host declaration at the configuration’s root level, and KrakenD fails starting when none.
- url_pattern (string - required): The path inside the service (no protocol, no host, no method). E.g: /users. Some functionalities under extra_config might drop the requirement of declaring an url_pattern. The URL must be RESTful, if it is not (e.g.: /url.{some_variable}.json) see below how to disable RESTful checking.
- encoding (string - optional): Define your needed encoding to inform KrakenD how to parse the response. Defaults to the value of its endpoint’s encoding, or to json if not defined anywhere else.
- sd (string - optional): The service Service Discovery system to resolve your backend services. Defaults to static (no external Service Discovery). Use dns to use DNS SRV records.
- method (string - optional): One of GET, POST, PUT, DELETE, PATCH (in uppercase!). The method does not need to match the endpoint’s method.
- disable_sanitize (boolean - optional): Set to true when the host doesn’t need to be checked for an HTTP protocol. This is the case of sd=dns or when using other protocols like amqp://, nats://, kafka://, etc. When set to true, and the protocol is not http, KrakenD fails with invalid host error. Defaults to false.
- extra_config (object - optional ): When there is additional configuration related to a specific component or middleware (like a circuit breaker, rate limit, etc.) it is declared under this section.
Other configuration options such as the ones for data manipulation are available. You will find them in each specific feature section.
Backend configuration example
In the example below, KrakenD offers an endpoint /v1/products that merges the content from two different services using the URLs /products and /offers. The marketing (marketing.myapi.com) and the products (products-XX.myapi.com) API requests are fired simultaneously. KrakenD will load balance among the listed hosts (here or in your service discovery) to pick one of the three hosts.
{
"endpoints": [
{
"endpoint": "/v1/products",
"method": "GET",
"backend": [
{
"url_pattern": "/products",
"host": [
"https://products-01.myapi.com:8000",
"https://products-02.myapi.com:8000",
"https://products-03.myapi.com:8000"
]
},
{
"url_pattern": "/offers",
"host": [
"https://marketing.myapi.com:8000"
]
}
]
}
]
}
Disable RESTful checking
By default KrakenD only works with RESTful URL patterns to connect to backends. Enable the option disable_rest in the root of your configuration if you have backends that aren’t RESTful, e.g.: /url.{some_variable}.json
{
"$schema": "https://www.krakend.io/schema/v3.json",
"version": 3,
"disable_rest": true,
"endpoints": [
{
"endpoint": "/foo",
"backend": [
{
"host": [
"http://mybackend"
],
"url_pattern": "/url.{some_variable}.json"
}
]
}
]
}
Validating the body with the JSON Schema integration
JSON Schema Configuration
The JSON Schema configuration has to be declared at the endpoint level with the namespace object validation/json-schema. KrakenD offers compatibility for the specs draft-04, draft-06 and draft-07.
The following example checks if the body is a json object:
{
"extra_config": {
"validation/json-schema": {
"type": "object"
}
}
}
You can apply constraints by adding keywords to the schema. For instance, you can check that the type is an instance of an object, array, string, number, boolean, or null.
All the configuration inside the namespace is pure JSON Schema vocabulary. Read the JSON schema documentation to get familiar with the specification.
A full configuration for you to try on the localhost with the debug endpoint is:
{
"version": 3,
"port": 8080,
"host": [ "http://127.0.0.1:8080" ],
"endpoints": [
{
"endpoint": "/address",
"method": "POST",
"backend": [
{
"url_pattern": "/__debug/"
}
],
"extra_config":{
"validation/json-schema": {
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
}
}
}
}
}
]
}
Backend/Upstream configuration
{
"host": ["http://your-api"],
"url_pattern": "/url"
}
The options relative to the backend definition are:
- host (array - required): An array with all the available hosts to load balance requests, including the schema (when possible) schema://host:port. E.g.: https://my.users-ms.com. If you are in a platform where hosts or services are balanced (e.g., a K8S service), write a single name in the array with the service name/balancer address. Defaults to the host declaration at the configuration’s root level, and KrakenD fails starting when none.
- url_pattern (string - required): The path inside the service (no protocol, no host, no method). E.g: /users. Some functionalities under extra_config might drop the requirement of declaring an url_pattern. The URL must be RESTful, if it is not (e.g.: /url.{some_variable}.json) see below how to disable RESTful checking.
- encoding (string - optional): Define your needed encoding to inform KrakenD how to parse the response. Defaults to the value of its endpoint’s encoding, or to json if not defined anywhere else.
- sd (string - optional): The service Service Discovery system to resolve your backend services. Defaults to static (no external Service Discovery). Use dns to use DNS SRV records.
- method (string - optional): One of GET, POST, PUT, DELETE, PATCH (in uppercase!). The method does not need to match the endpoint’s method.
- disable_sanitize (boolean - optional): Set to true when the host doesn’t need to be checked for an HTTP protocol. This is the case of sd=dns or when using other protocols like amqp://, nats://, kafka://, etc. When set to true, and the protocol is not http, KrakenD fails with invalid host error. Defaults to false.
- extra_config (object - optional ): When there is additional configuration related to a specific component or middleware (like a circuit breaker, rate limit, etc.) it is declared under this section.
Other configuration options such as the ones for data manipulation are available. You will find them in each specific feature section.
Backend configuration example
In the example below, KrakenD offers an endpoint /v1/products that merges the content from two different services using the URLs /products and /offers. The marketing (marketing.myapi.com) and the products (products-XX.myapi.com) API requests are fired simultaneously. KrakenD will load balance among the listed hosts (here or in your service discovery) to pick one of the three hosts.
{ "endpoints": [ { "endpoint": "/v1/products", "method": "GET", "backend": [ { "url_pattern": "/products", "host": [ "https://products-01.myapi.com:8000", "https://products-02.myapi.com:8000", "https://products-03.myapi.com:8000" ] }, { "url_pattern": "/offers", "host": [ "https://marketing.myapi.com:8000" ] } ] } ] }
Disable RESTful checking
By default KrakenD only works with RESTful URL patterns to connect to backends. Enable the option disable_rest in the root of your configuration if you have backends that aren’t RESTful, e.g.: /url.{some_variable}.json
{
"$schema": "https://www.krakend.io/schema/v3.json",
"version": 3,
"disable_rest": true,
"endpoints": [
{
"endpoint": "/foo",
"backend": [
{
"host": [
"http://mybackend"
],
"url_pattern": "/url.{some_variable}.json"
}
]
}
]
}
Data manipulation
Filtering
Two different filtering strategies, pick one or the other in each endpoint:
- Deny list (deny)
- Allow list (allow)
Note: Prior to KrakenD 1.2 these terms where known as whitelist and blacklist.
Deny
The deny list filter can be read as the don’t show this filter. KrakenD will remove from the backend response all matching fields (case-sensitive) defined in the list, and the ones that do not match are returned. Use the deny list to exclude some fields in the response.
To exclude a field from the response, add under the desired backend configuration a deny array with all the fields you don’t want to show. E.g.:
{
"deny": [
"token",
"CVV",
"password"
]
}
Nested fields (dot operator)
The deny fields of your choice can also be nested ones. Use a dot as the level separator. For instance the a1 field in the following JSON response { "a": { "a1": 1 } } can be added in the deny list as a.a1.
Deny list example
The KrakenD endpoint to accept URLs like/posts/1 is defined as follows:
{
"endpoint": "/posts/{user}",
"method": "GET",
"backend": [
{
"url_pattern": "/posts/{user}",
"host": [
"https://jsonplaceholder.typicode.com"
],
"deny": [
"body",
"userId"
]
}
]
}
When calling the KrakenD endpoint /posts/1 the response you would get will be as follows:
{
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
}
Allow
The allow list filter can be read as the only show this filter. When you set an allow list KrakenD will include in the endpoint response, only those fields that match exactly with your choice. Use the allow list to strictly define the fields you want to show in the response.
The allowed fields of your choice can also be nested. Use a dot as the level separator. For instance the a1 field in the following JSON response { "a": { "a1": 1 } } can be defined as a.a1.
Allow list example
We will repeat the same exercise we did in the deny list to get the same output. We only want to get the id and title fields from the backend.
{
"endpoint": "/posts/{user}",
"method": "GET",
"backend": [{
"url_pattern": "/posts/{user}",
"host": [
"https://jsonplaceholder.typicode.com"
],
"allow": [
"id",
"title"
]
}]
}
When calling the KrakenD endpoint /posts/1 the response you would get will be as follows:
{
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
}
Grouping
KrakenD can group your backend responses inside different objects. When you set a group attribute for a backend, instead of placing all the response attributes in the root of the response, KrakenD creates a new key and encapsulates the response inside.
Grouping example
The following code is an endpoint that gets data from two different backends, but one of the responses is encapsulated inside the field last_post.
{
"endpoint": "/users/{user}",
"method": "GET",
"backend": [
{
"url_pattern": "/users/{user}",
"host": ["https://jsonplaceholder.typicode.com"]
},
{
"url_pattern": "/posts/{user}",
"host": ["https://jsonplaceholder.typicode.com"],
"group": "last_post"
}
]
}
This will generate responses like this one:
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
},
"last_post": {
"id": 1,
"userId": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
}
Mapping
Mapping, or also known as renaming, let you change the name of the fields of the generated responses, so your composed response would be as close to your use case as possible without changing a line on any backend.
In the mapping section, map the original field name with the desired name.
Mapping example:
Instead of showing the email field we want to name it personal_email:
{
"endpoint": "/users/{user}",
"method": "GET",
"backend": [
{
"url_pattern": "/users/{user}",
"host": [
"https://jsonplaceholder.typicode.com"
],
"mapping": {
"email": "personal_email"
}
}
]
}
Will generate responses like this one:
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"personal_email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
}
Target
It is frequent in many API implementations that the desired data lives inside a generic field like data or content, and you don’t want to have the encapsulating level included in your responses.
Use target when you want to capture the content inside these generic containers and work with the rest of the manipulation options as if it were on the root. The capture takes place before other options like allow list or mapping.
The capturing option uses the attribute target in the configuration file.
Capturing a target example
Given a backend endpoint with this kind of responses containing a level data:
{
"apiVersion":"2.0",
"data": {
"updated":"2010-01-07T19:58:42.949Z",
"totalItems":800,
"startIndex":1,
"itemsPerPage":1,
"items":[]
}
}
And we want a response with the contents inside data only, like:
{
"updated":"2010-01-07T19:58:42.949Z",
"totalItems":800,
"startIndex":1,
"itemsPerPage":1,
"items":[]
}
We need this KrakenD configuration:
{
"endpoint": "/foo",
"method": "GET",
"backend": [
{
"url_pattern": "/bar",
"target": "data"
}
]
}
Collections
Working with collections (or arrays) is a special manipulation case. There are two different scenarios regarding collections:
- When the whole backend response is inside an array instead of an object
- When you want to manipulate collections (e.g., a path like data.item[N].property)
By default, KrakenD adds the key collection in the response, e.g.:
{
"collection": [
{"a": true },
{"b": false}
]
}
The following is a real example based on a collection response, copy and paste to test in your environment:
{
"endpoint": "/posts",
"backend": [
{
"url_pattern": "/posts",
"host": ["http://jsonplaceholder.typicode.com"],
"sd": "static",
"is_collection": true,
"mapping": {
"collection": "myposts"
}
}
]
}
The response will look like this:
{
"myposts": [
{ },
{ }
]
}
Rate-limiting backends
This parameter is defined at the krakend.json configuration file as follows:
{
"endpoint": "/products/{cat_id}",
"backend": [
{
"host": ["http://some.api.com/"],
"url_pattern": "/catalog/category/{cat_id}.rss",
"encoding": "rss",
"extra_config": {
"qos/ratelimit/proxy": {
"max_rate": 0.5,
"capacity": 1
}
}
}
There are two parameters you can set:
- max_rate (float): Maximum requests per second you want to accept in this backend.
- capacity: The capacity according to the token bucket algorithm with a bucket capacity == tokens added per second so KrakenD is able to allow some bursting on the request rates. Recommended value is capacity == max_rate
Service Discovery
Service discovery (sd) is an attribute in the backend section that enables KrakenD to detect and locate services automatically on your enterprise network.
The possible values for sd are:
- static: For static resolution
- dns: For DNS SRV resolution
Example:
{
"backend": [
{
"url_pattern": "/some-url",
"sd": "static",
"host": [
"http://my-service-01.api.com:9000",
"http://my-service-02.api.com:9000"
]
}
]
}
DNS SRV Service Discovery (Kubernetes/Consul)
The DNS SRV(RFC) is a market standard used by systems such as Kubernetes, Mesos, Haproxy, Nginx plus, AWS ECS, Linkerd, and more. An SRV entry is a custom DNS record used to establish connections between services. When KrakenD needs to know the location of a specific service, it will search for a related SRV record.
The format of the SRV record is as follows:
_service._proto.name. ttl IN SRV priority weight port target
Example. A service running on port 8000 with maximum priority (0) and a weight 5 ):
_api._tcp.example.com. 86400 IN SRV 0 5 8000 foo.example.com.
To integrate Consul, Kubernetes or any other DNS SRV compatible system as the Service Discovery, you only need to set two keys:
- "sd": "dns": To set service discovery = DNS SRV
- "host": []: The list of all the names providing the resolution
Add these keys in the backend section of your configuration. If there is another host key in the root level of the configuration, you don’t need to declare it here if the value is the same.
For instance:
{
"backend": [
{
"url_pattern": "/foo",
"sd": "dns",
"host": [
"api-catalog.service.consul.srv"
],
"disable_host_sanitize": true
}
]
}
Circuit breaker configuration
The Circuit Breaker is available in the namespace qos/circuit-breaker inside the extra_config key. The following configuration is an example of how to add circuit breaker capabilities to a backend:
{
"endpoints": [
{
"endpoint": "/myendpoint",
"method": "GET",
"backend": [
{
"host": [
"http://127.0.0.1:8080"
],
"url_pattern": "/mybackend-endpoint",
"extra_config": {
"qos/circuit-breaker": {
"interval": 60,
"timeout": 10,
"max_errors": 1,
"name": "cb-myendpoint-1",
"log_status_change": true
}
}
}
]
}
]
}
The attributes available for the configuration are:
- interval: (integer) Time window where the errors count, in seconds.
- timeout: (integer) For how long the circuit breaker will wait before testing again that the backend is healthy.
- max_errors: (integer) The consecutive number of errors within the interval window to consider the backend unhealthy.
- name: (string) A friendly name to identify this circuit breaker’s activity in the logs.
- log_status_change: (boolean) Whether to log the changes of state of this circuit breaker or not.
Supported backend encodings
Setting the encoding is an important part of the backend definition, as it informs KrakenD how to parse the responses of your services.
Each backend can reply with a different encoding and KrakenD does not have any problem working with mixed encodings at the same time. You can use the following encoding in each backend section:
- json
- safejson
- xml
- rss
- string
- no-op
How to choose the backend encoding?
Follow this table to determine how to treat your backend content:
| The backend returns… | Then use encoding… |
|---|---|
| JSON inside an object ({}) | json |
| JSON inside an array/collection ([]) | json with "is_collection": true |
| JSON with variable types | safejson |
| XML | xml |
| RSS | rss |
| Not an object, but a string | string |
| Nevermind, just proxy | no-op |
Caching backend responses
Enable the caching of the backend services in the backend section of your krakend.json with the middleware:
{
"extra_config": {
"qos/http-cache": {}
}
}
The middleware does not require additional configuration other than its simple inclusion, although the shared attribute can be passed.
See an example (with shared cache):
With shared cache:
{
"endpoint": "/cached",
"backend": [{
"url_pattern": "/",
"host": ["http://my-service.tld"],
"extra_config": {
"qos/http-cache": {
"shared": true
}
}
}]
}
The shared cache makes that different backend definitions with this flag enabled can reuse the cache. When the shared flag is missing or set to false, the backend uses its own cache private context.
Traffic shadowing or mirroring
To define a backend as a shadow backend you only need to add the flag as follows:
{
"extra_config": {
"proxy": {
"shadow": true
}
}
}
With this change, the backend containing the flag enters into production, but KrakenD ignores its responses.
Traffic shadowing example
The following example shows a backend that is changing from v1 to v2, but we are still unsure of the effects of doing this change in production, so we want to send a copy of the requests to v2 in the first place, but keep the end users receiving the responses from v1 only:
{
"endpoint": "/user/{id}",
"timeout": "150ms",
"backend": [
{
"host": [ "http://my.api.com" ],
"url_pattern": "/v1/user/{id}"
},
{
"host": [ "http://my.api.com" ],
"url_pattern": "/v2/user/{id}",
"extra_config": {
"proxy": {
"shadow": true
}
}
}
]
}
Array manipulation - flatmap
Types of manipulations
There are different types of operations you can do:
- Moving, embedding or extracting items from one place to another (equivalent concepts to mapping and allow)
- Deleting specific items (similar concept to deny)
- Appending items from one list to the other
Flatmap configuration
Depending on the stage you want to do the manipulation, you will need an extra_config configuration inside your endpoint or backend section. For both cases, the namespace is proxy.
The component structure with three operations would be as follows:
{
"extra_config": {
"proxy": {
"flatmap_filter": [
{
"type": "move",
"args": ["target_in_collection", "destination_in_collection"]
},
{
"type": "del",
"args": ["target_in_collection"]
},
{
"type": "append",
"args": ["collection_to_append", "returned_collection"]
}
]
}
}
}
- flatmap_filter (list) The list of operations to execute sequentially (top down). Every operation is defined with an object containing two properties:
- type (string) One of the recognized operation types
- args (list) The arguments passed to the operation.
Transforming requests and responses
Add martian modifiers in your configuration under the extra_config of any backend using the namespace modifier/martian.
Your configuration has to look as follows:
{
"extra_config": {
"modifier/martian": {
// modifier configuration here
}
}
}
Transform headers
The header.Modifier injects a header with a specific value. For instance, the following configuration adds a header X-Martian both in the request and the response.
{
"extra_config": {
"modifier/martian": {
"header.Modifier": {
"scope": ["request", "response"],
"name": "X-Martian",
"value": "true"
}
}
}
}
Modify the body
Through the body.Modifier, you can modify the body of the request and the response. You must encode in base64 the content of the body.
The following modifier sets the body of the request and the response to {"msg":"you rock!"}. Notice that the body field is base64 encoded.
{
"extra_config": {
"modifier/martian":
{
"body.Modifier": {
"scope": ["request","response"],
"body": "eyJtc2ciOiJ5b3Ugcm9jayEifQ=="
}
}
}
}
Transform the URL
The url.Modifier allows you to change settings in the URL. For instance:
{
"extra_config": {
"modifier/martian": {
"url.Modifier": {
"scope": ["request"],
"scheme": "https",
"host": "www.google.com",
"path": "/proxy",
"query": "testing=true"
}
}
}
}
Copying headers
Although not widely used, the header.Copy lets you duplicate a header using another name.
{
"extra_config": {
"modifier/martian": {
"header.Copy": {
"scope": ["request", "response"],
"from": "Original-Header",
"to": "Copy-Header"
}
}
}
}
Apply multiple modifiers consecutively
All the examples above perform a single modification in the request or the response. However, the fifo.Group allows you to create a list of modifiers that execute consecutively. The group is needed when using more than one modifier and encapsulates all the following actions to perform in the modifiers array. You can use the FIFO group even when there is only one modifier in the list.
Example of usage (modify the body, and set a header):
{
"extra_config": {
"modifier/martian": {
"fifo.Group": {
"scope": ["request", "response"],
"aggregateErrors": true,
"modifiers": [
{
"body.Modifier": {
"scope": ["request"],
"body": "eyJtc2ciOiJ5b3Ugcm9jayEifQ=="
}
},
{
"header.Modifier": {
"scope": ["request", "response"],
"name": "X-Martian",
"value": "true"
}
}
]
}
}
}
}
Gateway integration with RabbitMQ consumers
Configuration
The consumer retrieves messages from the queue when a KrakenD endpoint plugs to its AMQP backend. The recommendation is to connect consumers to GET endpoints.
A single endpoint can consume messages from N queues, or can consume N messages from the same queue by adding N backends with the proper queue name.
See Async Agents to consume messages without an endpoint.
The needed configuration to run a consumer is:
{
"backend": [{
"host": ["amqp://guest:guest@myqueue.host.com:5672"],
"disable_host_sanitize": true,
"extra_config": {
"backend/amqp/consumer": {
"name": "queue-1",
"exchange": "some-exchange",
"durable": true,
"delete": false,
"no_wait": true,
"no_local": false,
"routing_key": ["#"],
"prefetch_count": 10,
"prefetch_size": 1024,
"auto_ack": false
}
}
}]
}
- name - string as the queue name
- exchange - string the exchange name (must have a topic type if already exists).
- routing_key - list: The list of routing keys you will use to consume messages.
- durable - bool true is recommended, but depends on the use case. Durable queues will survive server restarts and remain when there are no remaining consumers or bindings.
- delete - bool false is recommended to avoid deletions when the consumer is disconnected
- no_wait - bool: When true, do not wait for the server to confirm the request and immediately begin deliveries. If it is not possible to consume, a channel exception will be raised and the channel will be closed.
- prefetch_count - int (optional): Is the number of messages you want to prefetch prior to consume them.
- prefetch_size - int (optional): Is the number of bytes you want to use to prefetch messages.
- no_local - bool (optional) - The no_local flag is not supported by RabbitMQ.
- auto_ack - bool. When KrakenD retrieves the messages, regardless of the success or failure of the operation, it marks them as ACK. Defaults to false
Gateway integration with RabbitMQ producers
Producer Configuration
{
"backend": [{
"host": ["amqp://guest:guest@myqueue.host.com:5672"],
"disable_host_sanitize": true,
"extra_config": {
"backend/amqp/producer": {
"name": "queue-1",
"exchange": "some-exchange",
"durable": true,
"delete": false,
"no_wait": true,
"no_local": false,
"routing_key": "#",
"prefetch_count": 10,
"prefetch_size": 1024,
"mandatory": false,
"immediate": false
}
}
}]
}
- name - string as the queue name
- exchange - string the exchange name (must have a topic type if already exists).
- routing_key - string: The routing keys you will use to send messages.
- durable - bool true is recommended, but depends on the use case. Durable queues will survive server restarts and remain when there are no remaining consumers or bindings.
- delete - bool false is recommended to avoid deletions when the producer is disconnected
- no_wait - bool: When true, do not wait for the server to confirm the request and immediately begin deliveries. If it is not possible to consume, a channel exception will be raised and the channel will be closed.
- prefetch_count - int (optional): Is the number of messages you want to prefetch prior to consume them.
- prefetch_size - int (optional): Is the number of bytes you want to use to prefetch messages.
- mandatory - bool (optional): The exchange must have at least one queue bound when true. Defaults to false.
- immediate - bool (optional): A consumer must be connected to the queue when true. Defaults to false.
Additionally, the items below are parameter keys that can be present in the endpoint URL and are passed to the producer. Parameters need capitalization on the first letter.
- exp_key - string
- reply_to_key - string
- msg_id_key - string
- priority_key - string - Key of the request parameters that is used as the priority value for the produced message.
- routing_key - string - Key of the request parameters that is used as the routing value for the produced message.
For instance, an endpoint URL could be declared as /produce/{a}/{b}/{id}/{prio}/{route} and the producer knows how to map them with a configuration like this:
{
...
"exp_key":"A",
"reply_to_key":"B",
"msg_id_key":"Id",
"priority_key":"Prio",
"routing_key":"Route"
}
Using publisher/subscribe as backends
Can connect an endpoint to multiple publish/subscribe backends, helping you integrate with event driven architectures.
For instance, a frontend client can push events to a queue using a REST interface. Or a client could consume a REST endpoint that is plugged to the last events pushed in a backend. You can even validate messages and formats as all the KrakenD available middleware can be used. The list of supported backend technologies is:
- AWS SNS (Simple Notification Service) and SQS (Simple Queueing Service)
- Azure Service Bus Topic and Subscription
- GCP PubSub
- NATS.io
- RabbitMQ
- Apache Kafka
Configuration
To add pub/sub functionality to your backends include the namespaces backend/pubsub/subscriber and backend/pubsub/publisher under the extra_config of your backend section.
The host key defines the desired driver, and the actual host is usually set in an environment variable outside of KrakenD:
For a subscriber:
{
"host": ["schema://"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/subscriber": {
"subscription_url": "url"
}
}
}
For a publisher:
{
"host": ["schema://"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/publisher": {
"topic_url": "url"
}
}
}
See the specification of each individual technology.
Example (RabbitMQ):
Set the envvar RABBIT_SERVER_URL='guest:guest@localhost:5672' and add in the configuration:
{
"host": ["amqp://"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/subscriber": {
"subscription_url": "myexchange"
}
}
}
GCP PubSub
Google’s Cloud Pub/Sub is a fully-managed real-time messaging service that allows you to send and receive messages between independent applications.
The configuration you need to use is:
- host: gcppubsub://
- url for topics: "projects/myproject/topics/mytopic" or the shortened form "myproject/mytopic"
- url for subscriptions: "projects/myproject/subscriptions/mysub" or the shortened form "myproject/mysub"
- Environment variables: GOOGLE_APPLICATION_CREDENTIALS, see Google documentation.
Example:
{
"host": ["gcppubsub://"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/subscriber": {
"subscription_url": "projects/myproject/subscriptions/mysub"
}
}
}
NATS
NATS.io is a simple, secure and high performance open source messaging system for cloud native applications, IoT messaging, and microservices architectures.
Configuration:
- host: nats://
- Environment variable: NATS_SERVER_URL
- url: mysubject
No query parameters are supported.
Example:
{
"host": ["nats://"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/subscriber": {
"subscription_url": "mysubject"
}
}
}
AWS SNS
Amazon Simple Notification Service (SNS) is a highly available, durable, secure, fully managed pub/sub messaging service that enables you to decouple microservices, distributed systems, and serverless applications. Amazon SNS provides topics for high-throughput, push-based, many-to-many messaging
AWS SNS sets the url without any host or environment variables, e.g:
{
"host": ["awssns:///arn:aws:sns:us-east-2:123456789012:mytopic"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/subscriber": {
"subscription_url": "?region=us-east-2"
}
}
}
AWS SQS
Amazon Simple Queue Service (SQS) is a fully managed message queuing service that enables you to decouple and scale microservices, distributed systems, and serverless applications.
AWS SQS sets the url without any host or environment variables, e.g:
Url: awssqs://sqs-queue-url
{
"host": ["awssqs://sqs.us-east-2.amazonaws.com/123456789012"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/subscriber": {
"subscription_url": "/myqueue?region=us-east-2"
}
}
}
Azure Service Bus Topic and Subscription
Microsoft Azure Service Bus supports a set of cloud-based, message-oriented middleware technologies including reliable message queuing and durable publish/subscribe messaging. These “brokered” messaging capabilities can be thought of as decoupled messaging features that support publish-subscribe, temporal decoupling, and load balancing scenarios using the Service Bus messaging workload.
Configuration:
- host: azuresb://
- Environment variable: SERVICEBUS_CONNECTION_STRING
- Topics: mytopic
- Subscriptions: mytopic?subscription=mysubscription
Note that for subscriptions, the subscription name must be provided in the ?subscription= query parameter.
Example:
{
"host": ["azuresb://"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/subscriber": {
"subscription_url": "mytopic"
}
}
}
RabbitMQ
RabbitMQ is one of the most popular open source message brokers.
Rabbit can alternatively be configured using the AMQP component.
Configuration:
- host: rabbit://
- Environment variable: RABBIT_SERVER_URL
- url for topics: myexchange
- url for subscriptions: myqueue
No query parameters are supported.
Example:
{
"host": ["rabbit://"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/subscriber": {
"subscription_url": "myexchange"
}
}
}
Kafka
Apache Kafka is a distributed streaming platform.
Kafka connection requires KrakenD >= 1.1.
-
host: kafka://
-
Environment var: KAFKA_BROKERS pointing to the server(s), e.g: KAFKA_BROKERS=192.168.1.100:9092
Kafka subscriptions:
{
"host": ["kafka://"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/subscriber": {
"subscription_url": "group?topic=mytopic"
}
}
}
Kafka topics:
{
"host": ["kafka://"],
"disable_host_sanitize": true,
"extra_config": {
"backend/pubsub/publisher": {
"topic_url": "mytopic"
}
}
}
Integration with AWS Lambda functions
The inclusion requires you to add the code in the extra_config of your backend section, using the backend/lambda namespace.
The supported parameters are:
- function_name: Name of the lambda function as saved in the AWS service.
- function_param_name: The endpoint {placeholder} that sets the function name. You have to choose between function_name and function_param_name but not both.
- region: The AWS identifier region (e.g.: us-east-1, eu-west-2, etc. )
- max_retries: Maximum times you want to execute the function until you have a successful response.
- endpoint: An optional parameter to customize the Lambda endpoint to call. Useful when Localstack is used for testing.
Example: Associate a lambda to a backend
When you associate a KrakenD endpoint to a unique lambda function, use this configuration:
{
"endpoint": "/call-a-lambda",
"backend": [
{
"host": ["ignore"],
"url_pattern": "/ignore",
"extra_config": {
"backend/lambda": {
"function_name": "myLambdaFunction",
"region": "us-east-1",
"max_retries": 1
}
}
}
]
}
Here is an example of Lambda code that could run a Hello World (NodeJS):
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
body: {"message": "Hello World"},
};
return response;
};
Example: Take the lambda from the URL
When the name of the Lambda depends on a parameter passed in the endpoint, use this configuration instead:
{
"endpoint": "/call-a-lambda/{lambda}",
"backend": [
{
"host": ["ignore"],
"url_pattern": "/ignore",
"extra_config": {
"backend/lambda": {
"function_param_name": "Lambda",
"region": "us-west1",
"max_retries": 1
}
}
}
]
}
Returning the details of backend errors
Showing backend errors
If you prefer revealing these details to the client, you can choose to show them in the gateway response. To achieve this, enable the return_error_details option in backend the configuration, and then all errors will appear in the desired key.
Place the following configuration inside the backend configuration:
{
"url_pattern": "/return-my-errors",
"extra_config": {
"backend/http": {
"return_error_details": "backend_alias"
}
}
}
Notice that return_error_details sets an alias for this backend.
Response for failing backends
When a backend fails, you’ll find an object named error_ + its backend_alias containing the detailed errors of the backend. The returned structure on error contains the status code and the body:
{
"error_backend_alias": {
"http_status_code": 404,
"http_body": "404 page not found\\n"
}
}
If there are no errors, the key won’t exist.
Example
The following configuration sets an endpoint with two backends that return its errors in two different keys:
{
"endpoint": "/detail_error",
"backend": [
{
"host": ["http://127.0.0.1:8081"],
"url_pattern": "/foo",
"extra_config": {
"backend/http": {
"return_error_details": "backend_a"
}
}
},
{
"host": ["http://127.0.0.1:8081"],
"url_pattern": "/bar",
"extra_config": {
"backend/http": {
"return_error_details": "backend_b"
}
}
}
]
}
Let’s say your backend_b has failed, but your backend_a worked just fine. The client response could look like this:
{
"error_backend_b": {
"http_status_code": 404,
"http_body": "404 page not found\\n"
},
"foo": 42
}