# [KrakenD API Gateway - Enterprise Edition Overview (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/overview/) **KrakenD Enterprise is a high-performance API Gateway** built on top of our open-source API Gateway but adding extended Enterprise functionality. As it happens with the Community Edition, the KrakenD Enterprise engine is also fueled by the [Lura Project](https://luraproject.org) (a framework we have hosted at **The Linux Foundation** with the help of the community). KrakenD Enterprise is unique because of its **stateless design** and **extreme simplicity**: - Declarative configuration with GitOps practices. - No node coordination needed when you have multiple instances running - No synchronization with any persistence layer is needed. - Zero complexity to operate and orchestrate. - Presents no challenges for Multi-region deployments. - Linear and constant scalability. - Immutable infrastructure to reproduce the state of the gateway at any point in time. The KrakenD Enterprise functionality allows you to: - Transition from legacy applications to microservices - Create API documentation for your consumers - Publish APIs with centralized authentication and RBAC - Export API activity to metrics, traces, and logs - Audit changes of your API contract thanks to its GitOps approach - Consistent rollback to previous states thanks to its immutable approach to versioning - Succeed as a customer with our SLA and success engineers - Secure and govern your internal and external systems - Efficient development and better time to market - Alleviate the pain of developing APIs and its shared concerns ## KrakenD Enterprise Features KrakenD Enterprise offers over 30 additional features compared to the Community Edition. A side-by-side comparison is shown in the [Open Source vs. Enterprise matrix](/features/). [Feature Matrix](/features/ "Feature Matrix") ### API Gateway The fastest API Gateway in the market, ready to run on premises, cloud, or hybrid. The gateway sits in front of your microservices to provide access control, security, throttling, analytics, and much more. KrakenD is not only a proxy but a Backend for Frontend that can aggregate and transform multiple calls simultaneously, avoiding network traffic and reducing bandwidth consumer to the end-user. ### Observability [Logs](/docs/enterprise/logging/), [metrics and traces](/docs/enterprise/telemetry/), and [enhanced dashboards](/docs/enterprise/telemetry/grafana/) are available to have complete control of what’s going on between the end-user and your microservices. ### Service connectivity KrakenD offers REST endpoints and [WebSockets](/docs/enterprise/websockets/) to the end-user that can aggregate content simultaneously from plenty of sources, like [gRPC services](/docs/enterprise/backends/grpc/), [SOAP](/docs/enterprise/backends/soap/), [REST APIs](/docs/enterprise/backends/), [Kafka](/docs/enterprise/backends/pubsub/#kafka), [RabbitMQ](/docs/enterprise/backends/amqp-consumer/), [NATS](/docs/enterprise/backends/pubsub/#nats), [Lambda functions](/docs/enterprise/backends/lambda/), and a lot more. ### No lock-in KrakenD Enterprise and KrakenD Community have a **compatible and exchangeable configuration**. If you come from the Community edition, you can inject the settings file to the Enterprise without further changes. And the other way around, if you are on Enterprise and want to downgrade to Community, the same configuration is valid. You will lose the enhanced Enterprise functionality as the community version ignores it, but the gateway will run anyway. ### Try KrakenD Enterprise If you’d like to try KrakenD Enterprise at no charge or commitment, [get in touch](/contact-sales/) so we can provide you a trial version. --- # [KrakenD Community Edition Documentation](https://www.krakend.io/docs/overview/) KrakenD is an extensible, declarative, [**high-performance open-source API Gateway**](/open-source/). Its core functionality is to create an API that acts as an aggregator of many microservices into single endpoints, doing the heavy-lifting automatically for you: aggregate, transform, filter, decode, throttle, auth, and more. KrakenD needs **no programming** as it offers a declarative way to create the endpoints. It is well structured and layered, and open to extending its functionality using plug-and-play middleware developed by the community or in-house. KrakenD focuses on being a pure Layer 7 API gateway, not coupled to the HTTP transport layer, and it has been in production in large Internet businesses since early 2017. [See who is using KrakenD and use cases](/case-study/) KrakenD is written in [Go](https://golang.org/). Our engine joined [The Linux Foundation on 2021](/blog/krakend-framework-joins-the-linux-foundation/) codenamed as the [Lura Project](https://luraproject.org/) and was previously known as the *Krakend Framework*. ![](/images/documentation/lura-by-tlf.png "Lura by The Linux Foundation") ## Why an API Gateway? When consumers of API content (especially in microservices) query backend services, the implementations suffer a lot of complexity and burden with the sizes of their microservices responses. KrakenD is an **API Gateway** that sits between the client and all the source servers, adding a new layer that removes all the complexity to the clients, providing them only the information that the UI needs. KrakenD **goes beyond other tools** that are mere reverse proxies, and acts as an **aggregator** of many sources, allowing you to consume in a single call many endpoints. It allows you to group, validate, wrap, transform and shrink requests and responses. Additionally, it supports a myriad of middleware and plugins that allow you to extend the functionality, such as adding OAuth2 authorization, security layers, circuit breaking, rate-limiting, connectivity, logging, metrics, traces, and much more. ### A practical example A mobile or javascript developer needs to construct a single front page that requires data from several calls to their backend services, e.g.: - `api.store.server/products` - `api.store.server/marketing-promos` - `api.users.server/users/{id_user}` - `api.users.server/shopping-cart/{id_user}` The mobile application needs to retrieve data from these four different sources, authorize each of the services, wait for each round trip, and pick only a few fields from the responses (doesn’t need the full response). What if, instead of doing these calls, the mobile client could call a single endpoint to KrakenD to get exactly what it needs? `api.krakend.server/frontpage/{id_user}` This is what KrakenD can do: ![](/images/documentation/krakend-gateway.png "Lura by The Linux Foundation") With this approach, the mobile client isolated itself from the backend implementation. As a result, the backends do not need to address several shared concerns (authentication, rate-limiting, filtering malicious requests, etc.). Furthermore, whenever the backends change their contract, the API contract for the mobile client remains the same, and you can update the gateway via a simple change of configuration. The client becomes faster, as it needs to handle a single HTTP connection for this use case, and there is a difference in size between the amount of data generated by the backends and what is finally traveling to the client. As KrakenD is declarative and GitOps oriented, the mobile developers can update the configuration. There is no need to wait for the backend/infra team to make the changes. --- # [Installing KrakenD Enterprise (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/overview/installing/) KrakenD Enterprise is delivered in several formats, including Docker, Linux packages (deb, rpm) and generic installation options. Find your KrakenD License email To use any Enterprise binaries, **check your inbox** and find our license activation email with the license details to activate the Enterprise content. Don’t have one? Ask for a trial version. [Managing the License](/docs/enterprise/overview/license-file/ "Managing the License") [Ask for a trial](/contact-sales/ "Ask for a trial") ## Docker If you are already familiar with Docker, the easiest way to get started is by pulling our KrakenD image Pull KrakenD Enterprise  ``` $docker pull krakend/krakend-ee:2.13.6 ``` After this command you will have in your local computer the krakend enterprise container. Windows and Mac **Windows and Mac** users have support through Docker containers. ## Linux Copy the license certificate to the server(s). Replace `user@yourserver` with your actual username and IP address: ```bash ssh user@yourserver mkdir -p /etc/krakend/ scp LICENSE user@yourserver:/etc/krakend/LICENSE ``` ### CentOS/RockyLinux, Oracle Linux, and Redhat (yum) The installation process requires following these steps: 1. Install the repo package 2. Install the KrakenD package 3. Start the KrakenD service Paste this in the terminal: Yum based  ``` $rpm -Uvh https://download.krakend.io/rpm/krakend-ee-repo-2.1-0.x86_64.rpm yum install -y krakend-ee systemctl start krakend-ee ``` ### Fedora Paste this in the terminal: DNF based  ``` $rpm -Uvh https://download.krakend.io/rpm/krakend-ee-repo-2.1-0.x86_64.rpm dnf install -y krakend-ee systemctl start krakend-ee ``` The current KrakenD version will run at least in Centos 7 and Fedora 24 ### Debian and Ubuntu The installation process requires following these steps: 1. Add the key 2. Add the repo to the sources.list 3. Update your package list 4. Install the KrakenD service Bottom line: DEB based  ``` $apt install -y ca-certificates gnupg apt-key adv --keyserver keyserver.ubuntu.com --recv 5DE6FD698AD6FDD2 echo "deb https://download.krakend.io/apt stable main" | tee /etc/apt/sources.list.d/krakend.list apt-get update apt-get install -y krakend-ee ``` Minimum supported versions are Debian 8, and Ubuntu 16.x. ### Generic Linux (`tar.gz`) You can also download the `tar.gz` and decompress it anywhere. Instructions to check the SHA and PGP signature [here](/docs/overview/verifying-packages/). [Download latest `tar.gz` (AMD64)](https://download.krakend.io/bin/krakend-ee_2.13.6_amd64_generic-linux.tar.gz) [Download latest `tar.gz` (ARM64)](https://download.krakend.io/bin/krakend-ee_2.13.6_arm64_generic-linux.tar.gz) Once you have downloaded and uncompressed the binary, you should move the contents to each folder. You will need root permissions or add `sudo` to some of these commands: ```bash mv usr/bin/krakend /usr/local/bin/krakend mkdir -p /opt/krakend/ mv plugins /opt/krakend chmod +x /usr/local/bin/krakend ``` Now you can type `krakend`. To stop KrakenD graceful use any of these two commands: ```bash killall -s SIGTERM krakend killall -s SIGINT krakend ``` --- # [KrakenD API Gateway Installation Guide](https://www.krakend.io/docs/overview/installing/) KrakenD is a **single binary file** that does not require any external libraries to work. To install KrakenD choose your operative system in the downloads section or use the Docker image. [Download](/download/ "Download") [Generate a config file](https://designer.krakend.io/ "Generate a config file") ## Installation You can install (or just run) KrakenD using different options. Just exploring? Use the [KrakenD Playground](https://github.com/krakend/playground-community) if you want to play with KrakenD without configuring it. The Playground comes with several flavors of KrakenD and a mock API. Everything is ready to start playing, just do a `docker compose up`! ### Docker The easiest way to get started is by pulling and running the [KrakenD image](https://hub.docker.com/_/krakend) from the Docker Hub. Running KrakenD using the Docker container  ``` $docker run -p 8080:8080 -v $PWD:/etc/krakend/ krakend run --config /etc/krakend/krakend.json ``` If you choose not to mount the volume (the `-v`), a default `krakend.json` serving a `/__health` endpoint will be used. The volume expects to find a `krakend.json` in the current directory ([generate your first here](https://designer.krakend.io/)). ### AWS and Azure VM See the [downloads page](/download/) for pre-built virtual machines in Azure and AWS. ### Mac OS X The [Homebrew](https://brew.sh/) formula will download the source code, build the binary, and link the binary for you. The installation might take a while. Install on Mac via Brew  ``` $brew install krakend ``` After the installation completes go to [Using KrakenD](/docs/overview/run/) ### Linux #### CentOS, Oracle Linux, and Redhat (yum) The installation process requires following these steps: 1. Install the repo package 2. Install the KrakenD package 3. Start the KrakenD service Paste this in the terminal: Yum based  ``` $rpm -Uvh https://repo.krakend.io/rpm/krakend-repo-0.2-0.x86_64.rpm yum install -y krakend systemctl start krakend ``` #### Fedora Paste this in the terminal: DNF based  ``` $rpm -Uvh https://repo.krakend.io/rpm/krakend-repo-0.2-0.x86_64.rpm dnf install -y krakend systemctl start krakend ``` The current KrakenD version will run at least in Centos 7 and Fedora 24 #### Debian and Ubuntu The installation process requires following these steps: 1. Add the key 2. Add the repo to the sources.list 3. Update your package list 4. Install the KrakenD service Bottom line: DEB based  ``` $apt install -y ca-certificates gnupg apt-key adv --keyserver keyserver.ubuntu.com --recv 5DE6FD698AD6FDD2 echo "deb https://repo.krakend.io/apt stable main" | tee /etc/apt/sources.list.d/krakend.list apt-get update apt-get install -y krakend ``` Minimum supported versions are Debian 8, and Ubuntu 16.x. #### Generic Linux (`tar.gz`) You can also [download](/download/) the `tar.gz` and decompress it anywhere. Instructions to check the SHA and PGP signature [here](/docs/overview/verifying-packages/). ## Compile from source As KrakenD is [open source](/open-source/) you can opt for [building the binary](https://github.com/krakend/krakend-ce). The binary you will produce is the same you can get in our download page, only that compiling it yourself always feels good! --- # [Running KrakenD Enterprise: Available commands (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/overview/run/) After installing KrakenD Enterprise, you can start using it by typing `krakend help`: The krakend command  ```` $krakend ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.6-ee KrakenD is a high-performance API gateway that helps you publish, secure, control, and monitor your services Usage: krakend [command] Available Commands: audit Audits a KrakenD configuration. check Validates that the configuration file is valid. check-plugin DEPRECATED: Use the new plugin command. Checks your plugin dependencies are compatible. e2e Executes an end to end test for the gateway based on the configuration file and a set of specs. generate Generates static content for several functionalities. See subcommands. help Help about any command license Checks license status openapi Import, export and serve OpenAPI specifications. See subcommands. plugin Plugin operations. See subcommands. revoker Starts a revocation service. run Runs the KrakenD server. test-plugin DEPRECATED: Use the new plugin command. Tests that one or more plugins are loadable into KrakenD. version Shows KrakenD version. Flags: -h, --help help for krakend Use "krakend [command] --help" for more information about a command. ```` To see all the options of a command type `krakend help ` or `krakend -h`. The commands are: - `krakend audit`: Use [krakend audit](/docs/enterprise/configuration/audit/) to get security recommendations for a given configuration. - `krakend check`: Use [krakend check](/docs/enterprise/configuration/structure/) to make sure the configuration file you have generated is not broken and has the required attributes to start the gateway. - `krakend plugin`: Different commands to [generate plugins](/docs/enterprise/extending/generating-plugins/), [check their dependencies](/docs/enterprise/extending/check-plugin/), and [test them](/docs/enterprise/extending/test-plugin/) - `krakend e2e`: Use the [end to end command](/docs/enterprise/developer/integration-tests/) to run integration tests. - `krakend generate`: Use the generate command to transform from and to different formats like [Postman](/docs/enterprise/developer/postman/). - `krakend license`: Use the [license command](/docs/enterprise/overview/license-file/) to integrate license checks in your pipeline and avoid service disruptions - `krakend openapi`: Use the openapi command to import and export [OpenAPI](/docs/enterprise/developer/openapi/). - `krakend revoker`: Use the revoker command to start the [Revoke Server](/docs/enterprise/authentication/revoke-server/). - `krakend run`: Use run to start the API gateway server. - `krakend version`: Use the version command to print the current KrakenD version and the Glibc and Go versions used during compilation. To start the server, invoke the `krakend run` command. The command will require a configuration file with your API definition. You can create your first `krakend.json` file using the [KrakenDesigner](https://designer.krakend.io/) if you prefer a UI. Make sure KrakenD can find a `krakend.json` and a `LICENSE` file in the same place where it is executed (workdir). To get started right away, you can paste the following content inside a `krakend.json` file: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3 } ``` And then you can start KrakenD: Command to start KrakenD  ``` $krakend run -c krakend.json ``` Or if you use Docker: Running KrakenD using the Docker container  ``` $docker run -p 8080:8080 -v "$PWD:/etc/krakend/" krakend/krakend-ee:2.13 run --config /etc/krakend/krakend.json ``` Now KrakenD is listening on `8080`, and you can see it working under `http://localhost:8080/__health`. --- # [Running KrakenD server](https://www.krakend.io/docs/overview/run/) After installing KrakenD, you can start using KrakenD by typing `krakend`. To see all the options of `krakend`, type `krakend -h` or `krakend -h`. For instance, the `krakend run` help is: Run command help  ```` $krakend run -h ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.8 The API Gateway builder Usage: krakend [command] Available Commands: audit Checks the integrity of the config and returns security recommendations. check Validates that the configuration file is valid. check-plugin Check the compatibility with the plugin deps. help Help about any command run Run the KrakenD server. version Shows KrakenD version. Flags: -c, --config string Path to the configuration filename -d, --debug Enable the debug -h, --help help for krakend Use "krakend [command] --help" for more information about a command. ```` You can use the following commands: - `krakend audit`: Use [krakend audit](/docs/configuration/audit/) to get security recommendations for a given configuration. - `krakend check`: Use [krakend check](/docs/configuration/structure/) to make sure the configuration file you have generated is not broken and has the required attributes to start the gateway. - `krakend check-plugin`: Use the [check-plugin](/docs/extending/check-plugin/) when you are developing custom plugins and you want to check that they are compatible with the server. - `krakend run`: Use run to start the API gateway server. - `krakend version`: Use the version command to print the current KrakenD version and the Glibc and Go versions used during compilation. ## Starting the gateway server To start the server, invoke the `krakend run` command with a configuration file containing your API definition. You can visually create your first `krakend.json` file using the [KrakenDesigner](https://designer.krakend.io/) if you prefer a UI. Or to get started right away, you can paste the following content inside a `krakend.json` file: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3 } ``` And then you can start KrakenD: Command to start KrakenD  ``` $krakend run -c krakend.json ``` Or if you use Docker: Command to start KrakenD with Docker  ``` $docker run -p "8080:8080" -v $PWD:/etc/krakend/ krakend:2.13.8 run -c krakend.json ``` Now KrakenD is listening on `8080`, and you can see it working under `http://localhost:8080/__health`. --- # [Managing the LICENSE file (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/overview/license-file/) To run the Enterprise software, whether it is API Gateway or AI Gateway functionality, **you must provide a valid license**. Single license for all products KrakenD distributes the **API Gateway** and the **AI Gateway** under the same binary, and the `LICENSE` file you load during startup entitles you to use one product or both. If you see an error in the logs for unlicensed content, reach out to [sales](/contact-sales/). ## Where is the license? The sales team sends the license file via email, containing a download link. Be sure to save the file(s) in a secure location. The file is named `LICENSE` (no extension) for the **production license**, and `LICENSE_DEV` for **non-production** environments (trials receive a single file). ## What happens if my LICENSE is wrong? When the license file is expired, incorrect, or missing, KrakenD **won’t start**. If KrakenD is running and the license expires, the service **shuts down**. It is important to have your license up to date and take the necessary measures to ensure its successful rotation. You can run `krakend license` to see if the installed `LICENSE` file is valid. ## License requirements As long as you **don’t change the license content** (e.g., copy/paste partially), everything will be fine. Avoid copying and pasting the content, as you might leave a single character out, and nothing will work. The `LICENSE` file: - Uses the default path `/etc/krakend/LICENSE` and must be readable by the KrakenD process. - It is a plain text file containing the certificate we gave you (it looks like a PEM file) - Includes the necessary surrounding `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` literals. - Accepts loading from a different path, filename (e.g., the development license LICENSE\_DEV), or environment variable **when specified**. See below. ## Checking the license validity Automate license validation To avoid problems, ensure that you automate periodic license validation so its expiration does not go unnoticed. Our sales team reaches out before that happens, but it’s better to be safe than sorry! The command `krakend license` and several of its subcommands help you validate the installed `LICENSE` file. Use `krakend license --help` for the complete list of available subcommands and help. **Add this command to your automated development workflow** to be warned when the license is about to expire. The `krakend license` command exits either with a: - **non-zero status code** and a `KO:` message string when the license is not valid or - **zero status code** and an `OK:` string when the file is valid in the requested time. All license files start and end on the agreed dates at `00:00 UTC`. For example, to check the expiration date of your license, you can do the following: License is valid until...  ``` $krakend license valid-until OK: License is valid until 2024-02-01 00:00:00 +0000 UTC ``` Or to find out the relative time: License is valid for...  ``` $krakend license valid-for OK: License is valid for the next 71 days, 6 hours, and 1 minutes ``` The following additional commands will help you check the license. #### Is the LICENSE valid right now? Execute the following command to know if your license is valid right now (although it could be invalid in the next second): Is the license valid?  ``` $krakend license OK: License is still valid ``` You can also check an arbitrary file name by passing the `--license` flag. Is a custom license valid?  ``` $krakend license --license /path/to/devel_license OK: License is still valid ``` When the license is invalid, you’ll see the reason: Example of invalid license  ``` $krakend license KO: x509: certificate has expired or is not yet valid: current time 2023-11-16T15:24:48+01:00 is after 2023-08-31T00:00:00Z ``` ### KrakenD Licensee For a valid license, you can retrieve who is the **licensee**: Licensee information  ``` $krakend license licensee ACME Inc. ``` For an invalid license, you won’t have any information. ### License info To get a verbose version of the license capabilities, environment, and entitlements, you can spawn an **info** command: License information  ``` $krakend license info Production license for Foo Inc. [AI Gateway + Agent Gateway + API Gateway], valid until 2026-04-02 ``` ### Days left Similarly, you can get the **remaining days** of a license as follows: License days left  ``` $krakend license days-left OK: License has 71 days remaining ``` ### Automated license expiration checking Because the command uses exit codes, you can include the `license` command in your automation and **abort a pipeline when the license is expired or about to expire**. The command can check a license now or at a time in the future. Use the subcommand `valid-for` to determine if the license will remain valid after many hours or days. Use a numeric followed by `h` or `d` and no spaces. For instance: License invalid in 3 months  ``` $krakend license valid-for 90d KO: License will be invalid in 71 days, 5 hours, and 49 minutes ``` Checking a relative date is probably the most convenient way to set this up quickly in a pipeline, but you can also check an absolute date using `valid-until`. For instance: Will the license be valid on a specific date?  ``` $krakend license valid-until 2024-11-10 KO: License will be invalid before 2024-11-10 ``` Notice that the date format is `yyyy-mm-dd`. ### The LICENSE file is a certificate Because our `LICENSE` file is a regular certificate, you can also use `openssl` to play with it. For instance, you can execute `openssl x509 -in LICENSE -text -noout` to get all the information. ## Change the location of the LICENSE or use a non-production LICENSE\_DEV The production `LICENSE` file is automatically loaded from `/etc/krakend/LICENSE`, although you can use a different type of license, rename it, or change its location using three different methods: 1. Set an environment variable `KRAKEND_LICENSE_PATH` pointing to the desired file. For instance, `KRAKEND_LICENSE_PATH=/path/to/prod_license.crt`. As long as the content inside is valid, the path or the extension you set (if any) does not matter. 2. Taking the content of the license from the `KRAKEND_LICENSE_BASE64` environment var (see below) 3. Passing the `--license` or `-l` flags when running the application. For instance: Start with a development license  ``` $krakend run -c krakend.json --license ./LICENSE_DEV ``` The flag `--license` takes precedence if you also set the environment variable `KRAKEND_LICENSE_PATH`. ## Saving the license in the KRAKEND\_LICENSE\_BASE64 environment variable Another possibility to pass the license to KrakenD is to save its contents in the `KRAKEND_LICENSE_BASE64` environment variable, encoded in base64. For instance: Passing the LICENSE content in an env var  ``` $KRAKEND_LICENSE_BASE64="$(base64 LICENSE)" krakend run -c krakend.json ``` When you use this method, you must ensure that the content of the license is encoded correctly. **When decoded** in base64, it must contain byte-for-byte the same content of the LICENSE file. If you declare both the `KRAKEND_LICENSE_BASE64` and the `--license` flag, the **environment variable always takes precedence**. ## How the license path is resolved If you don’t change the license location using the environment variable or flag, KrakenD internally searches for `./LICENSE`. Because KrakenD’s [working directory](/docs/enterprise/configuration/working-directory/) is `/etc/krakend`, the file is resolved to `/etc/krakend/LICENSE`. If, for any reason, you change the container’s working directory, you must update the `LICENSE` file to reflect the exact new location. For instance, if you have a custom `Dockerfile` like this one: ```Dockerfile FROM krakend/krakend-ee:2.13 WORKDIR /vault COPY LICENSE . ``` In this example, when you change the working directory, all relative paths move to the new base directory. When KrakenD starts, it loads the license from `/vault/LICENSE` (as well as any other external file referenced in the configuration relatively). ## Storing the LICENSE in the Docker image See [Generating a Docker artifact](/docs/enterprise/deploying/docker/) ## Storing the LICENSE in Secret managers You can store your LICENSE in any secret manager of your choice if you want to. However, ensure the line breaks are respected when retrieving them again. You must dump the contents of the secret in a `LICENSE` file somewhere in the container. ### AWS Secrets Manager One of the particularities of AWS Secrets Manager is that it may encounter issues when storing **multiline content**. Therefore, we recommend keeping the `LICENSE` content in `base64` format when working with AWS in plain text. You can create an AWS secret with your license with this command: Term  ``` $aws secretsmanager create-secret --name krakend-valid_trough-2023_05_22 --secret-string "$(base64 LICENSE)" { "Name": "krakend-valid_trough-2026_05_22", "ARN": "arn:aws:secretsmanager:eu-west-1:052351007912:secret:krakend-valid_trough-2026_05_22-OISgD6", "VersionId": "59f7c317-989a-4219-bdd2-b79bead69dd4" } ``` Notice that we have added the license’s expiration date to the secret name. A practice like this will help you quickly realize the expiration date of any license without testing. You can later retrieve the license file in your pipeline file with: Decoding the base64 license  ``` $aws secretsmanager get-secret-value --secret-id krakend-valid_trough-2026_05_22 --query 'SecretString' --output text | base64 -d > LICENSE ``` *Note*: Depending on your computer’s operating system, the decoding of base64 might be `base64 -D` instead of `base64 -d` ## What happens when the license expires? **Any running processes will shut down** when the KrakenD Enterprise license expires. The KrakenD sales team is always in touch before this happens to work on the next renewal. Suppose you no longer want to be an Enterprise customer. In that case, you can [downgrade to KrakenD Community](/docs/enterprise/faq/switch-versions/#downgrade-to-community) very quickly and run in the open-source mode without the Enterprise functionalities. ## Updating your license Replacing the file is not enough To update the license, **restart or redeploy** the service to ensure it uses the new content. When your license renewal comes, you must replace the file in the place and format you have decided, such as the `/etc/krakend/LICENSE`, another filename you decided, or the content of the `KRAKEND_LICENSE_BASE64`variable. Whichever is the case, you **must restart or redeploy** KrakenD for the changes to take effect. That’s all! ## Development licenses A developer license lets you test functionality during development, but it is limited in terms of throughput, as its performance degrades exponentially as you demand more throughput. This type of instance is not suitable for load testing or production workloads. In that case, you must use the production license. The limitations are: - The logger level is forced to `DEBUG`, regardless of the `telemetry/logging` configuration. - A `WARNING` log line is triggered at startup and each minute after that. The Developer license gives you full access to all Enterprise features—including API and AI Gateway capabilities—so you can build, integrate, and validate your implementation without any functional restrictions. This license is designed for development and proof-of-concept work that does not justify a full license yet. Need to stress-test before going live? Just reach out, we’ll provide a temporary performance-testing license at no extra cost. --- # [API Configuration Designer](https://www.krakend.io/docs/configuration/designer/) The [Designer](https://designer.krakend.io) is a UI that allows you to create KrakenD configurations from scratch or resume editing an existing one. It is a tool very useful in your **early contact with KrakenD**, as it helps you try functionalities without having to learn the different attributes of the configuration. **The designer is a learning tool** more than an administration one. KrakenD configuration and administration is designed with **GitOps** in mind (under the version control system and released through CI/CD). Combined with a [`:watch` container](/docs/developer/hot-reload/), you can **apply configuration changes automatically** in a development environment. [Open designer](https://designer.krakend.io "Open designer") ## Automatically applying changes to KrakenD Suppose you have a **Chrome, Edge, or Opera** desktop browser. In that case, you can have the whole experience of editing in the browser and see the changes applied to your local development container without doing anything else. The Designer uses experimental browser technology for file saving While you can use the Designer in any major browser for editing files and downloading a copy, some experimental [browser capabilities](https://developer.mozilla.org/en-US/docs/Web/API/Window/showOpenFilePicker#browser_compatibility) allow you to open local files and **apply changes automatically on a [KrakenD Watch](/docs/developer/hot-reload/) server** by simply using the web. To use this, you need to: - Start a container with the `:watch` tag - Edit in the browser the file you have mounted in the volume. From here, **every save will automatically apply the changes on KrakenD**. You’ll see a warning in the dashboard when your browser is not supported, or you are using a local copy without HTTPS. The first time you attempt to save a file you have loaded from the disk, the browser will ask permission. ### Example of hot reload after browser change Suppose you don’t have an initial configuration. In that case, you can generate an initial one by clicking *Download* on the Designer without needing to configure anything yet, or you can paste this inside a new file, `krakend.json` instead: ```json { "version": 3 } ``` Now that you have a fresh `krakend.json`, add a local `docker-compose.yaml` like this in the same folder if you are going to plug KrakenD into other containers locally: ```yml version: "3" services: krakend: image: krakend:watch volumes: - ".:/etc/krakend" ports: - "8080:8080" command: [ "run", "-dc", "krakend.json" ] ``` Or do a `docker run` if you don’t want a Docker compose: ```bash docker run -it --rm -v "$PWD:/etc/krakend" krakend:watch run -dc krakend.json ``` You can check that KrakenD is running by visiting its [health endpoint](/docs/service-settings/health/): http://localhost:8080/\_\_health Once KrakenD runs, the watcher follows changes happening in this folder. If you edit the file by hand, it will reload the new changes. But if you *Open* this file on the Designer, and save it, it will do it as well. ## Supported features As KrakenD supports hundreds of features, it might be overwhelming to review all the documentation. Therefore, a tool that allows you to play in the browser is beneficial. **The Designer supports *almost all* the functionality**, although advanced functionalities aren’t in the interface. In any case, when this happens, even if you don’t see them in the interface, they are kept in the final save if you loaded them. The Designer does not support [flexible configuration](/docs/configuration/flexible-config/), as the browser cannot render Go templates of a complex directory structure. The Designer supports editing Enterprise and Community features simultaneously. When you enable a single Enterprise feature, you will see a badge informing you about it. --- # [KrakenD API Gateway Enterprise Playground (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/overview/playground/) The [KrakenD Enterprise Playground](https://github.com/krakend/playground-enterprise) is a Docker Compose demonstration environment that puts together the necessary pieces to get you started with our API Gateway, using example use-cases. As KrakenD is an API gateway, we have added surrounding services: - Internal and third-party services that feed the gateway - Authentication/authorization examples, including JWT token-based authentication with Auth0 integration, a JWT token revoker, API-Key-based authentication, and basic authentication. - Static assets to test the by KrakenD Static Server based on disk content - Integrations with Grafana+Influx (metrics), ELK (log storing and visualization), and Jaeger (tracing). ## An environment ready to use! The KrakenD Enterprise Playground can be managed easily using customized `make` commands. Once up & running, you can fire up your browser, `curl`, Postman, httpie or anything else you use to interact with the services. Start the stack included in docker compose  ``` $ make start ``` Follow the KrakenD logs after the complete stack is up & running  ``` $ make logs ``` Shut down the complete stack, removing all the volumes  ``` $ make stop ``` Have a look at the `docker-compose.yml` file to understand how these services interact together, and also at the `krakend.json` file to find out what endpoints are exposed. If you’d like to see more examples, please let us know or open an [issue](https://github.com/krakend/playground-enterprise/issues)! --- # [KrakenD API Gateway Playground](https://www.krakend.io/docs/overview/playground/) If you are new to KrakenD, a quick way to get started is to make use of the [KrakenD Playground](https://github.com/krakend/playground-community). The KrakenD Playground is a **Docker Compose** environment that puts together the necessary pieces to let you play with KrakenD in a working environment. As KrakenD is an API gateway, we have also added to the environment an API (backend) to feed the gateway and a website to make use of the data. With the KrakenD Playground, you can see the different pieces that take place in the API Gateway use cases. ## An environment ready to use! The KrakenD Playground can be started with `docker compose up` and then fire up your browser, `curl`, Postman, httpie or anything else you use to interact with the services. When starting the Docker compose you will have: - The KrakenD API Gateway, on port 8080 - A client consuming data from the gateway, on port 3000 - A backend simulating API responses on port 8000 - Plus a [Jaeger](https://www.jaegertracing.io/) where traces are sent on port 16686 Have a look at the `docker-compose.yml` file to understand how these services interact together, and also at the `krakend.json` file to find out what endpoints are exposed. If you’d like to see more examples, please let us know or open an [issue](https://github.com/krakend/playground-community/issues)! --- # [Comprehensive Guides for KrakenD API Gateway](https://www.krakend.io/docs/overview/guides/) The documentation in this site covers all the functionality of KrakenD API Gateway, but over time we have received from KrakenD users all kinds of tutorials, guides and other resources that illustrate how to do a specific thing. We have included below a few links, but you can share yours. [Add a link](https://github.com/krakend/krakend-documentation/edit/master/overview/guides.md "Add a link") ## User-contributed resources - [Getting started with KrakenD on Kubernetes / AKS](https://partlycloudy.blog/2021/02/17/getting-started-with-krakend-on-kubernetes-aks/) by Christian Dennig (Cloud Solution Architect @ Microsoft) - [Build and Deploy with Kubernetes, Helm, and Skaffold](https://betterprogramming.pub/maintainability-in-the-face-of-api-complexity-e23531e2d322) by Fabijan Bajo (Technical Lead, DevOps / Cloud Infrastructure @ IBM) - [Microservices with Micronaut, KrakenD and Consul](https://piotrminkowski.com/2021/02/23/microservices-with-micronaut-krakend-and-consul/) by Piotr Minkowski (Solution Architect @ Red Hat) - [How to Successfully Set Up KrakenD on GKE: A Step by Step Guide](https://hackernoon.com/how-to-successfully-set-up-krakend-on-gke-a-step-by-step-guide-ny22311c) by Vijay Savanth on Hackernoon - [Writing Plugins using Golang](https://www.eventslooped.com/posts/krakend-writing-plugins/) by Ilya Nemtsev on Eventslooped - [Building Micro Services with .Net Core + Krakend API Gateway](https://balramchavan.medium.com/building-micro-services-with-net-core-krakend-api-gateway-fad036f10298) by Balram Chavan (Engineer @Boehringer Ingelheim) - [How To Effortlessly Adopt Microservices Using KrakenD](https://betterprogramming.pub/how-to-effortlessly-adopt-microservices-using-krakend-28c265573877) by Kirshi Yin on BetterProgramming - [Using a proxy and API gateway to optimize Compass for performance](https://blog.hcltechsw.com/compass/using-a-proxy-and-api-gateway-to-optimize-compass-for-performance/) by Michael Hudson (Technology Director @HCL Software) - [Configuring Grafana and InfluxDB](https://skalena.github.io/api-methodology/public/cont/krakend-grafana-influxdb/) by Skalena (KrakenD partner, Brazil) - [API Gateway na prática com KrakenD - Video in portuguese](https://www.youtube.com/watch?v=ni-rti5DgYY) by Wesley Willians (Full Cycle) - [Publish Messages to Google PubSub Topic with KrakenD APIGateway as REST API and Subscribe using SpringBoot](https://medium.com/@velmuruganmp1210/publish-messages-to-google-pubsub-topic-with-krakend-apigateway-as-rest-api-and-subscribe-using-75fb875f2f2f) by Velmurugan M. (Solutions Architect) If you have written a tutorial, guide, video or any other material worth sharing with the community, let us know. --- # [Configuration Guide for KrakenD API Gateway](https://www.krakend.io/docs/configuration/) All the setup a KrakenD server needs to operate is a single configuration file. This file is referred to as `krakend.json` throughout all the documentation. The name `krakend.json` is simply an **alias**, a convention that we use everywhere. Your real configuration file can have any name, multiple formats (such as YAML or TOML), be stored anywhere, or be **split into numerous pieces**. With this simple configuration mechanism in place, **versioning and automation are very convenient**. Any change to the API Gateway or the AI Gateway is always under version control, and the code controls the state of the gateway. Configuration using multiple files If your configuration file is too large or repetitive, or is maintained by many people, it can be split into several files using a templating system. See the [flexible configuration documentation](/docs/configuration/flexible-config/) for more information. ## Generating the configuration file(s) The configuration file is how you provide instructions to KrakenD. To start your first KrakenD configuration, you can: - Use the [KrakenD Playground](/docs/overview/playground/) and start editing its configuration from a working environment with several use-case examples. - Use the online configuration editor [KrakenDesigner](/docs/configuration/designer/). - Start a file from scratch or from a template The [KrakenDesigner](/docs/configuration/designer/) is a simple javascript application that helps you understand some of the capabilities of the API Gateway and helps you set the different values for all the different options. Using this option, you don’t need to learn and write from scratch all the attribute names. You can download the configuration file at any time and load it to resume editing. The Kraken Designer is a **pure static** page that **does not send any of your configuration elsewhere**. [Generate configuration now](https://designer.krakend.io/ "Generate configuration now") To start editing from scratch, use a [modern IDE with JSON Schema integration](/docs/developer/ide-integration/), and play with the autocomplete! Here’s a starting point: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "debug_endpoint": true, "echo_endpoint": true, "endpoints": [ { "endpoint": "/test", "backend": [ { "@comment": "Connect KrakenD to KrakenD itself as backend!", "host": [ "http://localhost:8080" ], "url_pattern": "/__echo/test" } ] } ] } ``` Start KrakenD and connect to `http://localhost:8080/test` or `http://localhost:8080/__health` now. ## Understanding the configuration parts Whether you choose to start with the KrakenDesigner, a template, or from scratch, we recommend that you understand the configuration structure and edit the file(s) by hand as soon as possible. Always [run the linter](/docs/configuration/check/) after you finish editing to make sure you have a valid configuration. Read the [introduction to the configuration file](/docs/configuration/structure/) to understand the different parts of the configuration. ## Supported file formats Throughout all the documentation, we refer to the configuration file as the `krakend.json` file; however, you can also express the configuration file using `.json`, `.toml`, or `.yaml` extensions. For more information and recommendations, see [supported file formats](/docs/configuration/supported-formats/). ## Validating the syntax of the configuration file Validate the syntax (not the logic) of your configuration file using the `krakend check` command: Check the configuration  ``` $krakend check --lint --config ./krakend.toml --debug ``` When the syntax is correct, you’ll see the message `Syntax OK!`; otherwise, the error is displayed. You can, of course, start the service directly without checking, and KrakenD will do everything possible to start the service (even if there are configuration inconsistencies, at your own risk), but **never send a configuration to production without passing the lint first** Read more about [`krakend check`](/docs/configuration/check/) --- # [Configuration Structure](https://www.krakend.io/docs/configuration/structure/) All KrakenD behavior depends on its configuration file(s). You’ll find it referenced through all this documentation and for simplicity as the `krakend.json`, although the configuration [supports formats other than JSON](/docs/configuration/supported-formats/) and it can be described [using multiple files and templates](/docs/configuration/flexible-config/). Being familiar with its structure it’s essential. The correctness of a configuration file is determined by the [check](/docs/configuration/check/) and [audit](/docs/configuration/audit/) commands using different perspectives. ## 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: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "endpoints": [], "extra_config": {} } ``` - `$schema`: *Optional* but recommended. When added, enables [IDE integration](/docs/developer/ide-integration/) with autocompletion and documentation. Defines the JSON schema to validate your configuration. The KrakenD linting process (`krakend check --lint`) does not read this property as it knows which version must use on runtime. - `version` (*mandatory*): The version of the configuration file format (not the version of KrakenD). - Format version `3`: **Current** (since `v2.0`) - Format version `2`: Deprecated in 2022, for versions between `v0.4` and `v1.4.1` - Format version `1`: Deprecated in 2016, for versions `v0.3.9` and older. - `endpoints[]`: An array of [endpoint objects](/docs/endpoints/) offered by the gateway and all the associated backends and configurations. This is your API definition. - `extra_config{}`: Service components’ configuration. Whatever is not a core functionality of the [Lura Project](https://luraproject.org) is declared in a unique **namespace** (a key) in the configuration, so that you can configure multiple elements without collisions. ### The `endpoints` structure Inside the `endpoints`, you declare an array with [endpoint objects](/docs/endpoints/). Every object has an `endpoint` (the URL) the gateway offers to users. For each endpoint, you need to declare at least one `backend` (the data origin). It looks like this: ```json { "endpoints": [ { "endpoint": "/v1/foo-bar", "backend": [ { "url_pattern": "/foo", "host": [ "https://my.foo-api.com" ] }, { "url_pattern": "/bar", "host": [ "https://my.bar-api.com" ] } ] } ] } ``` The configuration above declares an endpoint `/v1/foo-bar`, which returns the result of merging the responses from two backends `https://my.foo-api.com/foo` and `https://my.bar-api.com/bar`. These two calls execute in parallel. That’s the basic structure of endpoints; for more information see [how to create endpoints](/docs/endpoints/). ### The `extra_config` structure KrakenD is very modular and comes bundled with many components that extend the core functionality of the [Lura Project](https://luraproject.org). The `extra_config` stores each component configuration that is not handled by Lura itself. Components declare in their source code a **unique namespace**. KrakenD registers the component during the startup, and it passes to the component the configuration found under a key matching the **namespace** inside the `extra_config` object. ```json { "extra_config": { "component-1-namespace": { "some": "config" }, "component-2-namespace": { } } } ``` For instance, the [extended logging component](/docs/logging/) uses the **namespace** `telemetry/logging`: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "WARNING", "prefix": "[KRAKEND]", "stdout": true } } } ``` ### Placements for the `extra_config` The `extra_config` can appear in the root of the file and on other placements (or levels) as well. It depends entirely on **the scope** of every component and the nature of its functionality. An `extra_config` in the **root level** usually sets functionalities with a **service scope**: these influence the gateway globally and on every request (e.g., metrics). On the other hand, `extra_config` placed more profound in the configuration affects a tinier scope. An example could be a configuration that is loaded when a certain endpoint is called. All components will seek the `extra_config` in its defined scope. The possible placements of the `extra_config` are: - `service` (root level) - `endpoint` - `backend` For instance, you might want to set a [rate limit](/docs/throttling/) between a user and a `/my-rate-limited` endpoint in KrakenD. And for that, you would place the `extra_config` inside that `endpoint` scope. Or you might want to limit the connections between a KrakenD endpoint against your services; then you would place the `extra_config` in the `backend` scope. **You don’t have to guess where to put the `extra_config`** . Each component has in the documentation what is the scope(s) is built for. ### Example The following code is an example defining two simultaneous [rate limiting strategies](/docs/throttling/): A limit of 5000 reqs/second for a specific endpoint, but yet, one of its backends accepts a maximum of 100 reqs/s. When the backend limit is reached, the user will have partial responses, and when both are surpassed the user won’t have data from any of the backends. Notice how `extra_config` is present in the endpoints and backend scopes. ```json { "version": 3, "endpoints": [ { "endpoint": "/limited-to-5000-per-second", "extra_config": { "qos/ratelimit/router": { "max_rate": 5000 } }, "backend": [ { "host": [ "http://slow.backend.com/" ], "url_pattern": "/slow/endpoint", "extra_config": { "qos/ratelimit/proxy": { "max_rate": 100, "capacity": 1 } } }, { "host": [ "http://fast.backend.com/" ], "url_pattern": "/fast/endpoint" } ] } ] } ``` For larger sample files with more options you can have a look a the [KrakenD Playground](/docs/overview/playground/). --- # [Validating the configuration with `check`](https://www.krakend.io/docs/configuration/check/) The `krakend check` command **validates KrakenD configuration files** written in any of its [supported formats](/docs/configuration/supported-formats/). It’s able to perform three things: - **Syntax validation** - For any format (`.yml`, `.json`, `.toml`, etc) - **Linting** - Besides checking that the file isn’t malformed, the linter checks your config exhaustively against KrakenD’s official schema ([repo](https://github.com/krakend/krakend-schema)) to detect wrong types, unknown attributes, or misplaced components. Only available when you work with `JSON` formats. - **Testing** - It tests a run of the service to catch problems that are not strictly related to linting but to the runtime. For instance, you could declare a colliding endpoint (two endpoints in the same route), and the syntax would validate and lint, yet the configuration would be impossible to run. **The `check` command can guarantee that a configuration is valid with the three validations**. **TL;DR**: Add the following line before deploying (online validation of the schema): Online schema validation  ``` $krakend check -tlc krakend.json ``` Or for offline validation: Offline schema validation  ``` $krakend check -tnc krakend.json ``` See the usage below. ## Checking the configuration The `krakend check` command accepts the following options: Usage of KrakenD check  ```` $krakend check --help ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.8 Validates that the active configuration file has a valid syntax to run the service. Change the configuration file by using the --config flag Usage: krakend check [flags] Aliases: check, validate Examples: krakend check -d -c config.json Flags: -c, --config string Path to the configuration file -d, --debug count Information about how KrakenD is interpreting your configuration file -h, --help help for check -i, --indent string Indentation of the check dump (default "\t") -l, --lint Enables the linting against the official KrakenD online JSON schema -n, --lint-no-network Lint against the builtin Krakend JSON schema, no network is required -s, --lint-schema string Lint against a custom schema path or URL -t, --test-gin-routes Tests the endpoint patterns against a real gin router on the selected port ```` ## Flags Use `krakend check` in combination with the following flags: - `-c` or `--config` to specify the path to the configuration file in any of the [supported formats](/docs/configuration/supported-formats/), or to the starting template if used in combination with flexible configuration. - `-d` or `--debug` (*optional*) to enable the debug and see information about how KrakenD is interpreting your configuration file. Use from 1 to 3 levels of verbosity using `-d`, `-dd`, or `-ddd`. - `-l` or `--lint` (*optional*) to check that your configuration file is properly linted and does not contain unrecognized options or wrong types. This option downloads the schema from `https://www.krakend.io/schema/v2.13/krakend.json` every time (each version uses its schema), but use the `-n` flag to use the embedded schema (see below). **This flag is the most important one** and you should always have it in your pipeline. - `-i` or `--indent` (*optional*) in combination with `-d`, to change the indentation when the debug information renders (default: `TAB`). E.g.: `-i "#"` uses a hash instead of a tab for every nesting level. - `-n` or `--lint-no-network` (*optional*) will use the embedded schema to validate your configuration, instead of downloading the schema from the Internet. This option produces faster validation. The online schema rarely updates and it’s only done when there is a bug in the definition of an attribute. - `-s`, `--lint-schema` (*optional*). Lint against a custom schema path or URL you pass to the command. This option is useful if you develop your own plugins and still want to validate your own schema with your extended configuration. - `-t` or `--test-gin-routes` (*optional*) to test the configuration by trying to start the service for a second. This option is highly recommended as it prevents conflicting routes and other problems unrelated to the linting itself and would end up in a *panic*. Use –lint to do strict parsing The command `krakend run` will run any syntax-valid file, **ignoring unknown configuration keys**. Use the `--lint` flag in the check command to find incorrect entries. ## Debugging your configuration You can use three different verbosity levels with the `--debug` (or `-d`) flag. The levels are `-d`, `-dd`, and `-ddd`. When used a single time, you get the most relevant information after parsing the configuration, when you add more, you get more and more details. The following example shows the debug of a configuration with one endpoint: Checking the configuration with the debug flag  ``` $krakend check -t --lint -d -c krakend.json Parsing configuration file: krakend.json Global settings Name: My lovely gateway Port: 8080 4 global component configuration(s): - security/bot-detector - security/cors - telemetry/logging - telemetry/metrics 1 API endpoints: - GET /cel/req-resp/:id Timeout: 3s 1 endpoint component configuration(s): - validation/cel Connecting to 2 backend(s): [+] GET /__debug/{{.Id}} Timeout: 3s Hosts: [http://127.0.0.1:8080] 1 backend component configuration(s): - validation/cel [+] GET /__debug/{{.Id}} Timeout: 3s Hosts: [http://127.0.0.1:8080] 1 backend component configuration(s): - validation/cel 1 async agent(s): - cool-agent 1 agent component configuration(s): - github.com/devopsfaith/krakend-amqp/agent Connecting to 1 backend(s): [+] POST /__debug/cool-agent Timeout: 3s Hosts: [http://127.0.0.1:8080] Syntax OK! ``` The same example with the verbosity level 2 (`-dd`) adds more information in the global settings (like the TLS section) and shows the configuration of the extra\_config. The endpoints and the backends also show more information: Checking the configuration with the debug flag  ``` $Parsing configuration file: krakend.json Global settings Name: My lovely gateway Port: 8080 Default cache TTL: 1h0m0s Default timeout: 3s Default backend hosts: [] No TLS section defined No Plugin section defined 4 global component configuration(s): - security/bot-detector deny: [a b] patterns: [(Pingdom.com_bot_version_).* (facebookexternalhit)/.*] allow: [c Pingdom.com_bot_version_1.1] - security/cors expose_headers: [Content-Length] allow_origins: [*] max_age: 12h allow_methods: [POST GET] allow_headers: [Origin Authorization Content-Type] - telemetry/logging prefix: [KRAKEND] stdout: true level: DEBUG syslog: false - telemetry/metrics listen_address: :8090 collection_time: 60s 1 API endpoints: - GET /example/:id Timeout: 3s QueryString: [q id] CacheTTL: 1h0m0s Headers to pass: [X-Header] OutputEncoding: json Concurrent calls: 1 1 endpoint component configuration(s): - validation/cel [map[check_expr:'something' in req_headers['X-Header']] map[check_expr:int(req_params.Id) % 2 == 0]] Connecting to 2 backend(s): [+] GET /__debug/{{.Id}} Timeout: 3s Hosts: [http://127.0.0.1:8080] Concurrent calls: 1 Host sanitization disabled: false Target: Deny: [], Allow: [] Mapping: map[] Group: backend1 Encoding: Is collection: false SD: 1 backend component configuration(s): - validation/cel [map[check_expr:int(req_params.Id) % 3 == 0]] [+] GET /__debug/{{.Id}} Timeout: 3s Hosts: [http://127.0.0.1:8080] Concurrent calls: 1 Host sanitization disabled: false Target: Deny: [], Allow: [] Mapping: map[] Group: backend2 Encoding: Is collection: false SD: 1 backend component configuration(s): - validation/cel [map[check_expr:int(req_params.Id) % 5 == 0]] 1 async agent(s): - cool-agent Encoding: Consumer Timeout: 3s Consumer Workers: 20 Consumer Topic: *.bar Consumer Max Rate: 0.000000 Connection Max Retries: 10 Connection Backoff Strategy: exponential-jitter Connection Health Interval: 1s 1 agent component configuration(s): - github.com/devopsfaith/krakend-amqp/agent name: krakend host: amqp://guest:guest@localhost:5672/ exchange: foo prefetch_count: 40 auto_ack: true Connecting to 1 backend(s): [+] POST /__debug/cool-agent Timeout: 3s Hosts: [http://127.0.0.1:8080] Concurrent calls: 0 Host sanitization disabled: false Target: Deny: [], Allow: [] Mapping: map[] Group: Encoding: Is collection: false SD: 0 backend component configuration(s): Syntax OK! ``` And in level 3 (`-ddd`), there is everything that KrakenD could parse from the configuration: Checking the configuration with the debug flag  ``` $krakend check -t --lint -ddd -c krakend.json Parsing configuration file: krakend.json Global settings Name: My lovely gateway Port: 8080 Default cache TTL: 1h0m0s Default timeout: 3s Default backend hosts: [] Read timeout: 0s Write timeout: 0s Idle timeout: 0s Read header timeout: 0s Idle connection timeout: 0s Response header timeout: 0s Expect continue timeout: 0s Dialer timeout: 0s Dialer fallback delay: 0s Dialer keep alive: 0s Disable keep alives: false Disable compression: false Max idle connections: 0 Max idle connections per host: 250 No TLS section defined No Plugin section defined 4 global component configuration(s): - security/bot-detector patterns: [(Pingdom.com_bot_version_).* (facebookexternalhit)/.*] deny: [a b] allow: [c Pingdom.com_bot_version_1.1] - security/cors expose_headers: [Content-Length] allow_methods: [POST GET] allow_origins: [*] allow_headers: [Origin Authorization Content-Type] max_age: 12h - telemetry/logging level: DEBUG syslog: false stdout: true prefix: [KRAKEND] - telemetry/metrics listen_address: :8090 collection_time: 60s 1 API endpoints: - GET /example/:id Timeout: 3s QueryString: [q id] CacheTTL: 1h0m0s Headers to pass: [X-Header] OutputEncoding: json Concurrent calls: 1 1 endpoint component configuration(s): - validation/cel [map[check_expr:'something' in req_headers['X-Header']] map[check_expr:int(req_params.Id) % 2 == 0]] Connecting to 2 backend(s): [+] GET /__debug/{{.Id}} Timeout: 3s Hosts: [http://127.0.0.1:8080] Concurrent calls: 1 Host sanitization disabled: false Target: Deny: [], Allow: [] Mapping: map[] Group: backend1 Encoding: Is collection: false SD: 1 backend component configuration(s): - validation/cel [map[check_expr:int(req_params.Id) % 3 == 0]] [+] GET /__debug/{{.Id}} Timeout: 3s Hosts: [http://127.0.0.1:8080] Concurrent calls: 1 Host sanitization disabled: false Target: Deny: [], Allow: [] Mapping: map[] Group: backend2 Encoding: Is collection: false SD: 1 backend component configuration(s): - validation/cel [map[check_expr:int(req_params.Id) % 5 == 0]] 1 async agent(s): - cool-agent Encoding: Consumer Timeout: 3s Consumer Workers: 20 Consumer Topic: *.bar Consumer Max Rate: 0.000000 Connection Max Retries: 10 Connection Backoff Strategy: exponential-jitter Connection Health Interval: 1s 1 agent component configuration(s): - github.com/devopsfaith/krakend-amqp/agent exchange: foo prefetch_count: 40 auto_ack: true name: krakend host: amqp://guest:guest@localhost:5672/ Connecting to 1 backend(s): [+] POST /__debug/cool-agent Timeout: 3s Hosts: [http://127.0.0.1:8080] Concurrent calls: 0 Host sanitization disabled: false Target: Deny: [], Allow: [] Mapping: map[] Group: Encoding: Is collection: false SD: 0 backend component configuration(s): Syntax OK! ``` --- # [Auditing KrakenD API Gateway Configurations](https://www.krakend.io/docs/configuration/audit/) The `krakend audit` command is a rule evaluation tool that checks configuration files written in any of its [supported formats](/docs/configuration/supported-formats/) and returns practical **security recommendations**. It is designed to raise basic red flags and provide essential advice on your configuration. The output of the configuration and classification is inspired by the [CIS Benchmarks](https://www.cisecurity.org/communities/benchmarks). Security disclaimer If the audit command passes, it does not mean that your API is necessarily secure but that the evaluated rules have passed (find them as recommendations below). The tool displays practical and helpful information, including (but not limited to) misconfigurations opening the door to vulnerabilities, presence/absence of key components, dangerous flags or combinations, conflicting declarations, and statistics (planned), to put a few examples. ## Audit vs Check The `audit` command is complementary to the [`check` command](/docs/configuration/check/). Still, instead of focusing on the configuration file structure and linting, it evaluates the logic at a different stage. The command executes after parsing the configuration, using a **summarized tree** of the final recognized components and flags loaded. It **does not has access to the values of the properties** (but `check` does). For instance, if you write a `jwk_url` to validate tokens, `audit` does not know if you are using HTTPS or HTTP or which domain, but it does see if you have `disable_jwk_security`, which is dangerous in production. The purpose of the audit command is to add extra checks in your [automated CI pipeline](/docs/deploying/ci-cd/) to have safer deployments. ## Audit configuration The `audit` command has the following options: Usage of KrakenD audit  ```` $krakend audit --help ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.8 Audits a KrakenD configuration. Usage: krakend audit [flags] Examples: krakend audit -i 1.1.1,1.1.2 -s CRITICAL -c krakend.json Flags: -c, --config string Path to the configuration file -f, --format string Inline go template to render the results (default "{{ range .Recommendations }}{{.Rule}}\t[{{colored .Severity}}] \t{{.Message}}\n{{ end }}") -h, --help help for audit -i, --ignore string List of rules to ignore (comma-separated, no spaces) -I, --ignore-file string Path to a text-plain file containing the list of rules to exclude -s, --severity string List of severities to include (comma-separated, no spaces) (default "CRITICAL,HIGH,MEDIUM,LOW") ```` The simplest version of the command requires the path to the configuration file only, and outputs any problems found: Audit configuration  ``` $krakend audit -c krakend.json 1.2.1 [HIGH] Prioritize using JWT for endpoint authorization to ensure security. 2.2.1 [MEDIUM] Hide the version banner in runtime. 3.3.4 [CRITICAL] Set timeouts to below 1 minute for improved performance. 5.2.3 [LOW] Avoid coupling clients by overusing no-op encoding. ``` It also accepts different flags to customize its behavior: - `--severity`, `-s`: Severity requirements - `--ignore`, `-i`: Rules to ignore (inline) - `--ignore-file`, `-I`: Rules to ignore (from file) - `--format`, `--f`: Format of the output (Go template) More details of the flags below. ### Configuring the audit severity By default, the audit command will include **all severity levels**. However, you can choose through the `--severity` flag which groups you want to be printed in the console, separated by commas. The list is: - `CRITICAL` - `HIGH` - `MEDIUM` - `LOW` When the `--severity` is not defined, KrakenD uses `--severity CRITICAL,HIGH,MEDIUM,LOW`. You can use a **comma separated** string (no spaces) with all the severities you want to print. For instance, using the same example we had above, to filter by the most severe problems you would type: Term  ``` $krakend audit --severity CRITICAL,HIGH -c krakend.json 1.2.1 [HIGH] Prioritize using JWT for endpoint authorization to ensure security. 3.3.4 [CRITICAL] Set timeouts to below 1 minute for improved performance. ``` #### Excluding security audit rules The outputted security recommendations by the command are generic to any installation and might not apply to your setup, or you might disagree with our assigned severity. You can exclude future checking of any specific audit rules by passing a list or creating an exception file. To do that, use the `--ignore` flag passing a comma-separated list (no spaces) with all the ignore rules or a `--ignore-file` with the path to an ignore file. **All rules must have the numeric format `x.y.z`** . For the inline option, you could do the following: Ignore rules 1.2.3 and 4.5.6  ``` $krakend audit --ignore=1.2.3,4.5.6 -c krakend.json ``` For the option of an ignore file, you should create a plain text file with one rule per line. You can place this file anywhere and it does not require a specific extension or name. However, if it is not in the KrakenD workdir (`/etc/krakend/`), you must specify its relative or absolute path: Content of the ignore file  ``` $cat .audit_ignore 1.2.3 4.5.6 ``` And then calling it with: Ignore rules 1.2.3 and 4.5.6  ``` $krakend audit --ignore-file=.audit_ignore -c krakend.json ``` ### Customizing the output Finally, you can choose the **format of the output** according to your needs by injecting a Go template using the `-f` flag. The flag expects an inline template. The default template, as shown in the screenshot, applies the following go template: ```go-text-template {{ range .Recommendations }}{{.Rule}}\t[{{colored .Severity}}]\t{{.Message}}\n{{ end }} ``` As the output is processed using a template, you can inject anything you like. For instance, the example below generates a [TOML file](https://toml.io/en/) into `recommendations.toml`. Custom output  ``` $krakend audit -f '{{range .Recommendations}} [[recommendation]] rule = "{{.Rule}}" message = "{{.Message}}" severity = "{{.Severity}}" {{end}}' > recommendations.toml ``` Or the **JSON format** is even easier to write: JSON output  ``` $krakend audit -f '{{ marshal . }}' > recommendations.json ``` As you can see, the templates use a series of **variables and functions**, as follows: - `.Recommendations`: An array with all the recommendations, where each recommendation has the following structure: - `.Rule`: The identifier of this rule. E.g., `1.1.1` - `.Message`: A short message describing the recommendation. - `.Severity`: The level of severity for this recommendation In addition, you can use two functions: - `colored` to add colors to the severity (`{{colored .Severity}}`) when the output is in a terminal - `marshal` to return a JSON representation of the variable. E.g., `{{ marshal . }}` ## Audit recommendations The following is the list of **recommendations** you can find in the audit results. The recommendations are classified using a numeric code with the format `x.y.z`. You can use this rule identifier to exclude the rules during its checking, as explained above. ### 1. AuthZ and AuthN Recommendations for implementations of authorization or authentication to a more secure access control. ### 1.1 Authentication Recommendations for authentication issues only Recommendation [`1.1.1`](https://www.krakend.io/docs/configuration/audit/#rule-1.1.1 "Permalink to rule 1.1.1") **Implement more secure alternatives than Basic Auth to protect your data.** Severity HIGH Rationale Basic Auth is an insecure authentication method and is considered deprecated. It does not offer expiration mechanisms and can be hacked easily. Recommendation [`1.1.2`](https://www.krakend.io/docs/configuration/audit/#rule-1.1.2 "Permalink to rule 1.1.2") **Implement stateless authorization methods such as JWT to secure your endpoints as opposed to using API keys.** Severity MEDIUM Rationale API keys can be easily compromised and do not offer expiration mechanisms. Using JWT for stateless authorization ensures a secure and efficient way to authenticate and authorize users. ### 1.2 Authorization Recommendations for authorization issues only (token validation) Recommendation [`1.2.1`](https://www.krakend.io/docs/configuration/audit/#rule-1.2.1 "Permalink to rule 1.2.1") **Prioritize using JWT for endpoint authorization to ensure security.** Severity HIGH Rationale Properly securing endpoints through authorization is crucial for the security of your API. JWT is a recommended method for stateless authorization, as it ensures that the token is valid and has not been tampered with. [Add JWT validation](/docs/authorization/jwt-validation/) ### 2. HTTP transport This section covers recommendations for securing the HTTP transport, including TLS and securing HTTP headers. ### 2.1 TLS Recommendations for securing the use of TLS, ensuring that insecure connections are not allowed, that TLS is enabled and configured properly, and that strong cryptographic ciphers are used. Recommendation [`2.1.1`](https://www.krakend.io/docs/configuration/audit/#rule-2.1.1 "Permalink to rule 2.1.1") **Only allow secure connections (avoid insecure\_connections).** Severity HIGH Rationale This recommendation ensures that KrakenD is configured to only allow secure connections using TLS. This helps to prevent man-in-the-middle attacks and other forms of eavesdropping. [Disallow insecure connections](/docs/service-settings/tls/#allow_insecure_connections) Recommendation [`2.1.2`](https://www.krakend.io/docs/configuration/audit/#rule-2.1.2 "Permalink to rule 2.1.2") **Enable TLS or use a terminator in front of KrakenD.** Severity HIGH Rationale This recommendation ensures that KrakenD is either configured to use TLS or that a terminator is used in front of KrakenD to handle the encryption. This helps to prevent man-in-the-middle attacks and other forms of eavesdropping. [Enable TLS](/docs/service-settings/tls/) Recommendation [`2.1.3`](https://www.krakend.io/docs/configuration/audit/#rule-2.1.3 "Permalink to rule 2.1.3") **TLS is configured but its disable flag prevents from using it** Severity CRITICAL Rationale This recommendation ensures that TLS configuration does not have a disable flag to override it. Without this, communication between clients and KrakenD would be unencrypted and susceptible to eavesdropping and man-in-the-middle attacks. [Remove `disable`](/docs/service-settings/tls/#disabled) Recommendation [`2.1.7`](https://www.krakend.io/docs/configuration/audit/#rule-2.1.7 "Permalink to rule 2.1.7") **Enable HTTP security header checks (security/http).** Severity HIGH Rationale It is important to enable HTTP security header checks to ensure that your system is protected against common web-based attacks such as cross-site scripting (XSS) and clickjacking. [Enable http security](/docs/service-settings/security/) Recommendation [`2.1.8`](https://www.krakend.io/docs/configuration/audit/#rule-2.1.8 "Permalink to rule 2.1.8") **Avoid clear text communication (h2c).** Severity HIGH Rationale The h2c flag uses HTTP/2 in clear text. Use HTTP/2 over TLS instead (no flag declaration). [Disable h2c](/docs/service-settings/router-options/) Recommendation [`2.1.9`](https://www.krakend.io/docs/configuration/audit/#rule-2.1.9 "Permalink to rule 2.1.9") **Establish secure connections in internal traffic (avoid insecure\_connections internally).** Severity LOW Rationale This recommendation ensures that KrakenD is configured to only allow secure connections using TLS. This helps to prevent man-in-the-middle attacks and other forms of eavesdropping. [Disallow insecure connections](/docs/enterprise/backends/http-client/#client_tls) ### 2.2 Headers and parameters Recommendations for HTTP headers configuration and parameter forwarding Recommendation [`2.2.1`](https://www.krakend.io/docs/configuration/audit/#rule-2.2.1 "Permalink to rule 2.2.1") **Hide the version banner in runtime.** Severity MEDIUM Rationale It is important to hide the KrakenD version banner in order to prevent attackers from identifying vulnerabilities in your specific version of the software. Add the flag [hide\_version\_header](/docs/service-settings/router-options/#hide_version_header). Recommendation [`2.2.2`](https://www.krakend.io/docs/configuration/audit/#rule-2.2.2 "Permalink to rule 2.2.2") **Enable CORS.** Severity HIGH Rationale Cross-Origin Resource Sharing allows secure communication between different domains, which is crucial for protecting against cross-site scripting (XSS) attacks. [Enable CORS](/docs/service-settings/cors/) Recommendation [`2.2.3`](https://www.krakend.io/docs/configuration/audit/#rule-2.2.3 "Permalink to rule 2.2.3") **Avoid passing all input headers to the backend.** Severity HIGH Rationale Enabling the wildcard pollutes your backends and opens the door to malicious attackers to get through the gateway and scan your backend for vulnerabilities. [Avoid wildcard headers](/docs/endpoints/parameter-forwarding/) Recommendation [`2.2.4`](https://www.krakend.io/docs/configuration/audit/#rule-2.2.4 "Permalink to rule 2.2.4") **Avoid passing all input query strings to the backend.** Severity HIGH Rationale Enabling the wildcard pollutes your backends and opens the door to malicious attackers to get through the gateway and scan your backend for vulnerabilities. [Avoid wildcard query strings](/docs/endpoints/parameter-forwarding/) Recommendation [`2.2.5`](https://www.krakend.io/docs/configuration/audit/#rule-2.2.5 "Permalink to rule 2.2.5") **Avoid exposing gRPC server without services declared.** Severity LOW Rationale You are opening a gRPC server in the gateway that has no possible use, and therefore enabling it is an open door with no benefits. ### 2.3 Transport Recommendations for HTTP transport Recommendation [`2.3.1`](https://www.krakend.io/docs/configuration/audit/#rule-2.3.1 "Permalink to rule 2.3.1") **Limit the amount of cacheable content.** Severity MEDIUM Rationale There are cache components that do not set any limit to the amount of RAM the system can use, and a backend misconfiguration could lead to an unstable/crashing system. Add the flag [max\_size](/docs/backends/caching/#qos/http-cache-max_size) and [max\_items](/docs/backends/caching/#qos/http-cache-max_items) to your `qos/http-cache` configuration to limit the bytes you allow. ### 3. Traffic Management Recommendations for a better control of traffic and avoid disruptions ### 3.1 QoS Recommendations for ensuring Quality of Service Recommendation [`3.1.1`](https://www.krakend.io/docs/configuration/audit/#rule-3.1.1 "Permalink to rule 3.1.1") **Enable a bot detector.** Severity LOW Rationale Enabling a Bot detector can prevent unwanted crawling, and impacting performance for legitimate users. [Enable a bot detector](/docs/throttling/botdetector/) Recommendation [`3.1.2`](https://www.krakend.io/docs/configuration/audit/#rule-3.1.2 "Permalink to rule 3.1.2") **Implement a rate-limiting strategy and avoid having an All-You-Can-Eat API.** Severity HIGH Rationale Implementing a rate-limiting strategy can prevent a single user or service from overloading your API and impacting performance for other users. It also helps to prevent abuse and unwanted usage patterns. [Add throttling](/docs/throttling/) Recommendation [`3.1.3`](https://www.krakend.io/docs/configuration/audit/#rule-3.1.3 "Permalink to rule 3.1.3") **Protect your backends with a circuit breaker.** Severity HIGH Rationale Implementing a circuit breaker can protect your backends from being overwhelmed by requests in case of a service failure or unexpected traffic spike, by automatically stopping requests from being sent to the backend until the issue is resolved. [Add a circuit breaker](/docs/backends/circuit-breaker/) ### 3.2 Retry policies Retry policies to reconnect to key services behind KrakenD (future release) ### 3.3 Timeouts Recommendations for healthy timeouts and avoid overloading the system Recommendation [`3.3.1`](https://www.krakend.io/docs/configuration/audit/#rule-3.3.1 "Permalink to rule 3.3.1") **Set timeouts to below 3 seconds for improved performance.** Severity LOW Rationale Having short timeouts can help prevent slow requests from holding up the system and causing delays for other users. A timeout of 3 seconds or less is considered high enough and should be sufficient for most use cases. [Lower the timeout](/docs/endpoints/#timeout) Recommendation [`3.3.2`](https://www.krakend.io/docs/configuration/audit/#rule-3.3.2 "Permalink to rule 3.3.2") **Set timeouts to below 5 seconds for improved performance.** Severity MEDIUM Rationale Having medium-length timeouts can help prevent slow requests from holding up the system and causing delays for other users. A timeout of 5 seconds or more is considered high and should not be used in most of the cases. [Lower the timeout](/docs/endpoints/#timeout) Recommendation [`3.3.3`](https://www.krakend.io/docs/configuration/audit/#rule-3.3.3 "Permalink to rule 3.3.3") **Set timeouts to below 30 seconds for improved performance.** Severity HIGH Rationale Having long timeouts can help prevent slow requests from holding up the system and causing delays for other users. A timeout of 30 seconds or more is considered very high. [Lower the timeout](/docs/endpoints/#timeout) Recommendation [`3.3.4`](https://www.krakend.io/docs/configuration/audit/#rule-3.3.4 "Permalink to rule 3.3.4") **Set timeouts to below 1 minute for improved performance.** Severity CRITICAL Rationale Having timeouts set above 1 minute can lead to security vulnerabilities such as denial of service attacks. It’s important to ensure that your timeouts are always low to prevent these types of issues. [Lower the timeout](/docs/endpoints/#timeout) ### 4. Telemetry Recommendations for having full visibility of the gateway and underlying services activity. ### 4.1 Metrics Recommendations for the use of metrics. Recommendation [`4.1.1`](https://www.krakend.io/docs/configuration/audit/#rule-4.1.1 "Permalink to rule 4.1.1") **Implement a telemetry system for collecting metrics for monitoring and troubleshooting.** Severity MEDIUM Rationale Having a telemetry system in place is crucial for monitoring the performance and health of your KrakenD service. It allows you to track metrics such as request count, response time, and error rate. [Add metrics](/docs/telemetry/) Recommendation [`4.1.3`](https://www.krakend.io/docs/configuration/audit/#rule-4.1.3 "Permalink to rule 4.1.3") **Avoid duplicating telemetry options to prevent system overload.** Severity HIGH Rationale Having multiple telemetry options enabled can cause an overload on the system and make it harder to monitor your metrics. Ensure that you only have the necessary options enabled and that they are not duplicated. ### 4.2 Tracing Recommendations for the use of traces Recommendation [`4.2.1`](https://www.krakend.io/docs/configuration/audit/#rule-4.2.1 "Permalink to rule 4.2.1") **Implement a telemetry system for tracing for monitoring and troubleshooting.** Severity MEDIUM Rationale Implementing a telemetry system, such as OpenTracing, allows for better tracking and debugging of requests and can help identify issues or performance bottlenecks. [Add tracing](/docs/telemetry/) ### 4.3 Logging Recommendations for logging activity and best practices Recommendation [`4.3.1`](https://www.krakend.io/docs/configuration/audit/#rule-4.3.1 "Permalink to rule 4.3.1") **Use the improved logging component for better log parsing.** Severity MEDIUM Rationale The improved logging component allows for better parsing of logs, making it easier to identify and troubleshoot issues. This is a recommended configuration to have in place. [Add logging](/docs/logging/) ### 5. API Gateway General recommendations to have a better API gateway definition ### 5.1 Routing Recommendations for routing and endpoint structure Recommendation [`5.1.1`](https://www.krakend.io/docs/configuration/audit/#rule-5.1.1 "Permalink to rule 5.1.1") **Follow a RESTful endpoint structure for improved readability and maintainability.** Severity LOW Rationale RESTful endpoint structure is a best practice for API design and makes it easier for developers to understand and consume your API. Make sure that your endpoints are organized in a logical and consistent manner, and that they follow the standard CRUD operations. [Remove `disable_rest`](/docs/service-settings/http-transport-settings/#disable_rest) Recommendation [`5.1.2`](https://www.krakend.io/docs/configuration/audit/#rule-5.1.2 "Permalink to rule 5.1.2") **Disable the /\_\_debug/ endpoint for added security.** Severity LOW Rationale The `/__debug/` endpoint is a special endpoint that is used for debugging and testing purposes, and despite is considered safe to use in production, it’s not necessary in production environments. [Remove the debug endpoint](/docs/endpoints/debug-endpoint/). Recommendation [`5.1.3`](https://www.krakend.io/docs/configuration/audit/#rule-5.1.3 "Permalink to rule 5.1.3") **Disable the /\_\_echo/ endpoint for added security.** Severity LOW Rationale The `/__echo/` endpoint is a special endpoint that is used for debugging and testing purposes. It’s not necessary in production environments. [Remove the echo endpoint](/docs/endpoints/echo-endpoint/). Recommendation [`5.1.4`](https://www.krakend.io/docs/configuration/audit/#rule-5.1.4 "Permalink to rule 5.1.4") **Declare explicit endpoints instead of using wildcards.** Severity LOW Rationale Using wildcard endpoints might be very convenient to maintain less endpoints, but it opens the door to having zombie APIs and endpoints. Zombie APIs are those forgotten entries that you are not aware they are published. [Remove wildcards](/docs/enterprise/endpoints/wildcard/). Recommendation [`5.1.5`](https://www.krakend.io/docs/configuration/audit/#rule-5.1.5 "Permalink to rule 5.1.5") **Declare explicit endpoints instead of using /\_\_catchall.** Severity MEDIUM Rationale Although this endpoint might be super-useful, keep in mind that, generally speaking, you should avoid having a catchall route. All types of unknown requests will hit your fallback backend, opening the door for malicious users to test and scan it. From a security perspective, you are safer without this because you cut any traffic that you don’t explicitly declare. [Remove /\_\_catchall](/docs/enterprise/endpoints/catch-all/). Recommendation [`5.1.6`](https://www.krakend.io/docs/configuration/audit/#rule-5.1.6 "Permalink to rule 5.1.6") **Avoid using multiple write methods in endpoint definitions.** Severity MEDIUM Rationale You are doing a distributed transaction when you declare multiple unsafe methods (like POST) in a single endpoint. You don’t have any rollback or fail-safe mechanism in the gateway, and you can only hope for the best. Recommendation [`5.1.7`](https://www.krakend.io/docs/configuration/audit/#rule-5.1.7 "Permalink to rule 5.1.7") **Avoid using sequential proxy.** Severity MEDIUM Rationale Using sequential calls is considered an anti-pattern because when you make a network service dependent on the other, you are increasing the latency, decreasing the performance, and augmenting the error rate. [Avoid sequential proxy](/docs/endpoints/sequential-proxy/#do-you-really-need-a-sequential-proxy) ### 5.2 Proxy pipe Recommendations for proxy pipe configurations and best practices Recommendation [`5.2.1`](https://www.krakend.io/docs/configuration/audit/#rule-5.2.1 "Permalink to rule 5.2.1") **Ensure all endpoints have at least one backend for proper functionality.** Severity CRITICAL Rationale Having at least one backend per endpoint ensures that the endpoint is functional and can handle incoming requests. [Add a backend](/docs/backends/) Recommendation [`5.2.2`](https://www.krakend.io/docs/configuration/audit/#rule-5.2.2 "Permalink to rule 5.2.2") **Benefit from the backend for frontend pattern capabilities** Severity LOW Rationale All of your endpoints are connected to a single backend. Creating endpoints that connect simultaneously to several backends and aggregate the information, increases the speed and quality of service you deliver to end-users. Recommendation [`5.2.3`](https://www.krakend.io/docs/configuration/audit/#rule-5.2.3 "Permalink to rule 5.2.3") **Avoid coupling clients by overusing no-op encoding.** Severity LOW Rationale Abusing no-op encoding can lead to client coupling, which can negatively impact scalability and maintainability. It is recommended to use a different encoding for your clients and endpoints other than no-op. ### 6. Event Driven Gateway Recommendations for implementations of event-driven gateway and async agents that can lead to problems. ### 6.1 Async agents Recommendations for async agents configuration and deployment issues Recommendation [`6.1.1`](https://www.krakend.io/docs/configuration/audit/#rule-6.1.1 "Permalink to rule 6.1.1") **Ensure Async Agents do not start sequentially to avoid overloading the system. (+10 agents)** Severity LOW Rationale Starting too many async agents one after the other can cause delays during startup. Ensure that async agents are started asynchronoulsy by [disabling sequential start](/docs/service-settings/http-server-settings/#sequential_start). ### 7. Deprecated components Components that are unsupported or discontinued. Their usage is descouraged as they will be removed in new versions and they do not receive security updates. ### 7.1 Deprecated plugins Plugins that even might be functional, they are marked as deprecated and do not receive security patches. Upgrade to their alternative when available. Recommendation [`7.1.1`](https://www.krakend.io/docs/configuration/audit/#rule-7.1.1 "Permalink to rule 7.1.1") **Virtual Host is now a native component** Severity HIGH Rationale Avoid using deprecated plugin virtualhost. and use its native counterpart instead See [upgrading from plugin](/docs/enterprise/service-settings/virtual-hosts/#upgrading-from-the-old-plugin-before-v24) to upgrade to the new virtualhost. Recommendation [`7.1.2`](https://www.krakend.io/docs/configuration/audit/#rule-7.1.2 "Permalink to rule 7.1.2") **Static Filesystem is now a native component** Severity HIGH Rationale Avoid using deprecated plugin static-filesystem and use its native counterpart instead. See [upgrading from plugin](/docs/enterprise/endpoints/serve-static-content/#upgrading-from-the-old-plugin-before-v24) to upgrade to the new static-filesystem.. Recommendation [`7.1.3`](https://www.krakend.io/docs/configuration/audit/#rule-7.1.3 "Permalink to rule 7.1.3") **Basic-Auth is now a native component** Severity HIGH Rationale Avoid using deprecated plugin basic-auth and use its native counterpart instead. Please move your configuration to the namespace auth/basic to use the new component. See [basic authentication](/docs/enterprise/authentication/basic-authentication/) documentation. Recommendation [`7.1.4`](https://www.krakend.io/docs/configuration/audit/#rule-7.1.4 "Permalink to rule 7.1.4") **Wildcard is now a native component** Severity HIGH Rationale Avoid using deprecated plugin wildcard. and use its native counterpart instead See [upgrading from plugin](/docs/enterprise/endpoints/wildcard/#upgrading-from-the-old-wildcard-plugin-before-v23) to upgrade to the new Wildcard. Recommendation [`7.1.5`](https://www.krakend.io/docs/configuration/audit/#rule-7.1.5 "Permalink to rule 7.1.5") **HTTP PROXY is now a native component** Severity HIGH Rationale Avoid using deprecated plugin http-proxy and use its native counterpart instead. See [upgrading from plugin](/docs/enterprise/backends/http-proxy/#migration-from-old-plugin) to upgrade to the new options. Recommendation [`7.1.6`](https://www.krakend.io/docs/configuration/audit/#rule-7.1.6 "Permalink to rule 7.1.6") **Static Filesystem is now a native component** Severity HIGH Rationale Avoid using deprecated plugin static-filesystem and use its native counterpart instead. See [upgrading from plugin](/docs/enterprise/endpoints/serve-static-content/#upgrading-from-the-old-plugin-before-v24) to upgrade to the new static-filesystem. Recommendation [`7.1.7`](https://www.krakend.io/docs/configuration/audit/#rule-7.1.7 "Permalink to rule 7.1.7") **No-redirect plugin is now a native component** Severity HIGH Rationale Avoid using deprecated plugin no-redirect and use its native counterpart instead. See [upgrading from plugin](/docs/enterprise/backends/client-redirect/#migration-from-old-plugin) to upgrade to the new options. Recommendation [`7.1.8`](https://www.krakend.io/docs/configuration/audit/#rule-7.1.8 "Permalink to rule 7.1.8") **content-replacer plugin is now a native component** Severity HIGH Rationale Avoid using deprecated plugin content-replacer and use its native counterpart instead. See [upgrading from plugin](/docs/enterprise/endpoints/content-replacer/#migration-from-old-plugin) to upgrade to the new options. Recommendation [`7.1.9`](https://www.krakend.io/docs/configuration/audit/#rule-7.1.9 "Permalink to rule 7.1.9") **response-schema-validator plugin is now a native component** Severity HIGH Rationale Avoid using deprecated plugin response-schema-validator and use its native counterpart instead. See [upgrading from plugin](/docs/enterprise/endpoints/response-schema-validator/#migration-from-old-plugin) to upgrade to the new options. ### 7.2 Deprecated namespaces Namespaces that even might be functional, they are marked as deprecated and do not receive security patches. Upgrade to their alternative when available. Recommendation [`7.2.1`](https://www.krakend.io/docs/configuration/audit/#rule-7.2.1 "Permalink to rule 7.2.1") **Unsupported namespace telemetry/ganalytics** Severity HIGH Rationale Avoid using deprecated component telemetry/ganalytics. [Upgrade to OpenTelemetry](/docs/telemetry/opentelemetry/). Recommendation [`7.2.2`](https://www.krakend.io/docs/configuration/audit/#rule-7.2.2 "Permalink to rule 7.2.2") **Unsupported namespace telemetry/instana** Severity HIGH Rationale Avoid using deprecated component telemetry/instana. [Upgrade to OpenTelemetry](/docs/telemetry/opentelemetry/). Recommendation [`7.2.3`](https://www.krakend.io/docs/configuration/audit/#rule-7.2.3 "Permalink to rule 7.2.3") **Deprecated component telemetry/opencensus** Severity HIGH Rationale Avoid using deprecated component telemetry/opencensus. [Upgrade to OpenTelemetry](/docs/telemetry/opentelemetry/). Recommendation [`7.2.4`](https://www.krakend.io/docs/configuration/audit/#rule-7.2.4 "Permalink to rule 7.2.4") **Deprecated component telemetry/influx** Severity HIGH Rationale Avoid using deprecated component telemetry/influx. [Upgrade to OpenTelemetry](/docs/telemetry/influxdb/). ### 7.3 Deprecated configuration options Configuration options that will become unavailable in future versions. Recommendation [`7.3.1`](https://www.krakend.io/docs/configuration/audit/#rule-7.3.1 "Permalink to rule 7.3.1") **Add missing level \`keys\`** Severity MEDIUM Rationale Avoid using `private_key` and `public_key` directly under `tls` and place them inside the `keys` array. See [TLS Server Settings](/docs/service-settings/tls/#tls-server-settings) --- # [Extended Flexible Configuration (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/configuration/flexible-config/) The **Extended Flexible Configuration** allows you to express your KrakenD configuration using **multiple files**, reusing code blocks, and injecting external data through a **templates system**. It is called “Extended” because it uses an engine more capable and easier to use than its [open source alternative](/docs/configuration/flexible-config/), while it remains 100% compatible. In its simplest form, you can do things like: ```json { "version": 3, "extra_config": { "$ref": "./service/extra_config.json" } } ``` As you can guess, in the example above, the `$ref` entry is replaced by the object declared in the external file, much like it works in the JSON Schema specification. You may need this feature to better control changes in your configuration and facilitate collaboration. In addition to the `$ref`, the **settings** files allow you to declare custom data structures you can access in the configuration. For instance, if you have a `company.json` or `company.yaml` with the following content: ```yaml # company.yaml name: acme ``` Then you could access its attributes like this: ```json { "version": 3, "extra_config": { "$ref": "{{ .company.name }}/service/extra_config.json" } } ``` The example above is actually a **template** now because we have added the template delimiters `{{ }}`, a very simple one though. We are loading the `extra_config.json` file from a dynamic location that is dictated by a file `company.yaml`, in this case from `acme/service/extra_config.json`. But **templates** can get *really* complex, with conditionals, iterations, sub-templates, recursivity, etc. If you introduce a lot of complexity, your folks might find your configuration challenging to follow and maintain. Here’s another simple example: ```go-text-template { "version": 3 {{ if .company }} ,{{ template "service/components.tmpl" .company }} {{ end }} } ``` The example above is a template with a conditional that it renders as a JSON file. Still, you can encode your configuration files in any of the [supported formats](/docs/configuration/supported-formats/) (`json`, `yaml`, `toml`, etc.), as the template engine is agnostic of its contents. Templates are based on the `text/template` [Go library](https://golang.org/pkg/text/template/), and we recommend you start reading our [introduction to templates](/docs/enterprise/configuration/templates/) ## Flexible Configuration activation Activating the Extended Flexible Configuration requires creating a **behavioral file** that defines how the engine parses the injected content and where it should take it from. This file sets things like the **paths** to your content or options to parse and debug. **Copy and paste** the following JSON file and save it as `flexible_config.json`: ```json { "$schema": "https://www.krakend.io/schema/v2.13/flexible_config.json", "settings": { "paths": ["settings"], "allow_overwrite": true, "dir_field_prefix": "", "allowed_suffixes": [ ".yaml", ".json" ] }, "partials": { "paths": ["partials"] }, "templates": { "paths": ["templates"] }, "meta_key": "meta", "out": "result.json", "debug": false, "ref_key": "$ref" } ``` Then, create an **initial directory structure** reflecting the `paths` you have set in the configuration. Following the example above, that would be: Template boilerplate  ``` $mkdir -p {partials,settings,templates} ``` The meaning of these directories is explained below. Now you can run the software using `krakend run` or `krakend check`, and it will have the templating engine enabled. If you come from the open-source version of the flexible configuration, you’ll realize you don’t need any previous `FC_` environment variables and that many of the limitations are now gone. For instance, if your configuration is in a `krakend.tmpl` file: Execute a simple template  ``` $docker run --rm -v "$PWD:/etc/krakend/" krakend/krakend-ee check -c krakend.tmpl Parsing configuration file: krakend.tmpl Syntax OK! ``` This is all you need to start using the Extended flexible configuration. ### The `flexible_config.json` structure The Extended Flexible Configuration behaves according to its `flexible_config.json` (default name) located in the [working directory](/docs/enterprise/configuration/working-directory/). As you have seen above, you must express it in JSON format. If you change its name, you can pass an **environment variable** `FC_CONFIG=new_file_name.json` specifying your chosen custom filename. The structure of this file is as follows: ##### Fields of Schema validation for Extended Flexible Configuration. See: https://www.krakend.io/docs/enterprise/configuration/flexible-config/ \* required fields ##### [`debug`](https://www.krakend.io/docs/enterprise/configuration/flexible-config/#debug "Click to copy link to debug") boolean Whether to enable the debug mode or not. The debug shows the hierarchy of the settings tree in the console. You can also enable this flag during runtime passing an environment variable `FC_DEBUG=true`. ##### [`keys_naming_rules`](https://www.krakend.io/docs/enterprise/configuration/flexible-config/#keys_naming_rules "Click to copy link to keys_naming_rules") Allows you to apply configuration inheritance using `freeform` keys, or alphanumeric characters (`strict`). Possible values are: `"strict"` , `"freeform"` ##### [`meta_key`](https://www.krakend.io/docs/enterprise/configuration/flexible-config/#meta_key "Click to copy link to meta_key") string The name of the variable you have available on templates containing the tree of files involved in the configuration (except partials). You can access to this variable with a starting dot, e.g., `{{ .meta }}`. Defaults to `"meta"` ##### [`out`](https://www.krakend.io/docs/enterprise/configuration/flexible-config/#out "Click to copy link to out") string It specifies a filename where the result of compiling the templates is stored. The directory and the filename you use must be writeable by the KrakenD process. It has no other purpose than debugging the resulting template. Defaults to `""` ##### [`partials`](https://www.krakend.io/docs/enterprise/configuration/flexible-config/#partials "Click to copy link to partials") object When needed, the partials are treated as raw text files, and they are inserted in the placeholder ‘as is’, and no template evaluation of its content is performed. ##### [`paths`](#partials-paths "Click to copy link to paths") array of strings The [relative](https://www.krakend.io/docs/enterprise/configuration/working-directory/) or absolute paths to the partials directories, each entry is a different path. Contents are scanned **recursively**. Example: `["partials"]` Defaults to `[]` ##### [`ref_key`](https://www.krakend.io/docs/enterprise/configuration/flexible-config/#ref_key "Click to copy link to ref_key") string Instead of using the keyword `$ref` to replace configuration blocks, use another keyword. This is useful when you want to use OpenAPI’s `$ref` and Flexible Config’s `$ref` together and avoid crossed behavior. Example: `"$replace"` Defaults to `"$ref"` ##### [`settings`](https://www.krakend.io/docs/enterprise/configuration/flexible-config/#settings "Click to copy link to settings") object Settings are files expressed in JSON, YAML, ENV, TOML, INI or properties (Java) formats that are parsed and available as a tree like `.filename` inside the templates. Example: `{"paths":["settings/production","settings/common"]}` ##### [`allow_overwrite`](#settings-allow_overwrite "Click to copy link to allow_overwrite") boolean When the settings tree is converted to a traversable variable, whether to accept that another path that resolves to the same tree path can override a previous one or not. Defaults to `false` ##### [`allowed_suffixes`](#settings-allowed_suffixes "Click to copy link to allowed_suffixes") array of strings The list of file suffixes you want the parser to take into account. Non-matching files are ignored and unavailable in the templates. Example: `[".yaml",".json","krakend.env"]` Defaults to `[".yaml",".yml",".json",".toml",".tml",".ini",".dotenv",".env",".properties",".prop",".props"]` ##### [`dir_field_prefix`](#settings-dir_field_prefix "Click to copy link to dir_field_prefix") string When converting the settings tree into a traversable variable, add a prefix to directory names. Example: `"dir_"` Defaults to `""` ##### [`paths`](#settings-paths "Click to copy link to paths") array of strings The [relative](https://www.krakend.io/docs/enterprise/configuration/working-directory/) or absolute paths to the settings directories, each entry is a different path. Contents are scanned **recursively**. Example: `["settings"]` Defaults to `[]` ##### [`templates`](https://www.krakend.io/docs/enterprise/configuration/flexible-config/#templates "Click to copy link to templates") object The location of the Go templates you want to use to express your configuration. Example: `{"paths":["templates"]}` ##### [`paths`](#templates-paths "Click to copy link to paths") array of strings The [relative](https://www.krakend.io/docs/enterprise/configuration/working-directory/) or absolute paths to the templates directories, each entry is a different path. You might have multiple directories when you have templates shared across environments or projects, and then you have dedicated templates for the running environment. Defaults to not use any templates directory. Contents are scanned **recursively**. Example: `["templates"]` Defaults to `[]` ##### [`undefined_vars`](#templates-undefined_vars "Click to copy link to undefined_vars") How to treat access to unexisting variables in the templates. When the value is `error` the template will fail to compile, when `zero` it will set a zero-value (e.g, empty string), and when the value is `invalid` it will replace the content with the string ``. Possible values are: `"error"` , `"zero"` , `"invalid"` Defaults to `"error"` Schema: [https://www.krakend.io/schema/v2.13/flexible\_config.json](https://www.krakend.io/schema/v2.13/flexible_config.json "JSON schema definition") ### Settings The settings directories store files expressed in many formats such as `.json`, `.yaml`, `.env`, `.yml`, `.toml`, `.ini`, `.prop`, `.properties`, or `.props`. You can set multiple `paths` which define the list of all data sources available. They can be absolute directories (e.g.: `/etc/krakend/settings`), relative to the [working directory](/docs/enterprise/configuration/working-directory/) (e.g., `./settings`) or individual files (e.g.,: `./override.yaml`). **Directories are loaded recursively**, and the settings variables will contain all its nesting levels. For instance, if you have a group of settings shared across all environments, another group for production only settings, and finally a manual override of specific values, you could have a configuration like this: ```json { "settings": { "paths": [ "./settings/common", "./settings/production", "override.yaml" ] } } ``` When all directories and files are parsed, you can access them in the templates using the dot notation `.`. It means that if you, for instance, have a file under `./settings/production/a/b/c/file.json` and you defined the `paths` as above, you can access its contents in the templates as `{{ .a.b.c.file }}` (notice the starting dot). All contents under the `paths` you defined are placed at the root level of the settings tree. The engine will load **recursively** and, **in the order defined**, all files and subdirectories you have specified in the `paths`. Their content is merged if you have repeated file names with different extensions (e.g.: `file.yaml` and `file.json`). **Settings option `allow_overwrite`** With the example above, you could have that the production folder overwrites some settings that were already defined in the common folder. If there are conflicts like this when converting directories and file contents into the settings tree (they use the same name path), the `allow_overwrite` flag lets you define what you want to do. When overwrite is allowed, the last parsed file replaces any conflicting variables in the tree so that the production differences will replace any existing settings in common, at the attribute level, not the file level. There might also be directory names that clash with file names or keys inside the data structures. For instance, you have `./settings/foo.json` and `./settings/foo/common.json`. This would resolve to `{{ .foo }}` and `{{ .foo.common }}`. The last one could replace a key `common` if it exists in `foo.json`. A practical example for `allow_overwrite` could be having these two files: ```yaml # common/connection.yaml host: localhost port: 1234 ``` And ```yaml # production/connection.yaml host: myserver.com ``` With an order as specified in the example above, the final `{{ .connection }}` variable would have `myserver.com` as host and `1234` as port: The result of merging the two files and overriding the differences. **Settings option `dir_field_prefix`** You have the opportunity to rename the paths of directories with a prefix. For instance, if you use `"dir_field_prefix": "dir_"`, then the same scenario would resolve to `{{ .foo }}` and `{{ .dir_foo.common }}` which gives no room to conflict. **Settings option `allowed_suffixes`** And finally, you can have more files in the settings directories than the flexible configuration needs to load for whatever reason. To filter which extensions or ending paths you want to load, you can use the `allowed_suffixes` list. For instance: ```json { "settings": { "allowed_suffixes": [ ".yaml", ".prod.json" ] } } ``` The example above would only load YAML files or JSON ones that end in `.prod.json`. ### Partials The partials are treated as **raw text files**, and they are inserted in the placeholder **“as is”**, and no template evaluation of its content is performed. ```json { "partials": { "paths": [ "./partials" ] } } ``` ### Templates When you start KrakenD, the configuration file passed with the `-c` flag is treated as the **base template**, and from the base you can load other sub-templates. You must add a `paths` entry inside the `templates` setting. It is also a list that defines multiple directories or individual template files. For instance: ```json { "templates": { "paths": [ "templates", "some_file.tmpl" ] } } ``` In the example above, we pass a `templates` folder and an individual template file `some_file.tmpl`. All `paths` are traversed recursively, meaning that all insider templates are also available if there are subfolders. The relative path is removed from the template name when there are subdirectories. For instance, a directory structure like `dir1/dir2/file.tmpl` lets you load the template as `{{ template "file.tmpl" }}`, and as you can see, there is no `dir1` or `dir2` when you reference it. When there are conflicts (you have the same filename in different folders), you will see a warning in the logs, and the engine will rename the template to `{{ template "dir2_file.tmpl"}}`, using the last directory name as a prefix. If there was also a conflict, you would have `{{ template "dir1_dir2_file.tmpl"}}`, and so on. But unless there are conflicts, the template base name is all you need. To start working with templates, read [Flexible Configuration Templates](/docs/enterprise/configuration/templates/) ### Out file Our final attribute of the behavior file is the `out`. It specifies a filename where the result of compiling the templates is stored. The directory and the filename you use must be writeable by the KrakenD process. It let’s you see the resulting file after rendering all the templates. It has no other purpose than debugging the resulting template. For instance: ```json { "out": "result.json" } ``` ### Metadata tree All templates have a variable `{{ .meta }}` (or whatever you choose under `meta_key`) that contains metadata of the processed files and the `flexible_config.json` configuration file. It has a structure like this: ```json { "flexible_config": { "meta_key": "meta", "settings": { "allow_overwrite": true, "allowed_suffixes": [".json",".yml"], "dir_field_prefix": "", "paths": null }, "templates": { "partials": [], "paths": null, "undefined_vars": "" } }, "settings": { "files": [ "production/servers.json", "common/base.yml", ] }, "templates": { "failed": [], "files": {} } } ``` You can use this tree in your templates as best fits. ## `$ref`: Reference external files One of the main differences between the Extended Flexible Configuration and the open source one is that you can reference other configuration files using a reference, **even if you don’t want to use Go templates**. The `$ref` keyword will inject the content of the referenced file, allowing you to work with non-template files (but the templates are welcome, too!). It does not matter if you have the configuration in YAML, INI, or JSON. For instance, when you add a `$ref` keyword, the content of the referenced file will replace the `$ref` block (instead of `$ref` you can use another key if needed with `ref_key` in the settings file). For instance, given that you have a `websockets.json` as follows: ```json { "url_pattern": "/ws/{room}", "disable_host_sanitize": true, "host": [ "ws://chat:8888" ] } ``` You can include it in the configuration as follows: ```json { "endpoint": "/chat/{room}", "backend": [ { "$ref": "./backends/websockets.json" } ] } ``` And KrakenD resolves this to: ```json { "endpoint": "/chat/{room}", "backend": [ { "url_pattern": "/ws/{room}", "disable_host_sanitize": true, "host": [ "ws://chat:8888" ] } ] } ``` In case you would like to use another key instead of `$ref`, you can set the setting `ref_key` to a different value. This might make sense when you combine OpenAPI generation with Extended Flexible Configuration, as both provide a meaning for this key. In addition, you can also use **data pointers** (or anchors), so you can use `#/` to traverse a specific route inside the referenced file, and use additional `/` for nested objects. For instance: ```json { "endpoint": "/chat/{room}", "backend": [ { "url_pattern": "/ws/{room}", "disable_host_sanitize": true, "host": { "$ref": "./backends/websockets.json#/host" } } ] } ``` In the example above, we import just one field `host` from the referenced file. Using `$ref` compared to template syntax **is better for maintenance** because the complexity is much lower. A final note on using `$ref` is that if you don’t want to use templates, partials, or settings, your `flexible_config.json` can be simplied. For instance, you can have just this, which just saves the resulting file after resolving all refs: ```json { "out": "result.json" } ``` ## Testing the configuration We recommend using a Docker compose file to work faster with flexible configuration. Save the following `docker-compose.yml` and do a `docker compose up`. This setup with the [`:watch` image](/docs/enterprise/developer/hot-reload/) will allow you to work locally with the latest version of KrakenD and apply the changes automatically whenever you change a source file. ```yml version: "3" services: krakend: image: krakend/krakend-ee:watch volumes: - "./:/etc/krakend/" command: ["run","-dc","krakend.tmpl"] ``` As the flexible configuration is composed of several pieces, it’s easy to make a mistake at some point. Test the syntax of all the files with the `krakend check` command and pay attention to the output to verify there aren’t any errors. When there are errors, the output contains information to help you resolve it, e.g.: ``` ERROR parsing the configuration file: loading flexible-config settings: - backends.json: invalid character '}' looking for beginning of object key string ``` The setting `out` in the behavior file (`flexible_config.json` above) writes the content of the final file in a known path, so you can check its contents at any time. If you don’t use `docker compose`, you can also use flexible configuration as follows: Checking the configuration  ``` $FC_CONFIG=flexible_config.json \ FC_DEBUG=true \ krakend check -t -d -c "$PWD/krakend.tmpl" ``` ## Multiple versions of the `flexible_config.json` There are times when you have several environments where the flexible configuration file needs to differ, like loading the files from a different path. There are several ways to manage divergences of the behavioral file. The most obvious solution is to manage different versions, like `flexible_config.prod.json` and `flexible_config.devel.json` and injecting the right one using the `FC_CONFIG=` environment variable when the program starts. Another possibility could be having a template that contains environment variables. For instance, a file `flexible_config.template.json`: ```json { "settings": { "paths": [ "$PWD/settings/global", "$PWD/settings/$TARGET" ] } } ``` Then, you can create the final `flexible_config.json` file during the build process using `envsubst`, which replaces the environment variables with their values. The file above assumes the existence of `$TARGET` and `$PWD` as environment variables. Then, you can generate the final file as follows: Environment variable substitution  ``` $envsubst < flexible_config.template.json > flexible_config.json ``` The command, which is generally available in Linux distributions, takes a template file as input and outputs the same file with the environment variables replaced. You have to be aware that missing variables are simply replaced by an empty string. Note: on Alpine-based containers, like the KrakenD image, you need to do an `apk add envsubst` to use this command. --- # [Flexible Configuration: A template-based config for API Gateway](https://www.krakend.io/docs/configuration/flexible-config/) The **Flexible Configuration** allows you to declare the configuration using **multiple files** and use a **templates system**, opening the door to multi-environment configurations and code reuse. The Flexible Configuration enables a **template processor** based on [Go templates](https://golang.org/pkg/text/template/) and is enriched with [Sprig functions](http://masterminds.github.io/sprig/) and KrakenD functions. You can encode your configuration files in any of the [supported formats](/docs/configuration/supported-formats/) (`json`, `yaml`, `toml`, etc.), as the template is agnostic of its contents. Use the Flexible Configuration when you need to: - Split an extensive configuration into several files - Inject variables, like environment settings, into the configuration - Use placeholders and reusable code blocks to avoid repeating code - Organize your code better when multiple developers are modifying the gateway - Manage KrakenD using multiple repositories (and merging the files in the CI) - Track changes, avoid conflicts, and review code more easily - Have the full power of a template system! ## How it works Activating the Flexible Configuration requires injecting at least the **environment variable** `FC_ENABLE=1` when running a krakend `run` or `check` command and **additional variables** depending on the features you’d like to enable. When you enable through the environment variables this feature, the configuration is loaded internally into a template parser that collects all the involved files and renders the final configuration file. ## Flexible Configuration variables Different usage on Enterprise and Open Source The **Enterprise version** of the Flexible Configuration uses the [Extended Flexible Config](/docs/enterprise/configuration/flexible-config/) engine, which **simplifies** the operation, allows using nested directories, recursivity, or the `$ref` operator, and needs none of the following variables amongst other features. Still, all your [open source edition](/open-source/) templates are 100% compatible with the Enterprise counterpart. The **environment variables** of the flexible configuration are: - `FC_ENABLE=1`: Activates Flexible Configuration. You can use `1` or any other value (but `0` won’t turn it off!). The file passed with the `--config` flag is the **base template** and contains the references to any other templates. - `FC_TEMPLATES=path/to/templates`: The path to the `templates` directory. These are evaluated using the Go templating system. - `FC_SETTINGS=path/to/settings`: The path to the `settings` directory. Settings are JSON files that you can use to fill values in the templates, much similar to **env files** in other applications, but richer as you can use multiple files, structures, and nesting. - `FC_PARTIALS=path/to/partials`: The path to the `partials` directory. Partial files are pieces of text that DON’T EVALUATE, and they are inserted in the placeholder “as is”. - `FC_OUT=file.json`: Saves the resulting configuration after rendering the template, useful for debugging, not required for runtime. For instance, let’s write a simple template `simple.tmpl` (go template emulating a json format): ```go-text-template { "version": {{add 2 1}} } ``` If the template system works, the server will start with a value `"version": 3`. We can test it with (Docker example): Execute a simple template  ``` $docker run --rm -v "$PWD:/etc/krakend/" -e "FC_ENABLE=1" -e "FC_OUT=result.json" krakend check -c simple.tmpl Parsing configuration file: simple.tmpl Syntax OK! ``` We know that it works because KrakenD will fail with a different `version` value, but we can also check the contents of `result.json` for debugging purposes. Let’s now introduce the rest of the **optional** variables. For instance, let’s assume you decided to organize your code as follows: ``` . ├── krakend.tmpl └── config ├── partials │ └── file.txt ├── templates │ ├── telemetry.tmpl │ └── endpoints.tmpl └── settings ├── prod | └── urls.json └── dev └── urls.json ``` Then you could 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" \ FC_OUT="output.json" \ krakend run -c "krakend.tmpl" ``` In the example above, notice that the `FC_SETTINGS` includes the path to the `prod`uction folder. This is how you would set a specific environment. You might inject here an env var if you have multiple environments. **The directory structure is completely up to you**. Use a docker compose Consider using a docker compose in combination with the `:watch` image to speed up your development time. ## Template syntax The configuration file passed with the `-c` flag is treated as a **Go template**, and you can use all the power the template engine brings. In addition, the templating system is overloaded with [Sprig functions](http://masterminds.github.io/sprig/), and KrakenD functions, adding more features. The data evaluations or control structures are easily recognized as they are surrounded by `{{` and `}}`. Any other text outside these delimiters is unprocessed text copied to the output as it is. Read the [Flexible Config Templates](/docs/configuration/templates/) documentation to start playing with templates. ## Testing the configuration We recommend using a Docker compose file to work faster with flexible configuration. Save the following `docker-compose.yml` and do a `docker compose up`. This setup with the [`:watch` image](/docs/developer/hot-reload/) will allow you to work locally with the latest version of KrakenD and apply the changes automatically whenever you change a source file. ```yml version: "3" services: krakend: image: krakend:watch volumes: - "./:/etc/krakend/" environment: - FC_ENABLE=1 - FC_OUT=/etc/krakend/out.json - FC_PARTIALS=/etc/krakend/config/partials - FC_SETTINGS=/etc/krakend/config/settings/test - FC_TEMPLATES=/etc/krakend/config/templates command: ["run","-dc","krakend.tmpl"] ``` As the flexible configuration is composed of several pieces, it’s easy to make a mistake at some point. Test the syntax of all the files with the `krakend check` command and pay attention to the output to verify there aren’t any errors. When there are errors, the output contains information to help you resolve it, e.g.: ``` ERROR parsing the configuration file: loading flexible-config settings: - backends.json: invalid character '}' looking for beginning of object key string ``` The variable `FC_OUT` writes the content of the final file in a known path, so you can check its contents at any time. If you don’t use `docker compose`, you can also use flexible configuration as follows: Checking the configuration  ``` $FC_ENABLE=1 \ FC_SETTINGS="$PWD/config/settings" \ FC_PARTIALS="$PWD/config/partials" \ FC_TEMPLATES="$PWD/config/templates" \ FC_OUT=out.json \ krakend check -t -d -c "$PWD/config/krakend.json" ``` --- # [Introduction to Templates](https://www.krakend.io/docs/configuration/templates/) There are **several components and features** in KrakenD that allow you to define configurations or content manipulations using **templates**. Whether you are using templates with [flexible configuration](/docs/configuration/flexible-config/), a [Request generator](/docs/enterprise/backends/body-generator/) or [Response manipulation](/docs/enterprise/backends/response-body-generator/) the syntax you use is the same, and it’s based on **Go templates** (as Helm, Kubernetes, and many other systems). Our convention for saving templates, is using the `.tmpl` extension, although this is not enforced. This document provides a few direction to use templates. ## Template syntax The templates use internally the Go `text/template` package and **Sprig functions**. For templates loaded in the Flexible Configuration, there are additional custom functions to load external resources. There are two initial external documentation pages worth reading to get familiar with these, although you’ll find practical information below: - The [Go text/template documentation](https://golang.org/pkg/text/template/) sets the rules of templates. - [Sprig functions](http://masterminds.github.io/sprig/) ### Basics of templates You will recognize templates because their data evaluations or control structures use surrounding `{{` and `}}`. Any other text outside these delimiters is unprocessed text copied to the output as it is. For instance, let’s write a simple template `krakend.tmpl` (A Go template rendering in JSON format): ```go-text-template { "version": {{add 2 1}} } ``` The template above uses a Sprig function `add` that sums two numbers, and prints when executed: ```json { "version": 3 } ``` **Templates are agnostic of the encoding** you want to express. The example above renders a JSON file, but you could write XML (e.g., when writing a SOAP request) or anything else. A few basics to get started: - Comments look like `{{/* a comment */}}` and can be multiline - Variables set by KrakenD are under `{{ .variable_name }}`. Notice the starting dot `.`. - Variables you assign can use the syntax `{{ $myvariable := "hello" }}`, and when a variable already exists with `{{ $myvariable = "hello2" }}` - Conditionals use the syntax `{{ if CONDITION }}yes{{else}}no{{end}}`. You can also use `{{else if}}`. Empty values evaluate to `false`. - Loops, or iterations use the syntax `{{ range .ELEMENT}}...{{end}}` or `{{ range .ELEMENT}}...{{else}}...{{end}}`. The `else` is used when the element you want to iterate is empty. Additionally you can use `{{break}}` and `{{continue}}` in loops. - You can loop with assigned indexes and variables as `{{ range $key, $value := .ELEMENT}}...{{end}}` - Access to elements using `{{with .ELEMENT}}...{{end}}`, or `{{with .ELEMENT}}yes{{else}}no{{end}}` when the variable is empty - The context is represented with a starting dot `.` (see below) - You can suppress preceding and following spaces from any block adding `-`, for instance: `{{- if true -}}...{{- end -}}` - You can pipe [functions](https://pkg.go.dev/text/template#hdr-Functions) you can add to this ### Understanding the context (the dot) When you execute a template you have an **initial data structure** available that contains different variables, like the settings files (in a flexible configuration), or the request data (in a request generator) to put a couple of different usages. The template has access to the data using a period `.` and called “dot”. You can print wherever you are the context like this: ```go-text-template {{ toJson . }} ``` You can access the data structure traversing its keys using the dot operator. For instance, if you have a `.var` like `{"a": { "b": "hi" } }` you can access the value `hi` using `{{ .var.a.b }}`. Variables can be passed as arguments to functions and actions. When you are in a `{{ range .var }}` (loop), or use a `{{ with .var}}`, you are passing a `.var` context. Inside the block you will have a **new dot**. For instance, considering `.var` has the structure above: ```go-text-template {{ with .var.a }} {{ .b }} {{end}} ``` Prints `hi`. As you can see the context inside the with is different, and we don’t access it like `.a.b`. When calling templates from templates (flexible config), make sure to add the final dot `.` to pass all the settings files to the next template or pass those variables that are needed: - `{{ template "hello.tmpl" . }}`: The hello template receives all setting files and works as its calling template. - `{{ template "hello.tmpl" .urls.users_api }}`: receives only the string value of the user API. - `{{ template "hello.tmpl" "hello world" }}`: receives only a constant string Only in the Enterprise edition you have an additional variable `.meta` holding directory metadata under the settings tree (maybe you want to traverse directory contents in a template). ### Using the `$` notation to access outsider context As we have seen, when making a loop with a `range` or accessing a `with`, the variables inside are relative to its context. Still, you can access outsider variables with the `$` notation. For instance with the same `{{.var}}` containing `{"a": { "b": "hi" } }`: ```go-text-template {{ with .var.a }} {{ .b }} and {{ $.var.a.b }} {{end}} ``` Prints `hi and hi`, because the `$` allowed you to get content from outsider its context. ### How to separate objects with commas A lot of times, you need to iterate content and separate it using a comma. To do so, place the comma insertion at the beginning instead of the end when the index in the loop is not zero: ```go-text-template {{ range $index, $endpoint := .endpoints_list }} {{if $index}},{{end}} { "endpoint": "{{ $endpoint.path }}", "backend": [] } {{end}} ``` The `{{if $index}},{{end}}` is adding the comma except on the first item because the index `0` does not meet the condition. ### Remove white spaces and line breaks When you use code `{{ blocks }}` on your templates, you can add a left dash `{{- blocks }}` to suppress preceding whitespaces and linebreaks or a right dash `{{ blocks -}}` to remove the following ones, or both `{{- blocks -}}`. Suppressing additional spaces is irrelevant in JSON but not in YAML. ### Iterate all files under settings or all keys in a map If you want to iterate all keys in a map, like the settings files, you must be aware that you need the key names in the first place. You need to use the `keys` function, which returns all the key names in the map, and then you can access its contents using the function `index YourMapName yourKeyName`. For instance, you can dump all settings file contents like this: ```go-text-template {{ range $idx, $setting := keys .}} {{- if $idx}},{{end -}} "{{ $setting }}": {{marshal (index $ $setting)}} {{end}} ``` In the example above, the `range` iterates the key names of `.` (not the object itself), which is all the settings in the root template. Then, the `marshal` dumps all the contents provided. The dollar sign `$` inside the index represents all your content under `.` outside the range. As you are inside a range (a different scope) the `.` belongs to the range context, so you need to pass the dollar to access the outsider/parent context. The `index` function gives you access to an element of the map, so `index $ $setting` is equivalent to `$[$setting]` in other languages. ### Inserting an external file as base64 A few fields in KrakenD require you to set their value in base64 format instead of the raw counterpart. For example, sometimes you want to version control the raw file in an external file and reference it as base64. To do so, you could have a template `render_as_base64.tmpl` with the following content: ```go-text-template {{/* Notice the dashes (-) at the beginning and end of the following code. They remove all spaces and linebreaks that appear before and after when the result outputs. */}} {{- $raw_content := include . -}} {{- $raw_content | b64enc -}} ``` And call it in the `krakend.tmpl` like this: ```go-text-template { "version": 3, "some_base_64_value": "{{template "render_as_base64.tmpl" "file.json"}}" } ``` ### Sprig functions Complementing the [Go built-in template language](http://golang.org/pkg/text/template/), Sprig adds more than 100 commonly used functions. For instance, you could **inject secrets from environment variables**. Like: ```go-text-template { "some_secret": "{{ env "SECRET_VARIABLE_NAME" }}" } ``` Sprig provides many functions in the following categories: - String Functions: `trim`, `wrap`, `randAlpha`, `plural` and more. - String List Functions: `splitList`, `sortAlpha` and more. - Integer Math Functions: `add`, `max`, `mul` and more. - Integer Slice Functions: `until`, `untilStep` - Float Math Functions: `addf`, `maxf`, `mulf` and more. - Date Functions: `now`, `date` and more. - Defaults Functions: `default`, `empty`, `coalesce`, `fromJson`, `toJson`, `toPrettyJson`, `toRawJson`, `ternary` - Encoding Functions: `b64enc`, `b64dec` and more. - Lists and List Functions: `list`, `first`, `uniq` and more. - Dictionaries and Dict Functions: `get`, `set`, `dict`, `hasKey`, `pluck`, `dig`, `deepCopy` and more. - Type Conversion Functions: `atoi`, `int64`, `toString` and more. - Path and Filepath Functions: `base`, `dir`, `ext`, `clean`, `isAbs`, `osBase`, `osDir`, `osExt`, `osClean`, `osIsAbs` - Flow Control Functions: `fail` - Advanced Functions - UUID Functions: `uuidv4` - OS Functions: `env`, `expandenv` - Version Comparison Functions: `semver`, `semverCompare` - Reflection: `typeOf`, `kindIs`, `typeIsLike` and more. - Cryptographic and Security Functions: `derivePassword`, `sha256sum`, `genPrivateKey` and more. - Network: `getHostByName` [Sprig documentation](http://masterminds.github.io/sprig/ "Sprig documentation") ### Flexible Configuration custom functions The following custom functions are available for the **flexible configuration**, but **not in other components**. To load external resources for the templates during runtime (partials, templates, and settings), you reference them in the templates as follows: - `{{ include "file.txt" }}`: Inserts the content of the `file.txt` “as is”. You can use any extension in these files. - `{{ template "file.tmpl" . }}`: Renders the Go template `file.tmpl` passing all its variables as context. The context is the final `.` you can see in the block. The `file.tmpl` can access this context using `{{ . }}`. The context can be a simple value (like a string) or an object/map with nested elements. - `{{ .setting_name }}`: All the settings files resolve to a tree that you can access in the templates. For instance, a `filename.json` is immediately available as the variable `{{ .filename }}` in the template. For instance, having a file `settings/urls.json` with the following content: ```json { "users_api": "https://users-api.mycompany.com", "inventory_api": "https://inventory-api.mycompany.com", "3rdparty": { "github": "https://api.github.com" } } ``` You can refer to those values in the template like `{{ .urls.users_api }}` (which resolves to `https://users-api.mycompany.com`). Or you could use nested content like `{{ .urls.3rdparty.github }}` and get `https://api.github.com`. As you can see, the first word after the dot is the filename (without the extension), and the following dots traverse the objects to the final value. #### 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`. ```go-text-template {{ marshal .urls }} ``` You can dump the context anywhere like this (useful for debugging): ```go-text-template {{ . | marshal }} ``` The example would write the entire content of the `urls.json` file. #### Include a partial To insert the content of an external partial file in place use: ```go-text-template {{ include "dir1/dir2/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 as **partials** and can have any name and extension. Filenames referenced are **case sensitive**, and although your host operating system might work with case insensitive files (e.g., A docker volume on Mac) when copied to a Docker image not respecting the case will fail. #### 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. The syntax is as follows: ```go-text-template {{ template "template_name.tmpl" .some_context }} ``` The template `template_name.tmpl` is executed and processed. The depicted variable `.some_context` is passed in the template as the context. #### The context The context is data you pass to a template as if it were a single parameter of a function. When the base template loads (e.g., the `krakend.tmpl`), it automatically **receives in the context the whole settings tree**. It means that if you dump the content of the context, you will see the entire tree made of all files and data structures in the settings dir. If you want to pass **all the settings tree** to the calling template, write just a dot `.`, which stands for “everything”. For instance: ```go-text-template {{ template "template_name.tmpl" . }} ``` The called template can see the object you passed as the context under `{{ . }}`. In this case, all the settings are available as in the base template. But sometimes templates need a smaller object, a constant string, or a number as a parameter. You can also do this, for instance: ```go-text-template {{ template "subtemplate1.tmpl" .some_setting.some_value }} {{ template "ratelimit_per_minute.tmpl" 100 }} ``` Go templates allow you to introduce handy stuff like conditionals or loops and create powerful configurations. #### Example file for flexible config There is a lot of theory so far. To demonstrate the usage of the flexible configuration, we will reorganize a configuration file into several pieces. This is a simple example to see the basics of the templates system: ``` . ├── krakend.tmpl ├── partials │ └── all_backends_extra_config.json └── settings ├── endpoint.json └── service.json ``` **partials/all\_backends\_extra\_config.json** In this file, we have written the content of the rate limit configuration and circuit breaker we want for any backend. This file is inserted when included “as is”, because it is a partial: ```json { "$schema": "https://www.krakend.io/schema/v2.13/backend_extra_config.json", "qos/ratelimit/proxy": { "max_rate": 100, "capacity": 100 }, "qos/circuit-breaker": { "interval": 10, "max_errors": 5, "timeout": 5 } } ``` **settings/service.json** In the settings directory, we write all the files whose values can be accessed as variables. ```json { "port": 8090, "default_hosts": [ "https://catalog-api-01.srv", "https://catalog-api-02.srv", "https://catalog-api-03.srv" ], "extra_config": { "security/http": { "allowed_hosts": [], "ssl_proxy_headers": { "X-Forwarded-Proto": "https" }, "ssl_certificate": "/opt/rsa.cert", "ssl_private_key": "/opt/rsa.key" } } } ``` **settings/endpoint.json** This file declares a couple of endpoints that feed on a single backend: ```json { "example_group": [ { "endpoint": "/users/{id}", "backend": "/v1/users?userId={id}" }, { "endpoint": "/posts/{id}", "backend": "/posts?postId={id}" } ] } ``` **krakend.tmpl** Finally, let’s introduce 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: ```go-text-template { "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 "all_backends_extra_config.json" }} } ]} {{ 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 }}`. --- # [Supported Configuration Formats](https://www.krakend.io/docs/configuration/supported-formats/) The expected configuration file format by default is `json`, but KrakenD can parse any of these file formats: - `.json` (recommended) - `.toml` - `.yaml` - `.yml` - `.properties` - `.props` - `.prop` - `.hcl` You can validate the syntax of any of these with [`krakend check`](/docs/configuration/check/) ## Why is JSON recommended? You are free to choose `json`, `yaml`, `toml` or any of the other formats listed above at your best convenience. But have in mind the following limitations when choosing a file format that is not `json`: - **Using the UI**: If you intend to generate or edit your configuration file using the [KrakenDesigner](https://designer.krakend.io), the input and output are always a `.json` file. - **Flexible Configuration**: If you want to split the configuration file into different pieces or use variables inside the configuration, the [flexible configuration](/docs/configuration/flexible-config/) works better with `json`, as formats that need a proper indentation present more problems to control it (e.g.: `yaml`). - **Documentation**: All our examples in the documentation and repositories are today shown in `json` format, so it’s always more convenient to reuse snippets of code (copy and paste!). - **Linting**: You cannot use the `--lint` flag on the [check command](/docs/configuration/check/) because expects json content to validate its schema (you could convert the file using a 3rd party tool though). You can still use the rest of the check commands. As you can see, if you are OK with the limitations above, you can freely choose any other format. --- # [Environment variables in the configuration](https://www.krakend.io/docs/configuration/environment-vars/) When KrakenD runs (whether with `run` or `check`), all the behavior is loaded from the [configuration file](/docs/configuration/structure/). Through environment variables, you can also set values. There are two different ways of injecting environment vars: - **Use a `KRAKEND_`-like reserved environment variable**: To override values set in the configuration. - **Set your own environment variables** when using the `{{env}}` function in [flexible configuration](/docs/configuration/flexible-config/) templates. ## Use a reserved environment variable There are a group of reserved environment variables that are automatically recognized by KrakenD when set. Examples are when you want to replace the `port`, the default `timeout`, or the configuration `name` (sent to your telemetry) that already exists in the configuration. In essence, you can replace any value in the configuration that lives in the root level, and is a string, an integer, or a boolean. To do it you only need to capitalize the property name and add a prefix `KRAKEND_`. Reserved variables are ignored unless the key exists in the configuration The following list of variables only set the desired values when you have its associated value in the configuration. They are meant to **override** settings **already present** in the configuration, but if you set one of them and there is no value in the configuration, it won’t have any effect. - Use `KRAKEND_CACHE_TTL` to override `cache_ttl` in the config (*string*) - Use `KRAKEND_DEBUG_ENDPOINT` to override `debug_endpoint` in the config (*boolean*) - Use `KRAKEND_DIALER_FALLBACK_DELAY` to override `dialer_fallback_delay` in the config (*string*) - Use `KRAKEND_DIALER_KEEP_ALIVE` to override `dialer_keep_alive` in the config (*string*) - Use `KRAKEND_DIALER_TIMEOUT` to override `dialer_timeout` in the config (*string*) - Use `KRAKEND_DISABLE_COMPRESSION` to override `disable_compression` in the config (*boolean*) - Use `KRAKEND_DISABLE_KEEP_ALIVES` to override `disable_keep_alives` in the config (*boolean*) - Use `KRAKEND_DISABLE_REST` to override `disable_rest` in the config (*boolean*) - Use `KRAKEND_DNS_CACHE_TTL` to override `dns_cache_ttl` in the config (*string*) - Use `KRAKEND_ECHO_ENDPOINT` to override `echo_endpoint` in the config (*boolean*) - Use `KRAKEND_EXPECT_CONTINUE_TIMEOUT` to override `expect_continue_timeout` in the config (*string*) - Use `KRAKEND_IDLE_CONNECTION_TIMEOUT` to override `idle_connection_timeout` in the config (*string*) - Use `KRAKEND_IDLE_TIMEOUT` to override `idle_timeout` in the config (*string*) - Use `KRAKEND_LISTEN_IP` to override `listen_ip` in the config (*string*) - Use `KRAKEND_MAX_HEADER_BYTES` to override `max_header_bytes` in the config (*integer*) - Use `KRAKEND_MAX_IDLE_CONNECTIONS` to override `max_idle_connections` in the config (*integer*) - Use `KRAKEND_MAX_IDLE_CONNECTIONS_PER_HOST` to override `max_idle_connections_per_host` in the config (*integer*) - Use `KRAKEND_MAX_SHUTDOWN_WAIT_TIME` to override `max_shutdown_wait_time` in the config (*string*) - Use `KRAKEND_NAME` to override `name` in the config (*string*) - Use `KRAKEND_PORT` to override `port` in the config (*integer*) - Use `KRAKEND_READ_HEADER_TIMEOUT` to override `read_header_timeout` in the config (*string*) - Use `KRAKEND_READ_TIMEOUT` to override `read_timeout` in the config (*string*) - Use `KRAKEND_RESPONSE_HEADER_TIMEOUT` to override `response_header_timeout` in the config (*string*) - Use `KRAKEND_SEQUENTIAL_START` to override `sequential_start` in the config (*boolean*) - Use `KRAKEND_TIMEOUT` to override `timeout` in the config (*string*) - Use `KRAKEND_USE_H2C` to override `use_h2c` in the config (*boolean*) - Use `KRAKEND_WRITE_TIMEOUT` to override `write_timeout` in the config (*string*) - Use `USAGE_DISABLE` to remove usage telemetry, a request to our stats server during startup or every 12 hours with anonymous non-sensitive information (e.g.: version, operative system, architecture, uptime - [see source code](https://github.com/krakend/krakend-usage)) (*boolean*) ### Reserved variable example For instance, take the following `krakend.json` configuration as an example: ```json { "version": 3, "timeout": "3s", "name": "Example gateway.", "cache_ttl": "0s" } ``` You could start the server with the following command which would allow you to override the values in the configuration: 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: ```json { "version": 3, "timeout": "500ms", "name": "Build ABC0123" } ``` **Important**: Notice that the `port` attribute is not present in the configuration, despite passing a `KRAKEND_PORT` parameter. This is because the `port` didn’t exist previously in the configuration file, and the environment variables can only **override** values. ## Setting your environment variables If you need to set content using environment variables at any level, you have can either use the [flexible configuration](/docs/configuration/flexible-config/), which includes a series of [advanced functions](/docs/configuration/templates/#sprig-functions) including an `env` function, or you can not use KrakenD at all and rely on the operating system `envsubst` command. Obviously you can also write your custom replacement process. ### Environment variables with Flexible Configuration Here is an example with Flexible Configuration: ```go-text-template { "version": 3, "name": "Configuration for {{ env "MY_POD_NAMESPACE" }}" } ``` When you use the flexible configuration, you can start KrakenD from the template that uses them. ### Environment variables with envsubst Another example is not to use any of the built-in features of KrakenD and rely on your operating system via the command `envusbst`. For instance, you have a configuration file `krakend.template.json` like the following: ```json { "version": 3, "name": "Configuration for $MY_POD_NAMESPACE" } ``` Then you can generate the final configuration `krakend.json` like this: Environment variable substitution  ``` $export MY_POD_NAMESPACE="my-namespace" && envsubst < krakend.template.json > krakend.json ``` The command, which is generally available in Linux distributions, takes a template file as input and outputs the same file with the environment variables replaced (you cannot override the same file). You have to be aware that missing variables are simply replaced by an empty string. Note: on Alpine-based containers, like the KrakenD image, you need to do an `apk add envsubst` to use this command. --- # [Hot Reload Feature](https://www.krakend.io/docs/developer/hot-reload/) The Docker tag `:watch` is a **development-phase image** that allows you to **restart KrakenD automatically after saving** or changing files inside the configuration folder, although **is not recommended for production workloads**. It works whether you use [flexible configuration](/docs/configuration/flexible-config/) or a single file (`krakend.json` or any other [supported format](/docs/configuration/supported-formats/)). The watch is a regular KrakenD binary wrapped in a [reflex watcher](https://github.com/cespare/reflex). When it detects that any piece inside the configuration has changed, it restarts the service, applying the changes. This behavior is very convenient while developing as it allows you to test new changes without manually restarting KrakenD back and forth You must build the tag `:watch` on the Community Edition Since KrakenD became an [official Docker image](https://hub.docker.com/_/krakend), the `:watch` tag is no longer available in the Docker registry **for the open source edition**. To use `:watch`, you **must build** the Docker tag as described below, but Enterprise users don’t need to build this image and can continue working as always as `:watch` is ready to [`docker pull`](/docs/enterprise/developer/hot-reload/). ## Building the watch image To use the `:watch` image on the Open Source edition, [clone the `krakend-watch` repository](https://github.com/krakend/krakend-watch/) and do the `docker build` as explained in its README: ```bash git clone https://github.com/krakend/krakend-watch.git cd krakend-watch docker build -t krakend:watch . ``` If you want to pin a specific KrakenD version, replace the `:latest` string on the `Dockerfile` with your desired version before building. After the build completes, you will be able to use `krakend:watch` normally. ## Watch usage The container expects you to mount the configuration as a volume mapped to `/etc/krakend`. For instance: Simple KrakenD watch  ``` $docker run --rm -v "$PWD:/etc/krakend" krakend:watch ``` When you use [flexible configuration](/docs/configuration/flexible-config/), you can pass the environment variables when starting the container. For instance: Hot reload with Flexible Configuration  ``` $docker run --rm -it -v "$PWD:/etc/krakend" \ -e "FC_ENABLE=1" \ -e "FC_OUT=compiled.json" \ -e "FC_PARTIALS=/etc/krakend/partials" \ -e "FC_SETTINGS=/etc/krakend/settings" \ -e "FC_TEMPLATES=/etc/krakend/templates" \ krakend:watch run -c krakend.tmpl ``` You can also integrate inside your IDE a watch to continuously check the configuration with the `check` command instead of `run`. ## Risks of hot-reloading in production As we said, in the introduction, this tag is for development. Because you might be tempted to use it in production, we don’t recommend it because: - It kills the server without a graceful option. If a user consumes an endpoint, the connection will be interrupted immediately, no matter if it ended or not. - You might break the configuration after a save and leave the server unable to restart. - KrakenD runs through a watch binary wrapper, hurting it, and will: - Decrease the performance (10 to 30% less throughput) - Decrease the stability of the binary (you might have undesired panics and unexpected exits) ### Why KrakenD needs restarting? - **Performance**: This is the #1 reason, and strong enough. **KrakenD focuses on performance**, and during startup, calculates routes and decision trees, and the configuration is never read again. - **GitOps**: We believe an API contract must go under the version control system and, after that, be released (and through CI/CD if possible), but never altered at runtime by throwing curls or whatever (Stick to [POLA](https://en.wikipedia.org/wiki/Principle_of_least_astonishment)). - **Consistency**: Imagine yourself distributing configuration updates in an ecosystem like the one KrakenD is offering: **independent, non-coordinated, multi-region, and stateless**. It would require you to develop complex strategies to ensure consistency that is far out of the scope of an API Gateway. - **It happens in other servers, too**: You are used to this behavior from Varnish, Apache, Nginx, Mysql, or any other major service. All of them are reloaded when configuration changes and the Internet is still running. [Blue/green deployment](/docs/deploying/#use-bluegreen-or-similar-deployment-strategy) and similar approaches were invented to ensure there is never downtime even if you change the service. --- # [Hot Reload Feature (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/developer/hot-reload/) The Docker tag `:watch` is a **development-phase image** that allows you to **restart KrakenD automatically after saving** or changing files inside the configuration folder, although **is not recommended for production workloads**. It works whether you use [flexible configuration](/docs/configuration/flexible-config/) or a single file (`krakend.json` or any other [supported format](/docs/configuration/supported-formats/)). The watch is a regular KrakenD binary wrapped in a [reflex watcher](https://github.com/cespare/reflex). When it detects that any piece inside the configuration has changed, it restarts the service, applying the changes. This behavior is very convenient while developing as it allows you to test new changes without manually restarting KrakenD back and forth ## Watch usage The container expects you to mount the configuration as a volume mapped to `/etc/krakend`. For instance: Simple KrakenD watch  ``` $docker run --rm -v "$PWD:/etc/krakend" krakend/krakend-ee:watch ``` When you use [flexible configuration](/docs/configuration/flexible-config/), you can pass the environment variables when starting the container. For instance: Hot reload with Flexible Configuration  ``` $docker run --rm -it -v "$PWD:/etc/krakend" \ -e "FC_CONFIG=/etc/krakend/flexible_config.json" \ krakend/krakend-ee:watch run -c krakend.tmpl ``` You can also integrate inside your IDE a watch to continuously check the configuration with the `check` command instead of `run`. ## Risks of hot-reloading in production As we said, in the introduction, this tag is for development. Because you might be tempted to use it in production, we don’t recommend it because: - It kills the server without a graceful option. If a user consumes an endpoint, the connection will be interrupted immediately, no matter if it ended or not. - You might break the configuration after a save and leave the server unable to restart. - KrakenD runs through a watch binary wrapper, hurting it, and will: - Decrease the performance (10 to 30% less throughput) - Decrease the stability of the binary (you might have undesired panics and unexpected exits) ### Why KrakenD needs restarting? - **Performance**: This is the #1 reason, and strong enough. **KrakenD focuses on performance**, and during startup, calculates routes and decision trees, and the configuration is never read again. - **GitOps**: We believe an API contract must go under the version control system and, after that, be released (and through CI/CD if possible), but never altered at runtime by throwing curls or whatever (Stick to [POLA](https://en.wikipedia.org/wiki/Principle_of_least_astonishment)). - **Consistency**: Imagine yourself distributing configuration updates in an ecosystem like the one KrakenD is offering: **independent, non-coordinated, multi-region, and stateless**. It would require you to develop complex strategies to ensure consistency that is far out of the scope of an API Gateway. - **It happens in other servers, too**: You are used to this behavior from Varnish, Apache, Nginx, Mysql, or any other major service. All of them are reloaded when configuration changes and the Internet is still running. [Blue/green deployment](/docs/deploying/#use-bluegreen-or-similar-deployment-strategy) and similar approaches were invented to ensure there is never downtime even if you change the service. --- # [Working Directory: Specifying paths](https://www.krakend.io/docs/configuration/working-directory/) Many components and options in KrakenD allow you to specify paths. In all of them, you can use **absolute** or **relative** paths. For **absolute paths**, no possible interpretation or mistake arises when reading them. For instance, if you write `/etc/krakend/krakend.json`, you know exactly where this file is. Absolute paths are as clear as water but less convenient when your environments have different locations. **Relative paths**, on the other hand, are helpful because you only specify a small part, but there is usually the question of the location of their corresponding base directory. For instance, if you write `krakend.json`, `./krakend.json`, or `./config/krakend.json`, what is their base directory? The answer to this question is that all relative paths use the **working directory** as the base path. In a Docker container, for instance, this is what you specify in the instruction `WORKDIR`. Our Docker images use `/etc/krakend` unless you overwrite it in your `Dockerfile`. In other installations, although we aim to default to `/etc/krakend`, you can still run the software from a different place and have a different working directory. ## How to get the working directory The short answer is to start the gateway and look for the following line early in the logs: ```log yyyy/mm/dd hh:mm:ss KRAKEND INFO: Working directory is /etc/krakend ``` This informative line was introduced in KrakenD 2.5 Now, if you set a relative path like `a/b.tmpl`, for instance, and the log tells you that the file does not exist, navigate to the base directory you saw in the console and see if the file is inside that location. --- # [Upgrading KrakenD from an older version](https://www.krakend.io/docs/configuration/migrating/) Upgrading to a new version of KrakenD is designed to be straightforward, thanks to our commitment to **maintaining backward compatibility** across versions within the same major release (e.g., within `2.x` versions). You can generally update KrakenD to a newer version without altering your configuration. However, to ensure optimal performance and access to the latest features, reviewing and adjusting your configuration is wise. KrakenD’s upgrade policy Our policy focuses on **minimizing disruption by preserving compatibility with previous versions**. However, important changes can occur between versions, such as the deprecation of components, the introduction of superior alternatives, the relocation of properties, or the outright removal of outdated features. Although KrakenD aims to ensure your existing setup will continue to run, these changes may necessitate adjustments to your configuration for improved stability and performance. ## Upgrade steps 1. **Review the [changelog](/changelog/)**. This document provides a chronological list of releases detailing new features, bug fixes, and possible breaking changes between major versions. 2. **Adjust your configuration and run the linter** as needed. Below, you’ll find the changes between versions. Scroll down to your current version and apply all changes above it. Then, run [the linter](/docs/configuration/check/) (`krakend check --lint`), which is designed to scrutinize your configuration rigorously. 3. **Update the KrakenD binary**. Replace the existing binary file with the latest version. This process varies depending on how KrakenD was installed and whether it is container-based or not. 4. If you have custom plugins, you need to recompile them with the enterprise builder, and rename in your CI/CD the [test](/docs/extending/test-plugin/) and [check](/docs/extending/check-plugin/) plugin commands. Jumping several versions To upgrade when you are more than one version away from the latest, adjust the configuration for all the versions that the upgrade comprehends. *The list below is automatically generated based on the changelog.* ## Upgrade to CE v2.13 (latest) Configuration changes needed to upgrade to v2.13: **This version does not require any configuration change.** ## Upgrade to CE v2.12 Configuration changes needed to upgrade to v2.12: **This version does not require any configuration change.** ## Upgrade to CE v2.11 Configuration changes needed to upgrade to v2.11: - End of support for Debian 11 and older versions (or Ubuntu 21.10 and older) for its Glibc incompatibility with secure Go versions. ## Upgrade to CE v2.10 Configuration changes needed to upgrade to v2.10: - The `devopsfaith/krakend` Docker container is no longer maintained because [`krakend` is now an official Docker image](/blog/official-docker-image/). Please use the `krakend` image instead. ## Upgrade to CE v2.9 Configuration changes needed to upgrade to v2.9: **This version does not require any configuration change.** ## Upgrade to CE v2.8 Configuration changes needed to upgrade to v2.8: - The `letgoapp/influx` namespace, which was available for historical reasons, has been removed from the configuration schema. Rename it to `telemetry/influx` for a quick fix (which is also deprecated), or upgrade to OpenTelemetry for a long-term solution. ## Upgrade to CE v2.7 Configuration changes needed to upgrade to v2.7: - The properties `public_key` and `private_key` of the `tls` configuration have been moved under an array `keys` to facilitate multiple certificates and domains. [Read more](/docs/service-settings/tls/#upgrade-from-legacy-key-declaration) ## Upgrade to CE v2.6 Configuration changes needed to upgrade to v2.6: - Removed from schema previosuly deprecated fields `allow_insecure_connections` in the root. Must be declared [under `client_tls.allow_insecure_connections`](/docs/service-settings/tls/#client-tls-settings) now. - Removed from schema previously deprecated `prefetch_size` and `prefetch_count` attributes from [AMQP](/docs/backends/amqp-consumer/). These attributes did not have any effect in the software. - The telemetry exporters based in `telemetry/opencensus`, although they are still available, **won’t receive further updates** and will be deprecated in the future. All efforts are focused on [OpenTelemetry](/docs/telemetry/opentelemetry/) instead. - The telemetry exporter `telemetry/influx` is still available, but **won’t receive further updates** and will be deprecated in the future. All efforts are focused on [OpenTelemetry](/docs/telemetry/opentelemetry/) instead. ## Upgrade to CE v2.5 Configuration changes needed to upgrade to v2.5: - The flag `router.use_h2c` has [moved to the root level](/docs/service-settings/http-server-settings/#use_h2c) as `use_h2c`, and its usage inside the `router` is marked as deprecated. ## Upgrade to CE v2.4 Configuration changes needed to upgrade to v2.4: - The flag `allow_insecure_connections` was relocated under `client_tls` in v2.3 and the old location on `tls` is no longer supported. - The [flag `prefer_server_cipher_suites`](/docs/v2.3/service-settings/tls/#prefer_server_cipher_suites) is no longer supported. Servers now select the best mutually supported cipher suite automatically based on the logic that considers inferred client hardware, server hardware, and security. ## Upgrade to CE v2.3 Configuration changes needed to upgrade to v2.3: - The flag `tls.allow_insecure_connections` is now marked as deprecated because it has moved to `client_tls.allow_insecure_connections`. The support under `tls` will be removed in the next version. ## Upgrade to CE v2.2 Configuration changes needed to upgrade to v2.2: **This version does not require any configuration change.** ## Upgrade to CE v2.1 Configuration changes needed to upgrade to v2.1: **This version does not require any configuration change.** ## Upgrade to v2.0 from v0.x or v1.x The KrakenD 2.0 release is a major version that **simplifies the configuration** of `v1.x` and **standardizes field names** that were using different criteria to declare the attributes. The migration tool allows you to migrate from KrakenD `0.x` or `1.x` to `2.0` [Download migration tool](https://github.com/krakend/krakend-config-migrator "Download migration tool") ### How to use the legacy migration tool - Use `git` or similar DVCS to track the changes. Compare the differences at the end. - Download the configuration [migration tool](https://github.com/krakend/krakend-config-migrator) and execute it passing the path to your KrakenD project - Review the changes the migration tool did to your config and start the config with the new version **If you have custom go plugins**, recompile them. KrakenD has now a command [`krakend check-plugin`](/docs/extending/check-plugin/) and [`krakend test-plugin`](/docs/extending/test-plugin/) to test them. Special attention to short words The migration script replaces words used by KrakenD in the past and are no longer supported that might collide with wording you use in your endpoints. Words like `whitelist` or `blacklist` will be replaced by `allow` and `deny`. Make sure to check the changes in the configuration and ensure that the migration tool didn’t change any endpoint definition using those names. The migration tool will take care of what is described below for you, and is actually quite simple. For the most part, what it does is to rename configurations and namespaces. The following list is what it takes care of: #### Renamed namespaces The most visible change is that all non-core components (this is everything outside of [Lura](https://luraproject.org)) were declared inside an `extra_config` section, using a looong **namespace**. That namespace contained what could look like a URL (e.g., `github.com/devopsfaith/krakend-jose/validator`) and generated frequent misunderstandings year after year. Now, all namespaces have been categorized and simplified to a description of their functionality (e.g., `auth/validator`). See the migration tool’s source code for the complete list of renamed namespaces. #### Consistent attribute naming Another relevant change is that some attributes have been renamed to be consistent across all configurations. Prior to 2.0, some attributes used hyphenation (`hyphen-ation`), while others used snake case (`snake_case`) or camel case (`camelCase`). Now, we use `snake_case` everywhere if possible. #### Removed deprecated elements The final change is that all functionalities and attributes marked as deprecated in 1.4 have been removed. - `whitelist` is removed, and only `allow` is recognized now - `blacklist` is removed, and only `deny` is recognized now - `krakend-etc` is no longer included in the binary - `krakend-consul`, the integration of consul for the JWT revoker, is no longer included in the binary. Summing up, see the before and after of the following snippet which has 3 of the changes mentioned above. **KrakenD 1**: ```json { "endpoint": "/foo", "extra_config": { "github.com/devopsfaith/krakend-jose/validator" { "alg": "RS256", "jwk-url": "https://url/to/jwks.json" } }, "backend": [ { "url_pattern": "/foo", "whitelist": ["field1", "field2"] } ] } ``` **KrakenD 2**: Differences highlighted ```json { "endpoint": "/foo", "extra_config": { "auth/validator": { "alg": "RS256", "jwk_url": "https://url/to/jwks.json" } }, "backend": [ { "url_pattern": "/foo", "allow": ["field1", "field2"] } ] } ``` --- # [Service Settings Configuration](https://www.krakend.io/docs/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). They determine how you start the HTTP server, enforce security parameters, or define behavioral options like which reporting activities occur, to name a few examples. Examples of service settings are, the [listening port](/docs/service-settings/http-server-settings/), [disabling keep alives](/docs/service-settings/http-transport-settings/), enabling [metrics and traces](/docs/telemetry/), [listening https](/docs/service-settings/tls/), or enabling [CORS](/docs/service-settings/cors/) to name a few. If you haven’t done it yet, read [Understanding the configuration file](/docs/configuration/structure/). All service settings are written directly in the root of the configuration file or its corresponding `extra_config`. So, for instance, here there is a configuration file describing a service listening on port 8080 with extended logging enabled: ```json { "version": 3, "port": 8080, "output_encoding": "json", "host": ["http://myapi"], "timeout": "3s", "cache_ttl": "60s", "listen_ip": "192.168.1.3", "endpoints": [], "extra_config": { "telemetry/logging": { "level": "WARNING", "syslog": true, "stdout": true } } } ``` The service accepts **numerous configuration options** that you’ll find explained through the rest of the documentation, but here is a preview of the most important ones: ##### Preview of important service configuration fields \* required fields ##### [`cache_ttl`](https://www.krakend.io/docs/service-settings/#cache_ttl "Click to copy link to cache_ttl") string Sets a default `Cache-Control: public, max-age=%d` header to all endpoints where `%d` is the conversion to seconds of any duration you write, indicating for how long the client (or CDN) can cache the content of the request. You can override this value per endpoint, but setting an endpoint to 0 will use the default value instead. Notice that KrakenD does not cache the content with this parameter, but tells the client how to do it. Defaults to `0s` (no cache). **For KrakenD cache, see [backend caching](https://www.krakend.io/docs/backends/caching/)**. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`endpoints`](https://www.krakend.io/docs/service-settings/#endpoints "Click to copy link to endpoints") array of objects Your API contract, or the list of all paths recognized by this gateway. The paths `/__health/`, `/__debug/`, `/__echo/`, `/__catchall`, and `/__stats/` are reserved by the system and you cannot declare them. Their existence depends on their respective settings. ##### [`extra_config`](https://www.krakend.io/docs/service-settings/#extra_config "Click to copy link to extra_config") object The optional configuration that extends the core functionality of the gateway is specified here. The `extra_config` at this level enables service components, meaning that they apply globally to all endpoints or activity. ##### [`host`](https://www.krakend.io/docs/service-settings/#host "Click to copy link to host") array of strings The default host list for all backends if they specify none. ##### [`listen_ip`](https://www.krakend.io/docs/service-settings/#listen_ip "Click to copy link to listen_ip") string The IP address that KrakenD listens to in IPv4 or IPv6. An empty string, or no declaration at all means listening on all interfaces. The inclusion of `::` is meant for IPv6 format only (**this is not the port**). Examples of valid addresses are `192.0.2.1` (IPv4), `2001:db8::68` (IPv6). The values `::` and `0.0.0.0` listen to all addresses and both are valid for IPv4 and IPv6 simultaneously. Examples: `"172.12.1.1"` , `"::1"` Defaults to `"0.0.0.0"` ##### [`output_encoding`](https://www.krakend.io/docs/service-settings/#output_encoding "Click to copy link to output_encoding") The encoding used to display the content to the end-user. This setting is the default for all endpoints, unless they have another `output_encoding` overrinding this value. Possible values are: `"json"` , `"fast-json"` , `"json-collection"` , `"xml"` , `"negotiate"` , `"string"` , `"no-op"` Defaults to `"json"` ##### [`port`](https://www.krakend.io/docs/service-settings/#port "Click to copy link to port") integer The TCP port where KrakenD is listening to. Recommended value is in the range 1024-65535 to run as an unpriviliged user Defaults to `8080` ##### [`timeout`](https://www.krakend.io/docs/service-settings/#timeout "Click to copy link to timeout") string Defines a default timeout for all endpoints. Can be overriden per endpoint. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"2s"` ##### [`version`](https://www.krakend.io/docs/service-settings/#version "Click to copy link to version") * The syntax version tells KrakenD how to read this configuration. This is not the KrakenD version. Each KrakenD version is linked to a syntax version, and since KrakenD v2.0 the version must be `3` Possible values are: `3` Schema: [https://www.krakend.io/schema/v2.13/krakend.json](https://www.krakend.io/schema/v2.13/krakend.json "JSON schema definition") Other service-level settings you can add: - [HTTP server settings](/docs/service-settings/http-server-settings/) - [HTTP transport settings](/docs/service-settings/http-transport-settings/) - [Router settings](/docs/service-settings/router-options/) - [SSL/TLS](/docs/service-settings/tls/) --- # [Redis connection pools for stateful functionality (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/service-settings/redis-connection-pools/) KrakenD’s architecture allows developers to define all Redis connections in a single `redis` namespace, so you can reuse the definitions in other components across the configuration. You can configure both **connection pools** and **clusters**. Whether you use one option or both, each pool and cluster is identified by a **unique name** that must not clash (make sure of it), which is then referenced by the components requiring a connection. This approach eliminates the need to repeatedly specify Redis connection details in every component configuration. For example, a configuration that defines 2 connection pools and 1 cluster could be: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "extra_config": { "redis": { "connection_pools": [ { "name": "shared_redis_pool", "address": "192.168.1.45:6379" }, { "name": "ratelimit_counters", "address": "192.168.1.52:6379", "db": 2 } ], "clusters": [ { "name": "shared_redis_cluster", "addresses": ["192.168.1.90:6379", "192.168.1.91:6379"] } ] } } } ``` The block above creates two Redis connection pools named `shared_redis_pool` and `ratelimit_counters`, and it also creates a connection set to a Redis Cluster `shared_redis_cluster` that utilizes internally two different addresses. These names are how other components (like the [Redis-based service rate limit](/docs/enterprise/throttling/global-rate-limit/)) can select which one to use. ## Configuration of Redis connection pools Different pools can connect to different redis instances, or to the same instances but with different parameters. The configuration allows you to declare extra options to fine tune how the pool should behave: secure connection, retries, etc .. Best practices to select those parameters will depend on the components that will use the pool. The full list of options is explained below. ##### Configuration of Redis Connection Pools \* required fields ##### [`connection_pools`](https://www.krakend.io/docs/enterprise/service-settings/redis-connection-pools/#connection_pools "Click to copy link to connection_pools") array of objects Defines all the connetion pools available to Redis functionality. The different components requiring Redis will access the pool based on its name Each item of connection\_pools accepts the following properties: ##### [`address`](#connection_pools-address "Click to copy link to address") * string The Redis host where you want to connect using the format `host:port`. Example: `"shared.redis.example.com"` ##### [`client_name`](#connection_pools-client_name "Click to copy link to client_name") string You can set how this connection shows in Redis when listing all the connections `CONN LIST` Example: `"krakend_pool"` ##### [`conn_max_idle_time`](#connection_pools-conn_max_idle_time "Click to copy link to conn_max_idle_time") string The maximum amount of time a connection may be idle. Should be less than server’s timeout.Expired connections may be closed lazily before reuse. If <= 0, connections are not closed due to a connection’s idle time. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30m"` ##### [`conn_max_life_time`](#connection_pools-conn_max_life_time "Click to copy link to conn_max_life_time") string The maximum amount of time a connection may be reused. When the attribute is not declared, the connection does not expire Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). ##### [`db`](#connection_pools-db "Click to copy link to db") integer The database number to select after connecting Defaults to `0` ##### [`dial_timeout`](#connection_pools-dial_timeout "Click to copy link to dial_timeout") string Dial timeout for establishing new connections Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"5s"` ##### [`max_active_conns`](#connection_pools-max_active_conns "Click to copy link to max_active_conns") integer Maximum number of connections allocated by the pool at a given time. When zero, there is no limit on the number of connections in the pool. Defaults to `0` ##### [`max_idle_conns`](#connection_pools-max_idle_conns "Click to copy link to max_idle_conns") integer Maximum number of idle connections. The value `0` means connections not closed Defaults to `0` ##### [`max_retries`](#connection_pools-max_retries "Click to copy link to max_retries") integer The number of times you want to retry a Redis command using a different connection from the pool, applying a random delay between `min_retry_backoff` and `max_retry_backoff`. Use `-1` to never retry (it might not be a good idea) Defaults to `3` ##### [`max_retry_backoff`](#connection_pools-max_retry_backoff "Click to copy link to max_retry_backoff") string Every retry is executed increasing randomly starting at the `min_retry_backoff` until the `max_retry_backoff` is reached. This is the longest possible time. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"512ms"` ##### [`min_idle_conns`](#connection_pools-min_idle_conns "Click to copy link to min_idle_conns") integer Minimum number of idle connections which is useful when establishing new connection is slow. `0` means connections are not closed. Defaults to `0` ##### [`min_retry_backoff`](#connection_pools-min_retry_backoff "Click to copy link to min_retry_backoff") string Every retry is executed increasing randomly starting at the `min_retry_backoff` until the `max_retry_backoff` is reached. This is the shortest possible time. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"8ms"` ##### [`name`](#connection_pools-name "Click to copy link to name") * string The connection pool name. This is an arbitrary name used to reference this connection in other parts of the configuration needing Redis. Examples: `"shared_instance"` , `"Redis_for_ratelimit"` ##### [`opentelemetry`](#connection_pools-opentelemetry "Click to copy link to opentelemetry") object The OpenTelemetry configuration establishing how to report Redis connection activity ##### [`disable_metrics`](#opentelemetry-disable_metrics "Click to copy link to disable_metrics") boolean Disables any metrics of this Redis pool Defaults to `false` ##### [`disable_traces`](#opentelemetry-disable_traces "Click to copy link to disable_traces") boolean Disables any traces of this Redis pool Defaults to `false` ##### [`traces_static_attributes`](#opentelemetry-traces_static_attributes "Click to copy link to traces_static_attributes") array Show properties Static attributes you want to pass for traces. Each item of traces\_static\_attributes accepts the following properties: ##### [`key`](#traces_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#traces_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send ##### [`password`](#connection_pools-password "Click to copy link to password") string Sets the password to connect to Redis. ##### [`pool_size`](#connection_pools-pool_size "Click to copy link to pool_size") integer The pool size defines the number of concurrent Redis commands that can be executed by this Redis pool. Take into account that this only saves the connection time to the server, but, Redis will still be a non-concurrent service. When `0` it maps to its default 10 Defaults to `10` ##### [`pool_timeout`](#connection_pools-pool_timeout "Click to copy link to pool_timeout") string Amount of time the client waits for a connection if all connections are busy. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). ##### [`tls`](#connection_pools-tls "Click to copy link to tls") object An object with any [client TLS configuration](https://www.krakend.io/docs/enterprise/service-settings/tls/#client-tls-settings) to this connection ##### [`user_name`](#connection_pools-user_name "Click to copy link to user_name") string The username to connect to Redis Schema: [https://www.krakend.io/schema/v2.13/redis.json](https://www.krakend.io/schema/v2.13/redis.json "JSON schema definition") ## Configuration of Redis clusters The configuration of Redis cluster is identical to connection pools, except that the `addresses` field is an array, that the `db` is not available, and a new field `max_redirects` is added. A simple configuration would look like this: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "extra_config": { "redis": { "clusters": [ { "name": "shared_redis_cluster", "addresses": ["192.168.1.45:6379", "192.168.1.52:6379"] } ] } } } ``` The full list of options is: ##### Configuration of Redis Clusters \* required fields ##### [`clusters`](https://www.krakend.io/docs/enterprise/service-settings/redis-connection-pools/#clusters "Click to copy link to clusters") array of objects Defines all the clusters available to Redis functionality. The different components requiring Redis will access the pool based on its name Each item of clusters accepts the following properties: ##### [`addresses`](#clusters-addresses "Click to copy link to addresses") * array of strings The list of redis members that conform the cluster using the format `host:port`. All members must be WRITEABLE Example: `["192.168.1.45:6379","192.168.1.52:6379"]` ##### [`client_name`](#clusters-client_name "Click to copy link to client_name") string You can set how this connection shows in Redis when listing all the connections `CONN LIST` Example: `"krakend_pool"` ##### [`conn_max_idle_time`](#clusters-conn_max_idle_time "Click to copy link to conn_max_idle_time") string The maximum amount of time a connection may be idle. Should be less than server’s timeout.Expired connections may be closed lazily before reuse. If <= 0, connections are not closed due to a connection’s idle time. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30m"` ##### [`conn_max_life_time`](#clusters-conn_max_life_time "Click to copy link to conn_max_life_time") string The maximum amount of time a connection may be reused. When the attribute is not declared, the connection does not expire Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). ##### [`dial_timeout`](#clusters-dial_timeout "Click to copy link to dial_timeout") string Dial timeout for establishing new connections Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"5s"` ##### [`max_active_conns`](#clusters-max_active_conns "Click to copy link to max_active_conns") integer Maximum number of connections allocated by the pool at a given time. When zero, there is no limit on the number of connections in the pool. Defaults to `0` ##### [`max_idle_conns`](#clusters-max_idle_conns "Click to copy link to max_idle_conns") integer Maximum number of idle connections. The value `0` means connections not closed Defaults to `0` ##### [`max_redirects`](#clusters-max_redirects "Click to copy link to max_redirects") integer The maximum number of redirects to follow when requesting a key that is in another instance Defaults to `3` ##### [`max_retries`](#clusters-max_retries "Click to copy link to max_retries") integer The number of times you want to retry a Redis command using a different connection from the pool, applying a random delay between `min_retry_backoff` and `max_retry_backoff`. Use `-1` to never retry (it might not be a good idea) Defaults to `3` ##### [`max_retry_backoff`](#clusters-max_retry_backoff "Click to copy link to max_retry_backoff") string Every retry is executed increasing randomly starting at the `min_retry_backoff` until the `max_retry_backoff` is reached. This is the longest possible time. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"512ms"` ##### [`min_idle_conns`](#clusters-min_idle_conns "Click to copy link to min_idle_conns") integer Minimum number of idle connections which is useful when establishing new connection is slow. `0` means connections are not closed. Defaults to `0` ##### [`min_retry_backoff`](#clusters-min_retry_backoff "Click to copy link to min_retry_backoff") string Every retry is executed increasing randomly starting at the `min_retry_backoff` until the `max_retry_backoff` is reached. This is the shortest possible time. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"8ms"` ##### [`name`](#clusters-name "Click to copy link to name") * string The connection pool name. This is an arbitrary name used to reference this connection in other parts of the configuration needing Redis. Examples: `"shared_instance"` , `"Redis_for_ratelimit"` ##### [`opentelemetry`](#clusters-opentelemetry "Click to copy link to opentelemetry") object The OpenTelemetry configuration establishing how to report Redis connection activity ##### [`disable_metrics`](#opentelemetry-disable_metrics "Click to copy link to disable_metrics") boolean Disables any metrics of this Redis pool Defaults to `false` ##### [`disable_traces`](#opentelemetry-disable_traces "Click to copy link to disable_traces") boolean Disables any traces of this Redis pool Defaults to `false` ##### [`traces_static_attributes`](#opentelemetry-traces_static_attributes "Click to copy link to traces_static_attributes") array Show properties Static attributes you want to pass for traces. Each item of traces\_static\_attributes accepts the following properties: ##### [`key`](#traces_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#traces_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send ##### [`password`](#clusters-password "Click to copy link to password") string Sets the password to connect to Redis. ##### [`pool_size`](#clusters-pool_size "Click to copy link to pool_size") integer The pool size defines the number of concurrent Redis commands that can be executed by this Redis pool. Take into account that this only saves the connection time to the server, but, Redis will still be a non-concurrent service. When `0` it maps to its default 10 Defaults to `10` ##### [`pool_timeout`](#clusters-pool_timeout "Click to copy link to pool_timeout") string Amount of time the client waits for a connection if all connections are busy. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). ##### [`tls`](#clusters-tls "Click to copy link to tls") object An object with any [client TLS configuration](https://www.krakend.io/docs/enterprise/service-settings/tls/#client-tls-settings) to this connection ##### [`user_name`](#clusters-user_name "Click to copy link to user_name") string The username to connect to Redis Schema: [https://www.krakend.io/schema/v2.13/redis.json](https://www.krakend.io/schema/v2.13/redis.json "JSON schema definition") ## Best practices for Redis connection management While the library sets defaults for all the options (except the address and the name), you might want to tweak several of the parameters available. Generally speaking, **the additional parameters are not needed**. A few things to have in mind when configuring pools and clusters: - Set realistic timeouts and limits: Use appropriate values for timeouts and connection limits to prevent overloading Redis and ensure smooth operations during peak traffic. - Don’t disable monitoring: Let OpenTelemetry to track metrics and traces, helping you identify and address potential bottlenecks. Add the [Redis Grafana dashboard](/docs/enterprise/telemetry/grafana/) for greater visibility. - Align pools with functionality: Use separate pools for different functionalities to isolate workloads and ensure predictable performance. --- # [HTTP Server Settings](https://www.krakend.io/docs/service-settings/http-server-settings/) KrakenD starts an HTTP server to offer the API Gateway server. You can personalize some of the settings used to start the service and also override the default settings of the underlying Go [standard library](https://pkg.go.dev/net/http#Server). If you want to customize any of the settings below, they must be written at the top level of the configuration. ##### Fields of `"_root"` \* required fields ##### [`cache_ttl`](https://www.krakend.io/docs/service-settings/http-server-settings/#cache_ttl "Click to copy link to cache_ttl") string Sets a default `Cache-Control: public, max-age=%d` header to all endpoints where `%d` is the conversion to seconds of any duration you write, indicating for how long the client (or CDN) can cache the content of the request. You can override this value per endpoint, but setting an endpoint to 0 will use the default value instead. Notice that KrakenD does not cache the content with this parameter, but tells the client how to do it. Defaults to `0s` (no cache). **For KrakenD cache, see [backend caching](https://www.krakend.io/docs/backends/caching/)**. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`disable_rest`](https://www.krakend.io/docs/service-settings/http-server-settings/#disable_rest "Click to copy link to disable_rest") boolean Endpoints require in its `endpoint` definition the usage of a RESTful pattern. If you require unrestful patterns, like `/file.{ext}` (instead of its RESTful counterpart `/file/{ext}`), then you must set this parameter to `true`. You can use multiple variables if needed, but only one can be in an unrestful position, and when you do, it must be in the last position of the definition. E.g.: you can declare an endpoint `/file/{name}/base.{ext}` but you cannot do `/file.{ext}.json` because the variable `{ext}` is not in the last position of the definitino, and therefore the remaining path after `{ext}` is ignored by the router. Defaults to `false` ##### [`idle_timeout`](https://www.krakend.io/docs/service-settings/http-server-settings/#idle_timeout "Click to copy link to idle_timeout") string The maximum amount of time to wait for the next request when keep-alives are enabled. If `idle_timeout` is zero, the value of `read_timeout` is used. If both are zero, there is no timeout. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`listen_ip`](https://www.krakend.io/docs/service-settings/http-server-settings/#listen_ip "Click to copy link to listen_ip") string The IP address that KrakenD listens to in IPv4 or IPv6. An empty string, or no declaration at all means listening on all interfaces. The inclusion of `::` is meant for IPv6 format only (**this is not the port**). Examples of valid addresses are `192.0.2.1` (IPv4), `2001:db8::68` (IPv6). The values `::` and `0.0.0.0` listen to all addresses and both are valid for IPv4 and IPv6 simultaneously. Examples: `"172.12.1.1"` , `"::1"` Defaults to `"0.0.0.0"` ##### [`max_header_bytes`](https://www.krakend.io/docs/service-settings/http-server-settings/#max_header_bytes "Click to copy link to max_header_bytes") integer Allows overriding the maximum size of headers sent in bytes. It does not limit the request body. When the value is zero, the default is used instead (1MB) Defaults to `1000000` ##### [`max_shutdown_wait_time`](https://www.krakend.io/docs/service-settings/http-server-settings/#max_shutdown_wait_time "Click to copy link to max_shutdown_wait_time") string The gateway gracefully shuts down, waiting for all active connections to drain. Nevertheless, persistent connections or long timeouts may leave the shutdown process running for an extended period. Set the maximum time to wait for all the remaining requests to finish when shutting down the service. After this time, the service will force a shutdown. Defaults to `0s` (wait indefinitely). Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`port`](https://www.krakend.io/docs/service-settings/http-server-settings/#port "Click to copy link to port") integer The TCP port where KrakenD is listening to. Recommended value is in the range 1024-65535 to run as an unpriviliged user Defaults to `8080` ##### [`read_header_timeout`](https://www.krakend.io/docs/service-settings/http-server-settings/#read_header_timeout "Click to copy link to read_header_timeout") string The amount of time allowed to read request headers. The connection’s read deadline is reset after reading the headers and the Handler can decide what is considered too slow for the body. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`read_timeout`](https://www.krakend.io/docs/service-settings/http-server-settings/#read_timeout "Click to copy link to read_timeout") string Is the maximum duration for reading the entire request, including the body. Because `read_timeout` does not let Handlers make per-request decisions on each request body’s acceptable deadline or upload rate, most users will prefer to use `read_header_timeout`. It is valid to use them both. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`sequential_start`](https://www.krakend.io/docs/service-settings/http-server-settings/#sequential_start "Click to copy link to sequential_start") boolean A sequential start registers all async agents in order, allowing you to have the starting logs in sequential order. A non-sequential start is much faster, but logs are harder to follow. Defaults to `false` ##### [`use_h2c`](https://www.krakend.io/docs/service-settings/http-server-settings/#use_h2c "Click to copy link to use_h2c") boolean Enable the support for HTTP/2 with no TLS. This option is only advised when you have a load balancer in front of KrakenD doing SSL termination, and you have no option to enable SSL communication between the balancer and KrakenD (no internal certificates available either). Otherwise, enabling this flag is less secure and less performant. Defaults to `false` ##### [`write_timeout`](https://www.krakend.io/docs/service-settings/http-server-settings/#write_timeout "Click to copy link to write_timeout") string Maximum duration before timing out writes of the response. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` Schema: [https://www.krakend.io/schema/v2.13/\_root.json](https://www.krakend.io/schema/v2.13/_root.json "JSON schema definition") ## Override settings using environment vars When you declare in the configuration file any of the HTTP server settings declared above, you can [override its value through environment variables](/docs/configuration/environment-vars/) when starting the server. All the environment variables have the same name are the same settings above in uppercase and with the `KRAKEND_` prefix. For instance, looking at the list of settings above, you could override: - `KRAKEND_PORT` - `KRAKEND_READ_TIMEOUT` - `KRAKEND_READ_HEADER_TIMEOUT` - `KRAKEND_WRITE_TIMEOUT` - `KRAKEND_IDLE_TIMEOUT` - `KRAKEND_USE_H2C` - etc… You can start KrakenD with the desired variables to override what you have in the configuration: Term  ``` $KRAKEND_PORT=8000 KRAKEND_READ_TIMEOUT="1s" krakend run -c krakend.json ``` --- # [Endpoint Configuration](https://www.krakend.io/docs/endpoints/) **Endpoints** are the most important core concept in KrakenD. An oversimplification is that they define the **available routes and protocols the gateway supports**. For instance, you create a `/foo` route and this connects to a `foo.example.com` to fetch the data. If you are familiar with the *API or AI Gateway offering*, as you continue reading, you will quickly realize that **KrakenD is quite different from other products**. Instead of acting as a mere proxy and coupling one sad route to one sad underlying service of the same type, in KrakenD you define **the API contract** you want to expose, and then you create the composite of services that fulfill the contract. Said otherwise, create whatever API definition you want, and plug it into your existing stack. It does not matter the protocols or encodings you have on each side, as KrakenD takes care of transforming the data for you. For example, say you declare a route that offers REST API content on \`/foo, but you want to aggregate the results of querying a gRPC server, a regular REST API, a Lambda service, and an LLM Model altogether. Or maybe you want to offer all the latter under a gRPC server. Or maybe you have a twenty-year-old SOAP service and want to create an MPC Server instantly so your AI client can talk “naturally” to it. All that and more is available as an endpoint. Because KrakenD treats users’ interactions with the gateway and the gateway’s interactions with services separate, endpoints are a piece of magic that can complete all kinds of use cases. ## The endpoint object Deep dive into the configuration If you are still getting familiar with KrakenD’s configuration structure, take a moment to read [Understanding the configuration file](/docs/configuration/structure/). This section explains what an `endpoint` looks like, but take a moment to understand the different pieces. We refer to all routes as families, regardless of their transport nature (HTTP, WebSockets, SOAP, etc.), as `endpoints`. Each of these endpoints can retrieve data from one or more different upstream services, which we collectively call the `backend` array. The `endpoints` array in the configuration contains the **API definition you are publishing**, and is a collection of **endpoint objects** that granularly define how each route behaves. Because everything is independent and highly configurable, the gateway can feed from a vast ecosystem and can add new functionality to your stack. To create an endpoint, you only need to add an **endpoint object** under the `endpoints` collection of the configuration. An endpoint object should contain at least the `endpoint` name and a `backend` section (to which it connects). The defaults are taken if no further information is declared (e.g., `method` will be a `GET`, and `output_encoding` as `json`). A simple`endpoints` section for a REST endpoint might look like this: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "endpoints": [ { "endpoint": "/v1/users/{user}", "method": "GET", "backend": [ { "url_pattern": "/users/summary/{user}", "method": "GET", "host": [ "https://api.example.com" ] } ] } ] } ``` The previous example exposes a REST `GET /v1/users/{user}` endpoint to the clients and takes the data from your existing backend `GET https://api.example.com/users/summary/{user}`. This is the most simple example of an endpoint definition, from here you can add manipulation options, transform the request or the response, and a lot more stuff. See the options below. ### Endpoint object configuration When declaring `endpoints` objects in the configuration, the following fields are available for each one of them: ##### Fields of Endpoint Object \* required fields ##### [`backend`](https://www.krakend.io/docs/endpoints/#backend "Click to copy link to backend") * array of objects An array with all the backend services you want to use in this endpoint. See [the backend object](https://www.krakend.io/docs/backends/) documentation to know all the available options, they are omitted here for brevity. ##### [`cache_ttl`](https://www.krakend.io/docs/endpoints/#cache_ttl "Click to copy link to cache_ttl") Sets or overrides the cache headers to inform how long the client or CDN can cache the request to this endpoint. Setting this value to a zero-value will use the `cache_ttl` in the root of the configuration if any. This attribute **does not cache** content but tells others how to do it. For caching, see [Caching Backend Responses](https://www.krakend.io/docs/backends/caching/). Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). ##### [`concurrent_calls`](https://www.krakend.io/docs/endpoints/#concurrent_calls "Click to copy link to concurrent_calls") integer The concurrent requests are an excellent technique to improve the response times and decrease error rates by requesting in parallel the same information multiple times. Yes, you make the same request to several backends instead of asking to just one. When the first backend returns the information, the remaining requests are canceled. Defaults to `1` ##### [`endpoint`](https://www.krakend.io/docs/endpoints/#endpoint "Click to copy link to endpoint") * string The path of the URL you want to expose. The path is **case-sensitive** and should start with a slash `/`. You can use `{placeholders}` to allow dynamic variables. For example: `/foo/{var}/baz`. You can also add an ending `/*` in the path to enable [wildcards](https://www.krakend.io/docs/enterprise/endpoints/wildcard/). **Enterprise only.** The router will try to automatically redirect calls to endpoints with an incorrect case or incorrect trailing slash to its correct version offering a `301`. There are no guarantees that it will succeed and the request might even fail completely while trying (and log an ugly error with a trace). The **safest option** is to [disable automatic redirections](https://www.krakend.io/docs/service-settings/router-options/) by setting to true the flags `disable_redirect_fixed_path` and `disable_redirect_trailing_slash` in the `router` options. **Limitations**: URLs do not support colons `:` in their definition. All `{vars}` are meant to be isolated in the path and not to be used to build words, like in `/file.{ext}` [See `disable_rest` for that usage](https://www.krakend.io/docs/service-settings/http-server-settings/#disable_rest). Examples: `"/new-endpoint"` , `"/foo/{var}"` , `"/foo/{var1}/{var2}"` ##### [`extra_config`](https://www.krakend.io/docs/endpoints/#extra_config "Click to copy link to extra_config") object Besides the functionality ofered by the endpoint, you can load more functionalty into this endpoint by adding new entries under this extra configuration object. You will find across all documentation a lot of functionality that is designed to be placed here. ##### [`input_headers`](https://www.krakend.io/docs/endpoints/#input_headers "Click to copy link to input_headers") array of strings Defines the list of all headers allowed to reach the backend when passed. By default, KrakenD won’t pass any header from the client to the backend. This list is **case-insensitive**. You can declare headers in lowercase, uppercase, or mixed. An entry `["Cookie"]` forwards all cookies, and a single star element `["*"]` as value forwards everything to the backend (**it’s safer to avoid this option**), including cookies. See [headers forwarding](https://www.krakend.io/docs/endpoints/parameter-forwarding/#headers-forwarding) Defaults to `[]` ##### [`input_query_strings`](https://www.krakend.io/docs/endpoints/#input_query_strings "Click to copy link to input_query_strings") array of strings Defines the exact list of quey strings parameters that are allowed to reach the backend. This list is **case-sensitive**. By default, KrakenD won’t pass any query string to the backend. A single star element `["*"]` as value forwards everything to the backend (**it’s safer to avoid this option**) Defaults to `[]` ##### [`method`](https://www.krakend.io/docs/endpoints/#method "Click to copy link to method") The method supported by this endpoint. Create multiple endpoint entries if you need different methods. Possible values are: `"GET"` , `"POST"` , `"PUT"` , `"PATCH"` , `"DELETE"` Defaults to `"GET"` ##### [`output_encoding`](https://www.krakend.io/docs/endpoints/#output_encoding "Click to copy link to output_encoding") The gateway can work with several content types, even allowing your clients to choose how to consume the content. See the [supported encodings](https://www.krakend.io/docs/endpoints/content-types/) Possible values are: `"json"` , `"json-collection"` , `"yaml"` , `"fast-json"` , `"xml"` , `"negotiate"` , `"string"` , `"no-op"` Defaults to `"json"` ##### [`timeout`](https://www.krakend.io/docs/endpoints/#timeout "Click to copy link to timeout") string The duration you write in the timeout represents the **whole duration of the pipe**, so it counts the time all your backends take to respond and the processing of all the components involved in the endpoint (the request, fetching data, manipulation, etc.). Usually specified in seconds (`s`) or milliseconds (`ms`. e.g.: `2000ms` or `2s`). If you don’t set any timeout, the timeout is taken from the entry in the service level, or to the system’s default Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Examples: `"2s"` , `"1500ms"` Defaults to `"2s"` Schema: [https://www.krakend.io/schema/v2.13/endpoint.json](https://www.krakend.io/schema/v2.13/endpoint.json "JSON schema definition") ### Endpoints with multiple nesting levels You might have envisioned KrakenD as a proxy and expected its `endpoint` declaration to **work as a prefix** and listen 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 an `"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 can declare all possible endpoints: `/user/{id}`, `/user/{id}/{level2}`, `/user/{id}/{level2}/{level3}`, etc. 2. You use a [Wildcard](/docs/enterprise/endpoints/wildcard/) ### 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 must 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: ```json { "endpoints": [ { "endpoint": "/v1/users/{user}", "method": "GET", "backend": [ { "url_pattern": "/users/summary/{user}", "method": "GET", "host": [ "https://api.example.com" ] } ] }, { "endpoint": "/v1/users/{user}", "method": "POST", "backend": [ { "url_pattern": "/users/summary/{user}", "method": "POST", "host": [ "https://api.example.com" ] } ] } ] } ``` Notice that the `method` is declared both in the endpoint and in the backend (as they could be different). ### Endpoint variables As you can see in the examples above, endpoints can define variables in their endpoint definition. To do so, encapsulate the variable name with curly braces, like `{var}`. ```json { "endpoint": "/user/{id}" } ``` The previous endpoint will accept requests like `/user/123` or `/user/A-B-C`. But **it won’t take** a request like `/user/1/2`, as there is an extra slash than the definition, and KrakenD considers this to be a different endpoint. ### Router rules to avoid collisions When you declare multiple endpoints that **share common prefixes**, make sure that you do not declare the same route with different variable names. For instance, you cannot have the following two endpoints coexisting: - endpoint: `/user/{userid}` - endpoint: `/user/{iduser}/some` This will cause a `panic` on startup (that you can catch earlier if you run a `krakend check -t -c krakend.json` ) But you can have the same routes declared as: - endpoint: `/user/{userid}` - endpoint: `/user/{userid}/some` And this will work. Similarly you can’t do this: - endpoint: `/v1/{domain}/user/{userid}` - endpoint: `/v1/{domain}/user/{iduser}/some` But you can declare the same route as: - endpoint: `/v1/{domain}/user/{userid}` - endpoint: `/v1/{domain}/user/{userid}/some` Summarizing, on colliding routes, make sure to use the same variable names. ### Disable RESTful checking By default KrakenD only works with **RESTful URL patterns** in its endpoint definition. Enable the option [`disable_rest`](/docs/service-settings/http-server-settings/#disable_rest) in the root of your configuration if need unrestful endpoints, e.g.: `/file.{extension}` ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "disable_rest": true, "endpoints": [ { "endpoint": "/foo/{var}/file.{extension}", "backend": [ { "host": [ "http://example.com" ], "url_pattern": "/{var}.{extension}" } ] } ] } ``` ## Automatic protocol and encoding translation The endpoints return HTTP content to the end-user in any of the [supported encodings](/docs/endpoints/content-types/), regardless of the type of backend you are connecting to. If, for instance, one of the backends you are connecting to uses AMQP, Kafka, gRPC, or any other supported services, KrakenD will perform automatically for you both the protocol and the encoding translation. ## Additional endpoint functionality The `extra_config` entry of any endpoint can include additional components that provide further functionality. The following is the list of all components accepted under `extra_config`, see the linked documentation of each for more information: ##### Available components of an endpoint \* required fields ##### [`ai/mcp`](https://www.krakend.io/docs/endpoints/#ai%2fmcp "Click to copy link to ai/mcp") **Enterprise only.** Declares the endpoint as an [MCP server entrypoint](https://www.krakend.io/docs/enterprise/ai-gateway/mcp-server/) ##### [`auth/api-keys`](https://www.krakend.io/docs/endpoints/#auth%2fapi-keys "Click to copy link to auth/api-keys") object **Enterprise only.** Validates that users of this endpoint pass a valid API-key containing one of the declared roles. ##### [`auth/basic`](https://www.krakend.io/docs/endpoints/#auth%2fbasic "Click to copy link to auth/basic") object **Enterprise only.** Validates the access to the endpoint using [Basic Authentication](https://www.krakend.io/docs/enterprise/authentication/basic-authentication/). ##### [`auth/signer`](https://www.krakend.io/docs/endpoints/#auth%2fsigner "Click to copy link to auth/signer") object A component that [cryptographically signs tokens](https://www.krakend.io/docs/authorization/jwt-signing/) generated by your legacy login system (without JWT signing) to be later validated by the `auth/validator` component. ##### [`auth/validator`](https://www.krakend.io/docs/endpoints/#auth%2fvalidator "Click to copy link to auth/validator") object Protect endpoints from public usage by [validating JWT tokens](https://www.krakend.io/docs/authorization/jwt-validation/) generated by any industry-standard OpenID Connect (OIDC) integration. ##### [`documentation/openapi`](https://www.krakend.io/docs/endpoints/#documentation%2fopenapi "Click to copy link to documentation/openapi") object **Enterprise only.** Documents the endpoint so you can [automatically generate OpenAPI documentation](https://www.krakend.io/docs/enterprise/developer/openapi/) through `krakend openapi export` command. ##### [`documentation/postman`](https://www.krakend.io/docs/endpoints/#documentation%2fpostman "Click to copy link to documentation/postman") object **Enterprise only.** Documents the endpoint so you can [generate postman documentation automatically](https://www.krakend.io/docs/enterprise/developer/postman/) through the `krakend postman export` command. ##### [`governance/quota`](https://www.krakend.io/docs/endpoints/#governance%2fquota "Click to copy link to governance/quota") object **Enterprise only.** [Attach a quota](https://www.krakend.io/docs/enterprise/governance/quota/) to the endpoint, backend, or service. The quota component allows you to limit the number of requests a user can do in a given time window. ##### [`modifier/jmespath`](https://www.krakend.io/docs/endpoints/#modifier%2fjmespath "Click to copy link to modifier/jmespath") object **Enterprise only.** The [JMESPath](https://www.krakend.io/docs/enterprise/endpoints/jmespath/) is a query language that allows you to select, slice, filter, map, project, flatten, sort, and all sorts of operations on data. ##### [`modifier/lua-endpoint`](https://www.krakend.io/docs/endpoints/#modifier%2flua-endpoint "Click to copy link to modifier/lua-endpoint") object [Scripting with Lua](https://www.krakend.io/docs/endpoints/lua/) is an additional choice to extend your business logic, and is compatible with the rest of options such as CEL, Martian, or other Go plugins and middlewares. ##### [`modifier/lua-proxy`](https://www.krakend.io/docs/endpoints/#modifier%2flua-proxy "Click to copy link to modifier/lua-proxy") object [Scripting with Lua](https://www.krakend.io/docs/endpoints/lua/) is an additional choice to extend your business logic, and is compatible with the rest of options such as CEL, Martian, or other Go plugins and middlewares. ##### [`modifier/request-body-extractor`](https://www.krakend.io/docs/endpoints/#modifier%2frequest-body-extractor "Click to copy link to modifier/request-body-extractor") object **Enterprise only.** [Extracts fields from the incoming request body](https://www.krakend.io/docs/enterprise/endpoints/request-body-extractor/) and promotes them to request headers or query strings. Runs in the proxy pipeline. ##### [`modifier/request-body-extractor/early`](https://www.krakend.io/docs/endpoints/#modifier%2frequest-body-extractor%2fearly "Click to copy link to modifier/request-body-extractor/early") object **Enterprise only.** [Extracts fields from the incoming request body](https://www.krakend.io/docs/enterprise/endpoints/request-body-extractor/) and promotes them to request headers or query strings. Runs at the HTTP handler level before authentication and all other handlers. ##### [`modifier/request-body-generator`](https://www.krakend.io/docs/endpoints/#modifier%2frequest-body-generator "Click to copy link to modifier/request-body-generator") object **Enterprise only.** Freely [transform the request body/payload](https://www.krakend.io/backends/body-generator/) you will send to your services using a templating system. ##### [`modifier/response-body`](https://www.krakend.io/docs/endpoints/#modifier%2fresponse-body "Click to copy link to modifier/response-body") object An alias of `modifier/response-body-generator` ##### [`modifier/response-body-generator`](https://www.krakend.io/docs/endpoints/#modifier%2fresponse-body-generator "Click to copy link to modifier/response-body-generator") object **Enterprise only.** Freely [transform the response body](https://www.krakend.io/docs/enterprise/backends/response-body-generator/) you get from your backends. ##### [`plugin/middleware`](https://www.krakend.io/docs/endpoints/#plugin%2fmiddleware "Click to copy link to plugin/middleware") object **Enterprise only.** Inject your own [Go middleware plugins](https://www.krakend.io/docs/extending/injecting-plugins/) ##### [`plugin/req-resp-modifier`](https://www.krakend.io/docs/endpoints/#plugin%2freq-resp-modifier "Click to copy link to plugin/req-resp-modifier") object Inject your own [Go request and response modifier plugins](https://www.krakend.io/docs/enterprise/extending/plugin-modifiers/) ##### [`proxy`](https://www.krakend.io/docs/endpoints/#proxy "Click to copy link to proxy") object Fine tune different options for the proxy phase of the API request/response flow. ##### [`qos/ratelimit/router`](https://www.krakend.io/docs/endpoints/#qos%2fratelimit%2frouter "Click to copy link to qos/ratelimit/router") object The router rate limit feature allows you to set a [number of maximum requests per second a KrakenD endpoint will accept](https://www.krakend.io/docs/enterprise/endpoints/rate-limit/). ##### [`qos/ratelimit/router/redis`](https://www.krakend.io/docs/endpoints/#qos%2fratelimit%2frouter%2fredis "Click to copy link to qos/ratelimit/router/redis") object **Enterprise only.** Stateful endpoint rate limit persisted on a [Redis database](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/) ##### [`qos/ratelimit/tiered`](https://www.krakend.io/docs/endpoints/#qos%2fratelimit%2ftiered "Click to copy link to qos/ratelimit/tiered") object **Enterprise only.** The [rate limit based on tiers](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/) allows you to have multiple sets of rate limits that apply differently to users depending on their tier or sometimes called subscription plans. ##### [`security/bot-detector`](https://www.krakend.io/docs/endpoints/#security%2fbot-detector "Click to copy link to security/bot-detector") object The [bot detector](https://www.krakend.io/docs/throttling/botdetector/) module checks incoming connections to the gateway to determine if a bot made them, helping you detect and reject bots carrying out scraping, content theft, and form spam. ##### [`security/cors`](https://www.krakend.io/docs/endpoints/#security%2fcors "Click to copy link to security/cors") object Define [Cross-Origin Resource Sharing (CORS)](https://www.krakend.io/docs/enterprise/service-settings/cors/) configuration to send additional HTTP headers to tell browsers if they can use resources from a different origin. ##### [`security/http`](https://www.krakend.io/docs/endpoints/#security%2fhttp "Click to copy link to security/http") object [Security HTTP headers](https://www.krakend.io/docs/service-settings/security/), including HSTS, HPKP, MIME-Sniffing prevention, Clickjacking protection, and others. ##### [`security/policies`](https://www.krakend.io/docs/endpoints/#security%2fpolicies "Click to copy link to security/policies") object **Enterprise only.** A [policies engine](https://www.krakend.io/docs/enterprise/security-policies/) that allows you to write custom sets of policies that are validated during requests, responses, or token validation. ##### [`telemetry/opentelemetry`](https://www.krakend.io/docs/endpoints/#telemetry%2fopentelemetry "Click to copy link to telemetry/opentelemetry") object Enables metrics and traces using [OpenTelemetry](https://www.krakend.io/docs/telemetry/opentelemetry/). ##### [`validation/cel`](https://www.krakend.io/docs/endpoints/#validation%2fcel "Click to copy link to validation/cel") array The [Common Expression Language (CEL)](https://www.krakend.io/docs/endpoints/common-expression-language-cel/) middleware enables expression evaluation, when an expression returns false, KrakenD does not return the content as the condition has failed. Otherwise, if all expressions returned true, the content is served. ##### [`validation/json-schema`](https://www.krakend.io/docs/endpoints/#validation%2fjson-schema "Click to copy link to validation/json-schema") object Apply [automatic validations using a JSON Schema](https://www.krakend.io/docs/endpoints/json-schema/) definition before the content passes to the backends. The json schema component allows you to define validation rules on the body, type definition, or even validate the fields’ values. ##### [`validation/response-json-schema`](https://www.krakend.io/docs/endpoints/#validation%2fresponse-json-schema "Click to copy link to validation/response-json-schema") object **Enterprise only.** Applies a [JSON schema validation to the response](https://www.krakend.io/docs/enterprise/endpoints/response-schema-validator/) before is sent to the end-user or before it’s merged in the endpoint with the rest of the backends. ##### [`websocket`](https://www.krakend.io/docs/endpoints/#websocket "Click to copy link to websocket") object **Enterprise only.** Enables [websocket communication](https://www.krakend.io/docs/enterprise/websockets/) Schema: [https://www.krakend.io/schema/v2.13/endpoint\_extra\_config.json](https://www.krakend.io/schema/v2.13/endpoint_extra_config.json "JSON schema definition") --- # [Backend Configuration](https://www.krakend.io/docs/backends/) The concept of `backend` refers to the origin servers providing the necessary data to populate your endpoints. A backend can be something like your HTTP-based API, a Lambda function, or a Kafka queue, for example. A `backend` can be any server inside or outside your network if it is reachable by KrakenD. For instance, you can create endpoints fetching data from your internal servers and enrich them by adding third-party data from an external API like Github, Facebook, or other services. You can also return everything aggregated in a single glorified response. A `backend` object is an array of all the services that an [`endpoint`](/docs/endpoints/) connects to. It defines the list of hostnames connected to and the URL to send or receive the data. If a backend has more than one `host`, then the array is the [egress load balancing list](/docs/throttling/load-balancing/#balancing-egress-traffic-to-upstream). When a KrakenD endpoint is hit, the engine requests **all defined backends in parallel** (unless a [sequential proxy](/docs/endpoints/sequential-proxy/) is used) and the content [merged and aggregated](/docs/endpoints/response-manipulation/#aggregation-and-merging) (unless you just proxy using the [`no-op` encoding](/docs/endpoints/no-op/)). The returned content is parsed according to its `encoding` or in some cases its `extra_config` configuration. Returned status codes and headers Because KrakenD is not a reverse proxy, the status code and headers returned depends on factors like the type of encoding and the configuration you have chosen. See [Status Codes](/docs/endpoints/status-codes/) and (Returning the backend headers and errors)\[/docs/backends/detailed-errors/] for more details. ## 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_pattern`set the full URL that KrakenD will use to fetch your upstream services. Most of the backends will require a simple configuration like: ```json { "host": ["http://your-api"], "url_pattern": "/url" } ``` The `url_pattern` accepts `{variables}` from the endpoint definition, and on Enterprise you can also [inject headers](/docs/enterprise/endpoints/dynamic-routing/) with patterns like `/{input_headers.X-Tenant}/foo` All the options relative to the **backend definition** are: ##### Fields of Backend Object \* required fields ##### [`allow`](https://www.krakend.io/docs/backends/#allow "Click to copy link to allow") array of strings **Only return the fields in the list**. Only the matching fields (case-sensitive) are returned in the final response. Use a dot `.` separator to define nested attributes, e.g.: `a.b` returns `{"a":{"b": true}}` Example: `["user_id","field1.subfield2"]` ##### [`deny`](https://www.krakend.io/docs/backends/#deny "Click to copy link to deny") array of strings **Don’t return the fields in the list**. All matching fields (case-sensitive) defined in the list, are removed from the response. Use a dot `.` separator to define nested attributes, e.g.: `a.b` removes `{"a":{"b": true}}`. Example: `["token","CVV","password"]` ##### [`disable_host_sanitize`](https://www.krakend.io/docs/backends/#disable_host_sanitize "Click to copy link to disable_host_sanitize") boolean Set it 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 an `invalid host` error. Defaults to `false` ##### [`encoding`](https://www.krakend.io/docs/backends/#encoding "Click to copy link to encoding") Defines your [needed encoding](https://www.krakend.io/docs/backends/supported-encodings/) to set how to parse the response. Defaults to the value of its endpoint’s `encoding`, or to `json` if not defined anywhere else. Notice that `fast-json` and `yaml` are for **Enterprise only.** Possible values are: `"json"` , `"safejson"` , `"fast-json"` , `"xml"` , `"rss"` , `"string"` , `"no-op"` , `"yaml"` Defaults to `"json"` ##### [`extra_config`](https://www.krakend.io/docs/backends/#extra_config "Click to copy link to extra_config") object 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. ##### [`group`](https://www.krakend.io/docs/backends/#group "Click to copy link to group") string Instead of placing all the response attributes in the root of the response, create a new key and encapsulate the response inside. Defaults to `"backend1"` ##### [`host`](https://www.krakend.io/docs/backends/#host "Click to copy link to host") array of strings An array with all the available hosts to [load balance](https://www.krakend.io/docs/throttling/load-balancing/#balancing-egress-traffic-to-upstream) 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 entry in the array with the service name/balancer address. Defaults to the `host` declaration at the configuration’s root level, and the service fails starting when there is none. ##### [`input_headers`](https://www.krakend.io/docs/backends/#input_headers "Click to copy link to input_headers") array of strings A second level of header filtering that defines the list of all headers allowed to reach this backend when different than the endpoint. By default, all headers in the endpoint `input_headers` reach the backend, unless otherwise specified here. An empty list `[]` is considered a zero-value and allows all headers to pass. Use `[""]` to explicitly remove all headers. See [headers forwarding](https://www.krakend.io/docs/endpoints/parameter-forwarding/#headers-forwarding) Examples: `[""]` , `["User-Agent","Accept"]` Defaults to `[]` ##### [`input_query_strings`](https://www.krakend.io/docs/backends/#input_query_strings "Click to copy link to input_query_strings") array of strings A second level of query string filtering that defines the list of all query strings allowed to reach this backend when different than the endpoint. By default, all query strings in the endpoint `input_query_strings` reach the backend, unless otherwise specified here. An empty list `[]` is considered a zero-value and allows all headers to pass. Use `[""]` to explicitly remove all query strings. See [query strings forwarding](https://www.krakend.io/docs/endpoints/parameter-forwarding/#query-string-forwarding) Examples: `[""]` , `["page","limit"]` Defaults to `[]` ##### [`is_collection`](https://www.krakend.io/docs/backends/#is_collection "Click to copy link to is_collection") boolean Set to true when your API does not return an object {} but a collection \[] Defaults to `true` ##### [`mapping`](https://www.krakend.io/docs/backends/#mapping "Click to copy link to mapping") object 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. Example: `{"from":"to"}` ##### [`method`](https://www.krakend.io/docs/backends/#method "Click to copy link to method") The method sent to this backend in **uppercase**. The method does not need to match the endpoint’s method. When the value is omitted, it uses the same endpoint’s method. Some special methods will require you to use no-op encoding (like HEAD or OPTIONS) when these return an empty body. Possible values are: `"GET"` , `"POST"` , `"PUT"` , `"PATCH"` , `"DELETE"` , `"OPTIONS"` , `"HEAD"` , `"CONNECT"` , `"TRACE"` Defaults to `"GET"` ##### [`sd`](https://www.krakend.io/docs/backends/#sd "Click to copy link to sd") The [Service Discovery](https://www.krakend.io/docs/backends/service-discovery/) system to resolve your backend services. Defaults to `static` (no external Service Discovery). Use `dns` to use DNS SRV records. Use `dns-shared` to share the DNS resolution between backends using the same hosts. Possible values are: `"static"` , `"dns"` , `"dns-shared"` Defaults to `"static"` ##### [`sd_scheme`](https://www.krakend.io/docs/backends/#sd_scheme "Click to copy link to sd_scheme") string The [Service Discovery](https://www.krakend.io/docs/backends/service-discovery/) scheme to connect to your backend services. Examples: `"http"` , `"https"` Defaults to `"http"` ##### [`target`](https://www.krakend.io/docs/backends/#target "Click to copy link to target") string Removes the matching object from the reponse and returns only its contents. Examples: `"data"` , `"content"` , `"response"` ##### [`url_pattern`](https://www.krakend.io/docs/backends/#url_pattern "Click to copy link to url_pattern") * string 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 a valid `url_pattern`, but they are exceptions. The URL must be RESTful, if it is not (e.g.: `/url.{some_variable}.json`), then see how to [disable RESTful checking](/#disable-restful-checking). Examples: `"/users"` , `"/user/{id_user}"` Schema: [https://www.krakend.io/schema/v2.13/backend.json](https://www.krakend.io/schema/v2.13/backend.json "JSON schema definition") Other configuration options such as the ones for [data manipulation](/docs/backends/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](/docs/backends/service-discovery/)) to pick one of the three hosts. ```json { "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" ] } ] } ] } ``` ## Connecting to HTTPS backends with self-signed certificates When using **self-signed certificates** in your backends, you must add the certificates to the local CA, or at least add them while developing the `allow_insecure_connections` setting to `true`. Example: ```json { "version": 3, "client_tls": { "allow_insecure_connections": true }, "endpoints": [] } ``` --- # [Parameter Forwarding](https://www.krakend.io/docs/endpoints/parameter-forwarding/) KrakenD is an API Gateway with a [**zero-trust security policy**](/docs/design/zero-trust/). You need to define what is allowed because KrakenD **does not forward** any unexpected [query string](/docs/endpoints/parameter-forwarding/#query-string-forwarding), [headers](/docs/endpoints/parameter-forwarding/#headers-forwarding), or [cookies](/docs/endpoints/parameter-forwarding/#cookies-forwarding). See below for instructions on how to set the forwarding rules. ![Diagram about parameters don’t passing to backend](/images/documentation/diagrams/parameter-forwarding-1.mmd.svg) ## 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: ##### Fields of `"endpoint"` \* required fields ##### [`input_headers`](https://www.krakend.io/docs/endpoints/parameter-forwarding/#input_headers "Click to copy link to input_headers") array of strings Defines the list of all headers allowed to reach the backend when passed. By default, KrakenD won’t pass any header from the client to the backend. This list is **case-insensitive**. You can declare headers in lowercase, uppercase, or mixed. An entry `["Cookie"]` forwards all cookies, and a single star element `["*"]` as value forwards everything to the backend (**it’s safer to avoid this option**), including cookies. See [headers forwarding](https://www.krakend.io/docs/endpoints/parameter-forwarding/#headers-forwarding) Defaults to `[]` ##### [`input_query_strings`](https://www.krakend.io/docs/endpoints/parameter-forwarding/#input_query_strings "Click to copy link to input_query_strings") array of strings Defines the exact list of quey strings parameters that are allowed to reach the backend. This list is **case-sensitive**. By default, KrakenD won’t pass any query string to the backend. A single star element `["*"]` as value forwards everything to the backend (**it’s safer to avoid this option**) Defaults to `[]` Schema: [https://www.krakend.io/schema/v2.13/endpoint.json](https://www.krakend.io/schema/v2.13/endpoint.json "JSON schema definition") ### Case-sensitive and case-insensitive parameters - The `input_query_strings` list is **case sensitive**, as per the RFC specification. For instance, a request `?Page=1` and `?page=1` are considered different parameters, and only the latter will pass when setting `"input_query_strings": ["page"]`. If you expect multiple cases, add them all. - The `input_headers` is **case-insensitive**, as per its RFC specification. It allows the passing of user headers in uppercase, lowercase, or mixed. Nevertheless, when the header is forwarded to the backend or used in other components, they receive it normalized in the **canonical format of the MIME header**, so users can mix capitalization and yet receive a consistent format. Canonical Headers While the `input_headers` declaration does not care about how you write the header (upper/lowercase), and KrakenD will access either way, when accessing or checking a header name through any components in KrakenD, you must write its canonical form regardless of what’s being provided by the user. The canonicalization **converts the first letter and any letter following a hyphen to uppercase**; the rest are converted to lowercase. For example, the canonical key for `accept-encoding`, `ACCEPT-ENCODING`, or `ACCept-enCODING` is `Accept-Encoding`. MIME header keys are assumed to be ASCII only. If the header contains a space or invalid header field bytes, it is returned without modifications. If you get used to writing headers in the canonical format, you will save yourself from a lot of trouble. **Example**: Send the query strings `items` and `page` to the backend, as well as `User-Agent` and `Accept` headers: ```json { "version": 3, "endpoints": [ { "endpoint": "/v1/foo", "input_query_strings": [ "items", "page" ], "input_headers": [ "User-Agent", "Accept" ], "backend": [ { "url_pattern": "/catalog", "host": [ "http://some.example.com:9000" ] } ] } ] } ``` Read below for further details and examples. ## Query string forwarding The zero-trust policy implies that, for instance, if a KrakenD endpoint `/foo` receives the request `/foo?items=10&page=2`, all its declared backends are not going to see either `items` or `page`, **unless otherwise configured**. Add the **list** `input_query_strings` in your `endpoint` definition to enable the transition of query strings to your backend. For instance, let’s forward `?items=10&page=2` to the backends now: ```json { "version": 3, "endpoints": [ { "endpoint": "/v1/foo", "input_query_strings": [ "items", "page" ], "backend": [ { "url_pattern": "/catalog", "host": [ "http://some.example.com:9000" ] } ] } ] } ``` The `input_query_strings` list has the following behavior: - **Items in the list** are forwarded to your backend when passed - **Additional query strings not in the list** are removed from the final call - **Writing a single *star* element** (`"input_query_strings":["*"]`) instead of individual strings, forwards **everything** to the backend With this configuration, given a request like `http://krakend:8080/v1/foo?items=10&page=2&evil=here`, the backend receives `items` and `page`, but `evil` is missing. Also, if a request like `http://krakend:8080/v1/foo?items=10` does not include `page`, this parameter is simply missing in the backend request. By definition, query string parameters are always optional, and the user can pass a subset of them, all or none. Suppose you want to enforce that the user provides a query string parameter. In that case, you must validate it with the [Common Expression Language](/docs/endpoints/common-expression-language-cel/) (faster) or with a [Lua script](/docs/endpoints/lua/) (slower). ### Sending all query string parameters While the default policy prevents sending unrecognized query string parameters, setting an asterisk `*` as the parameter name makes the gateway to **forward any query string to the backends**: ```json { "endpoint": "/foo", "input_query_strings":[ "*" ] } ``` **Enabling the wildcard pollutes your backends**, as any query string sent by end-users or malicious attackers gets through the gateway and impacts the backends behind. We recommend letting the gateway know which query strings are in the API contract and specify them in the list, even when it is long, and not use the wildcard. If you decide to go with the wildcard, ensure your backends can handle client abuse attempts. ### Mandatory query string parameters When your backend requires mandatory **query string** parameters and you want to make them **mandatory** in KrakenD, the only way to enforce this with the open source edition (without scripting) is using the `{variable}` placeholders in the endpoints definition. Enterprise users can use [Dynamic Routing](/docs/enterprise/endpoints/dynamic-routing/#routing-based-on-query-strings) and [Security Policies](/docs/enterprise/security-policies/) together. Mandatory means that the endpoint won’t exist unless the parameter is passed. For instance: ```json { "endpoint": "/v3/{channel}/foo", "backend": [ { "host": ["http://example.com"], "url_pattern": "/foo?channel={channel}" } ] } ``` The parameter is compulsory; if a value for `channel` is not provided, the server replies with a `404`. With the configuration above, a request to the KrakenD endpoint such as `http://krakend/v3/iOS/foo?limit=10&evil=here` makes a call to the backend with only the `channel` query string: ``` /foo?channel=iOS ``` Nevertheless, the `input_query_strings` could also be added in this configuration, creating a special case of optional and mandatory parameters! You would pass query strings hardcoded in the `url_pattern` and generated from the user input. In this strange case, the mandatory value is lost if the user passes a single optional query string parameter that is declared in `input_query_strings`. The compulsory value is used if the request contains no known optional parameter. For instance: ```json { "endpoint": "/v3/{channel}/foo", "input_query_strings": [ "page", "limit" ], "backend": [ { "host": [ "http://example.com" ], "url_pattern": "/foo?channel={channel}" } ] } ``` With `http://krakend/v3/iOS/foo?limit=10&evil=here` the backend receives: ``` /foo?limit=10 ``` No mandatory `channel` here! Because the optional parameter `limit` has been declared. On the other hand, `http://krakend/v3/iOS/foo?evil=here` produces: ``` /foo?channel=iOS ``` No optional parameter has been passed, so the mandatory one is used. Read the [`/__debug/` endpoint](/docs/endpoints/debug-endpoint/) to understand how to test query string parameters. ## Headers forwarding KrakenD **does not send client headers to the backend** except for the `Content-Type` unless they are under the `input_headers` list. The headers sent by the client that you want to let pass to the backend must be written explicitly in the `input_headers`. See below how to forward [all client headers](/docs/endpoints/parameter-forwarding/#sending-all-client-headers-to-the-backends) (and why it is a bad idea). **A client request from a browser or a mobile client contains a lot of headers**, including cookies. Typical examples of the variety of headers clients send are `Host`, `Connection`, `Content-Type`, `Accept`, `Cache-Control`, `Cookie`… and a long etcetera. Remember that unless explicitly defined, KrakenD will only allow the `Content-Type`. This security policy will save you from a lot of trouble. ### Default headers sent from KrakenD to Backends KrakenD will act as an independent client connecting to your backends and sending headers to them. Some are customizable, and others aren’t. #### Non-customizable headers You cannot override the values of these headers (unless you have a plugin or a Lua script): - `Host` - `X-Forwarded-For` - `X-Forwarded-Host` - `X-Forwarded-Via` (only when `User-Agent` is in the `input_headers`) The `X-Forwarded`-like headers are controlled by [`forwarded_by_client_ip`](/docs/service-settings/router-options/#forwarded_by_client_ip). All these headers **are always sent, regardless of whether you define them or not** under `input_headers`. You can override their values. #### Customizable headers The following headers are sent to the backends with default values, but if you add them under `input_headers`, you allow the client to send their own values. If the headers are under `input_headers`, but the client does not send them, KrakenD sets its values. The headers are: - `Content-Type`, the one passed in the request (if passed) - `Accept-Encoding`, set by KrakenD to `gzip`, unless you add it to the `input_headers` - `User-Agent` Contains the value `KrakenD Version 2.13.8`, unless you add it to the `input_headers` Needless to say that you can add any other header that is not listed in this page under `input_headers` and KrakenD will forward it to the backend. In addition, there are a few KrakenD components that support **setting attributes in headers**, like for instance [`propagate_claims` in JWT](/docs/authorization/jwt-validation/#propagate_claims). These components will send the additional headers you configure to the backend automatically. ### Overriding headers sent from KrakenD to Backends When you use the `input_headers`, consider that any headers listed above are replaced with the ones you declare. An example of passing the `User-Agent` to the backend: ```json { "version": 3, "endpoints": [ { "endpoint": "/v1/foo", "input_headers": [ "User-Agent" ], "backend": [ { "url_pattern": "/catalog", "host": [ "http://some.example.com:9000" ] } ] } ] } ``` This setting changes the headers received by the backend to: ```yaml Accept-Encoding: gzip Host: localhost:8080 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 X-Forwarded-For: ::1 ``` The `User-Agent` is no longer a KrakenD user agent but a Mozilla one. Read the [`/__debug/` endpoint](/docs/endpoints/debug-endpoint/) to understand how to test headers. ### 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**: ```json { "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). Let the gateway know which headers are in the API contract and specify them in the list. Even when the list is long, try not to use the wildcard. If the decision is to go with the wildcard, make sure your backends can handle client abuse attempts, and do not discard adding a second `input_headers` list in the backend (not all backends in aggregation might need every header). Exception for X-Forwarded-like headers Even though you allow to pass all headers with `"input_headers": ["*"]`, you cannot override the headers `X-Forwarded-For`, `X-Forwarded-Host`, and `X-Forwarded-Via` which are automatically calculated by KrakenD. If you’d like to take those headers into account, use the flag [`forwarded_by_client_ip` under the router section](/docs/service-settings/router-options/#forwarded_by_client_ip) ### Granular header filtering All headers listed in the `input_headers` parameter hit every single backend of the `endpoint`. If you want to add a second level of filtering, you can configure the `input_headers` list in the `backend` section too. Doing this allows you to have backends that receive fewer headers than other backends in the same endpoint. For instance, the following endpoint allows passing two headers to its backends, but the second backend allows a single header to pass: ```json { "version": 3, "host": [ "http://some.example.com:9000" ], "endpoints": [ { "endpoint": "/v1/foo", "input_query_strings": [ "items", "page" ], "input_headers": [ "User-Agent", "Accept" ], "backend": [ { "url_pattern": "/receive-defined-headers", }, { "url_pattern": "/receive-one-header-only", "input_headers": [ "User-Agent" ] } ] } ] } ``` ## Cookies forwarding A cookie is just some content passing through the `Cookie` header. If you want cookies to reach your backend, add the `Cookie` header under `input_headers`, just as you would with any other header. **All your cookies** are sent to all backends inside the endpoint when doing this. Use this option wisely! Example: ```json { "version": 3, "endpoints": [ { "endpoint": "/v1/foo", "input_headers": [ "Cookie" ], "backend": [ { "url_pattern": "/catalog", "host": [ "http://some.example.com:9000" ] } ] } ] } ``` --- # [Proxying directly to the backends with `no-op`](https://www.krakend.io/docs/endpoints/no-op/) KrakenD `no-op` (**no-operation**), is a special type of **encoding** that behaves as a **proxy** by passing the client’s request to the backend and returning the response to the client ***as it is*** (some additional operations are permitted). Essentially without any manipulation or operation. ## Using `no-op` to proxy requests When setting `no-op`, KrakenD does not inspect the request `body` or manipulates it in any way. Instead, when a request to a `no-op` endpoint is received, KrakenD directly forwards it to the backend without doing any operation with it. The *proxy pipe* (this is from KrakenD to backend) is marked to do no-operation, meaning that KrakenD does not aggregate content, filter, manipulate or any of the other functionalities performed during this pipe. It’s also important to notice that only a **single backend** is accepted, as the merge operation happens during the *proxy pipe*. Employing the same principle, when the backend produces the response, it’s passed back to the client *as is*, preserving its form: body, headers, status codes and such. On the other hand, the *router pipe*’s features (from client to KrakenD) remain unaltered, meaning that for instance you can still rate-limit your end-users or require JWT authorization to name a few examples. ## Key concepts 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). - `X-Krakend-Completed` will be false and `X-Krakend` with current version will be added in response headers ## When to use `no-op` Use `no-op` when you need to **couple the client with a specific backend without any KrakenD manipulation**. Examples: - You want to set a `Cookie` to the client directly from the backend. - You need to keep the headers of the backend as is. ## How to use `no-op` To declare endpoints that return the backend response as it is you need to define `"output_encoding": "no-op"`. KrakenD will set the `"encoding": "no-op"` in the `backend` section automatically, ignoring any different value you might have set. When using the no-op encoding remember that the endpoint can only have **one backend** as KrakenD is not going to inspect or manipulate the response (no merging happens). Also, other pipe options like the concurrent requests, or manipulation options are not available: you will find them flagged across the documentation as not compatible with no-op. ## 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: ```json { "endpoint": "/auth/login", "output_encoding": "no-op", "backend": [ { "encoding": "no-op", "host": [ "localhost:8080" ], "url_pattern": "/__debug/login" } ] } ``` --- # [Wildcard Endpoints (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/wildcard/) The **wildcard** functionality allows you to declare endpoints that have **many implicit paths** in their backend. Instead of declaring all possible routes one by one, you can create one or more endpoints that will respond to a path pattern instead. The gateway then forwards all routes starting with the configured path to the backend(s). For instance, you can declare an endpoint `/foo/*` that forwards all traffic starting with `/foo/` to a specific backend. When a user calls `/foo/bar` or `/foo/bar/vaz`, the request is processed by the same endpoint and backends. Since paths are implicit, KrakenD does not know if a request is valid or not beyond checking that the pattern is correct, or checking additional components you might add. Therefore, everything it can do is to “hope for the best” before routing the request, and it is the responsibility of the backend to determine if the path is valid. ![](/images/documentation/krakend-wildcard.png "Wildcard endpoints") The wildcard functionality is a good candidate when you are in a migration scenario and you need to expose a lot of endpoints quickly. However, it would be best to transition to an explicit declaration instead, and you should aim for it if security is a concern. Implicit declaration is less secure Please make explicit declarations (define all endpoints) whenever possible. Using a wildcard opens the door to exposing endpoints you are unaware of, allowing end-users to experiment with the URLs, scan your backend services, or exploit “zombie endpoints” that were originally published and forgotten over time. ## Wildcard configuration To enable wildcards, you only need to write a string ending in `/*` in the’ endpoint’ path. Then, the receiving backend will automatically append the remaining request to its `url_pattern`. For instance, given the following configuration: ```json { "endpoint": "/v1/*", "backend": [{ "host": ["http://legacy-api"], "url_pattern": "/" }] } ``` When requesting to `http://krakend/v1/hello`, you get a request to your backend as`http://legacy-api/hello`. Notice that you don’t need to write any asterisk in the `url_pattern`. The wildcard applies to the path only. If you want to use multiple methods (`GET`, `PUT`, `POST`, etc.), you still need to create an entry per method as in any other KrakenD endpoint. ### Wildcard Limitations Make sure to declare wildcards per the following rules: - You cannot use multiple asterisks (e.g.: `/v1/*/foo/*`). Instead, use `{placeholders}`. E.g., `/v1/{service}/foo/*`. - You cannot route all traffic on the root level (e.g., `"endpoint": "/*"`). Therefore, you must declare **at least one directory level** before placing the wildcard. See the [Catch-All endpoint](/docs/enterprise/endpoints/catch-all/) for such uses. - You cannot declare colliding paths. For instance if you have an endpoint `/v1/foo` you cannot create a wildcard `/v1/*` (although you can do this with the old plugin, see documentation below) - You cannot have an endpoint that starts with a `{variable}` and ends with `*`, like `/{foo}/bar/*`, except when there is one level alone `/{foo}/*`. Examples of **valid paths**: - `/a/b/c/*` - `/a/*` - `/{var}/*` (when alone) Examples of **invalid paths**: - `/abc*` (doesn’t end in `/*`) - `/*` (forwards all traffic, use [Catch-All](/docs/enterprise/endpoints/catch-all/) instead) - `/*/a/*` (multiple uses of `*`) - `/{foo}/bar/*` (starts with variable) - `/{foo}/{bar}/*` (starts with variable) Collisions during runtime Some collisions when using invalid paths, can be avoided when enabling the [router flag `disable_redirect_fixed_path`](/docs/enterprise/service-settings/router-options/#disable_redirect_fixed_path), which is the underlying reason for unhandled collisions. As you can see, combining the wildcard with endpoint variables is possible. For instance, you could have the following paths configured: ```json { "endpoints": [ { "endpoint": "/v1/{service}/*", "backend": [{ "url_pattern": "/v1/{service}/" }] }, { "endpoint": "/{owmamma}/*", "backend": [{ "url_pattern": "/{owmamma}/" }] } ] } ``` ## Upgrading from the old wildcard plugin (before v2.3) If you used Wildcards before EE v2.3, eliminate now all entries referring to plugins. This means: - If you don’t use additional plugins, you can get rid of the `plugin` entry in the root level. - You can delete all `plugin/http-server` objects if they only use the wildcard - You can delete any `wildcard` object in the configuration - You can delete all `plugin/http-client` with an insider `wildcard` entry. - You can delete all `"X-Krakend-Wildcard"` entries in `input_headers`. For instance, if you had this configuration: ```json { "version": 3, "port": 8080, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "extra_config": { "plugin/http-server": { "name": [ "wildcard" ], "wildcard": { "endpoints": { "/__wildcard/foo": [ "/foo" ], "/__wildcard/legacy": [ "/v1" ] } } } }, "endpoints": [ { "endpoint": "/__wildcard/foo", "input_headers": [ "X-Krakend-Wildcard" ], "backend": [ { "host": [ "http://localhost:8080" ], "url_pattern": "/__debug/foo", "extra_config": { "plugin/http-client": { "name": "wildcard" } } } ] }, { "endpoint": "/__wildcard/legacy", "input_headers": [ "X-Krakend-Wildcard" ], "backend": [ { "host": [ "http://localhost:8080" ], "url_pattern": "/__debug/legacy", "extra_config": { "plugin/http-client": { "name": "wildcard" } } } ] } ] } ``` Since EE 2.3, it has become: ```json { "version": 3, "port": 8080, "endpoints": [ { "endpoint": "/foo/*", "backend": [ { "host": [ "http://localhost:8080" ], "url_pattern": "/__debug/" } ] }, { "endpoint": "/v1/*", "backend": [ { "host": [ "http://localhost:8080" ], "url_pattern": "/__debug/" } ] } ] } ``` --- # [Dynamic Routing (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/dynamic-routing/) The **dynamic routing** extends the routing capabilities to add header, tokens, 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 the variables that inject the content provided by the user directly in the `url_pattern` or inside a `host` entry. For instance: ```json { "endpoint": "/foo", "backend":[ { "host": ["http://{input_headers.X-Tenant}.example.com"], "url_pattern": "/{input_headers.X-Route}", "disable_host_sanitize": true } ] } ``` The example above replaces the variable `{input_headers.X-Tenant}` with the value provided under a hypothetical `X-Tenant` header to construct the final DNS entry. It also uses a header `X-Route` to define the URL pattern. Notice that when using dynamic routing in the field `host` **, the flag `disable_host_sanitize` set to `true` is mandatory**. Also notice **the absence of a service discovery property (`sd`)** in the configuration, as the dynamic routing is incompatible with DNS-based service discovery strategies. Security notice Because the dynamic routing might take the user input to build the `host` string, you should always add a [Security Policy](/docs/enterprise/security-policies/) that validates the user input. ## Dynamic routing declaration REST-backends only The Dynamic Routing is only available to **non-persistent backends** that use static resolution (e.g., REST). Any stateful connection such as WebSockets or gRPC does not support this pattern. If the Service Discovery is DNS-based (even in REST) the Dynamic Routing is not supported. There are four different types of input injections you can do in the backend `url_pattern` or the `host` fields that are available with the following variables: - `{input_headers}`: Header routing - `{input_query_strings}`: Query string routing - `{JWT}`: JWT-claim routing - `{param}`: Parameter-based 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 (except for parameters). Missing parameters? When the user does not pass a required input header or query string, the gateway raises an error `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 policies](/docs/enterprise/security-policies/) ## Routing based on headers The most typical scenario for dynamic routing is when you want to reach a service or a path based on an input header. To access header values, use the above-defined variable `{input_headers.xxx}`, where `xxx` is the header name. As the **dynamic routing component executes before the endpoint evaluation**, you don’t need to add the headers you declare under `input_headers` unless you want these headers propagated to the backend. For instance, let’s say that you want to pass an `X-Tenant` header and use it as part of the host you will contact: ```json { "endpoints": [ { "endpoint": "/user/{id}", "backend": [{ "host": ["http://{input_headers.x-tenant}.example.com"], "url_pattern": "/user/{id}", "disable_host_sanitize": true }] } ] } ``` With a configuration like the above, the user would pass the customer information in the header, for instance: Using host routing based on headers  ``` $curl -H'X-Tenant: abcdef' http://krakend/user/1234 ``` The upstream services a request to `http://abcdef.example.com/user/1234`. Notice the **mandatory requirement of `disable_host_sanitize`** . The protocol should also be present in the declaration, not in the header. Similarly, you can do path routing based on a header like this: ```json { "endpoints": [ { "endpoint": "/user/{id}", "backend": [{ "url_pattern": "/{input_headers.customer}/user/{id}" }] } ] } ``` The upstream service receives, in this case, `/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](/docs/enterprise/security-policies/) component. For instance, you could make sure that the header adheres to a specific format expressed by a regex: ```json { "endpoints": [ { "endpoint": "/user/{id}", "backend": [ { "url_pattern": "/{input_headers.customer}/user/{id_user}" } ], "extra_config": { "security/policies": { "req": { "policies": [ "getHeader('Customer').matches('^/[a-zA-Z]{4}$')" ], "error": { "body": "Mailformed customer request", "status": 400 } } } } } ] } ``` An important observation on the example above is that Security Policies need to access header names in [canonical format](/docs/enterprise/endpoints/parameter-forwarding/#case-sensitive-and-case-insensitive-parameters). ## Routing based on query strings As with headers, you can also 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: ```json { "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. ### 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. ## Routing based on a JWT claim You can inject claims to the backend’s final URL if you are using the [JWT validator](/docs/authorization/jwt-validation/) 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: ```json { "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. ## Routing based on parameters When the endpoint definition adds a `{parameter}` you can also use this value to populate the `host` or `url_pattern` as well. ```json { "endpoints": [ { "endpoint": "/foo/{server}", "backend": [ { "host": [ "http://{server}:8080" ], "url_pattern": "/__debug/dynamic" } ] } ] } ``` As you can see, the request from the end-user would choose where to connect to. This has security implications that you should address with a Security Policy that validates the input received to see if it’s within the expected values. See below. ## Validating the user input with Security Policies Given the nature of this feature, you should always add a [Security Policy](/docs/enterprise/security-policies/) accompaning the usage of dynamic routing. For instance, if you allow in the configuration forming the host based on user input, validate the values to allow this: ```json { "endpoints": [ { "endpoint": "/foo/{server}", "extra_config": { "security/policies": { "req": { "policies": [ "req_params.Server in ['a.example.com', 'b.example.com']" ] } } }, "backend": [ { "host": [ "http://{server}:8080" ], "url_pattern": "/__debug/dynamic" } ] } ] } ``` The example above would abort user requests that do not use `/foo/a.example.com` or `/foo/b.example.com`. Obviously, you can use any other type of expression that adjusts to your requirements. --- # [Catchall (Fallback backend) (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/catch-all/) The catchall endpoint is a particular type of `endpoint` that, when added, receives all the traffic from routes and methods that do not resolve to any of the other existing `endpoints` in the configuration. Its primary use case is for **initial stages of a migration**, when you want to test a small subset of endpoints on KrakenD and have **all the rest of the traffic** forwarded to a common backend. The [wildcard endpoints](/docs/enterprise/endpoints/wildcard/) are similar in functionality, but they only listen to a starting path, not everything, and they are a better option. Catchall means forwarding the good and the bad Although this endpoint might be super-useful, keep in mind that, generally speaking, you should **avoid having a catchall route**. All types of unknown requests will hit your fallback backend, opening the door for malicious users to test and scan it. From a security perspective, you are safer without this because you cut any traffic that you don’t explicitly declare. ## Catchall configuration To enable the catchall endpoint, all it takes is to create a [regular endpoint](/docs/enterprise/endpoints/) with the reserved `endpoint` name `/__catchall`. As with any other endpoint, you must define the `url_pattern` and the `host` in its `backend` section, but in this case, it is acting as the **fallback backend**. Like this: ```json { "endpoint": "/__catchall", "output_encoding": "no-op", "input_headers": ["*"], "input_query_strings": ["*"], "backend": [{ "encoding": "no-op", "url_pattern": "/", "host":["http://mybackend:1234"] }] } ``` The catchall endpoint acts as a reverse proxy and internally works using a [no-op endpoint](/docs/enterprise/endpoints/no-op/). The route name `/__catchall` is not registered during startup, it is our convention to mark the fallback route. But effectively it makes little difference for the end-user as all unregistered routes (including `/__callback`) will fall here anyway. In connection with working as a no-op endpoint, a few remarks worth noticing: - You cannot change this endpoint’s `output_encoding` or `encoding`. If you do, it will be automatically reverted during startup to `no-op` and an error log will be shown in the console. - All query strings and headers sent by the consumer of your API are forwarded to the fallback backend. Regardless of your `input_query_strings` and `input_headers` settings, it will revert your settings to behave with a wildcard `["*"]`, passing everything to the backend. - As the response is a no-op, the client will get all the headers, encodings, content and status codes of the fallback backend as originally received. - You can still use components loaded under the `extra_config` of your `backend` that are compatible with `no-op`, or endpoint functionality that acts at the proxy level. ## Catchall limitations All functionality that is declared at the `extra_config` of the service level (config’s root) and all functionality inside a `backend` works normally. Components loaded in the `endpoint`’s `extra_config` or related with endpoint’s HTTP functionality are **not loaded**, including [Dynamic Routing](/docs/enterprise/endpoints/dynamic-routing/) evaluation. The following list are the **exceptions that still work**: - Security Policies (except for HTTP) - JMESpath - Body generators - Observability (New Relic, OpenCensus, OpenTelemetry) - Shadow proxy - JSON schema validation - CEL - LUA (except for HTTP) - Request and Response modifiers - Flatmap - Static responses Any other HTTP-related components loaded in the `extra_config` are ignored. For instance, you cannot apply JWT validation to all connections intercepted by a catchall, so you should move this logic to a [wildcard](/docs/enterprise/endpoints/wildcard/) instead. Catchall and Dynamic Routing Catchall functionality doesn’t execute endpoint’s HTTP-related components, meaning that **declaring Dynamic Routing patterns within a catchall endpoint won’t work** and might produce errors. --- # [Sequential Proxying](https://www.krakend.io/docs/endpoints/sequential-proxy/) The best experience consumers can have with KrakenD API is by letting the system fetch all the data from the different backends simultaneously. However, sometimes you need to **delay a backend call** until you have called a previous service. Although this is not ideal, the sequential proxy allows you to **chain backend requests**. The Sequential Proxy enables chaining multiple backend requests where **the output of one call is used as input for the next**, whether just injecting previous values of the response in the URL or as the body for the next call. This capability is especially powerful for orchestrating **dependent operations** or implementing simple workflows across services. For complex workflows, see the [Workflows documentation](/docs/enterprise/endpoints/workflows/) (Enterprise ) Although achievable, we do not recommend adopting sequential patterns to perform token exchange or pre-authentication steps and use the dedicated components for such usages. ## Do you really need a sequential proxy? Chained calls are considered an anti-pattern Using sequential calls is considered an anti-pattern because when you make a network service dependent on the other, you are **increasing the latency, decreasing the performance, and augmenting the error rate**. In non-sequential [aggregation](/docs/endpoints/response-manipulation/#aggregation-and-merging), parallel requests execute simultaneously, whereas, in sequential aggregation, requests are executed one at a time, with each one waiting for the previous request to finish (or timeout) before moving on to the next. If a backend in a sequence fails, the process aborts and the next backend is never reached, so there are many more chances that your users will be left without data. From an error rate perspective, the nature of sequential proxy performs more deficient: Suppose you have three backends with an error rate of 10% each, then the probability of success separately in each is 90%. However, when executing the three of them sequentially, the success rate drops to 73% (because `0.9 * 0.9 * 0.9 = 0.729`). In an aggregation scenario, the probability of having at least one working call is the opposite of the likelihood of having all calls result in errors. So, the chance of all three calls resulting in errors is 0.1% (because `0.1 * 0.1 * 0.1 = 0.001`). The contrast between a 99.9% chance of some data availability and a 73% probability is quite substantial. Isn’t it? That being said, from an architectural point of view, the sequential proxy should be your last resort. In addition, you can add to a sequence **multiple unsafe methods** (methods different than `GET`). When you chain several write requests in multiple nodes, you execute a **distributed transaction** in a flowery disguise, as in a database. But a gateway is not a database, and you don’t have any rollback mechanism if one of your write methods fails, so you can only *hope for the best*. ## Sequential proxy configuration To enable the sequential proxy, you need to add in the endpoint definition the following configuration: ```json { "endpoint": "/hotels/{id}", "extra_config": { "proxy": { "sequential": true } } } ``` After doing this, the `backend` list is executed one by one, and the next call has the opportunity to access data returned by the previous call in the `url_pattern` and not anywhere else. **The body of the last request is not sent to the next**, but you can save it as a parameter **and inject it later** using the `sequential_propagated_params`. When the sequential proxy is enabled, the `url_pattern` of every backend can use a new variable that references the **resp**onse of a previous API call. The sequential proxy variables have the following construction: ```js {resp0_XXXX} ``` Where `0` is the index of the specific `backend` you want to access (`0` for the first backend, `1` for the second, and so on), and where `XXXX` is the attribute name you want to inject from the response of the previous call. You can also access **nested objects** of the response using the dot notation. For example, given a response `{"user": { "hash": "abcdef }}`, the variable`{resp0_user.hash}` will contain the value `abcdef`. **You cannot access nested objects inside arrays or collections**: fields must be objects (although you could manipulate these arrays using flatmap or JMESpath to convert them to objects). You can reuse the `{resp0_XXXX}` variable in the `url_pattern` in any form you like, as part of the path or as a query string. For instance, the following examples would work: ```json { "url_pattern": "/user/{resp0_user.hash}" } ``` And also: ```json { "url_pattern": "/user?hash={resp0_user.hash}" } ``` All values are represented as string When using the `{resp0}` and similar variables, the value you get is always a string. This means that if, for instance, KrakenD takes data from an object that contains a `null` value (not an empty value), the variable will be a string containing ``. For instance, in the examples above, if the response contained `{"user": { "hash": null } }`, the final value of the `url_pattern` would be `/user/` and `/user?hash=` respectively. ### Example of sequential proxy passing values on the `url_pattern` It’s easier to understand with the example of the graph: ![Chained call](/images/documentation/krakend-sequential-call.png) The user calls the gateway with an URL like `/hotel-destinations/{id}`, which needs to fetch the hotel information and all its associated destinations. Let’s say the ID they request is `25`. The gateway calls a backend `/hotels/25` that returns data for the requested hotel, including a `destination_id` field that is a relationship identifier. The output for `GET /hotels/25` is like the following: ```json { "hotel_id": 25, "name": "Hotel California", "destination_id": 1034 } ``` KrakenD waits for the backend response and injects the value of `destination_id` in the URL of the next backend call. In this case, the next call is `GET /destinations/1034`, and the response is: ```json { "destination_id": 1034, "destinations": [ "LAX", "SFO", "OAK" ] } ``` Now KrakenD has both responses from the backends and can merge the data, returning the following aggregated object to the user: ```json { "hotel_id": 25, "name": "Hotel California", "destination_id": 1034, "destinations": [ "LAX", "SFO", "OAK" ] } ``` The configuration needed for this example is: ```json { "endpoint": "/hotel-destinations/{id}", "backend": [ { "@comment": "This is the index position 0", "host": [ "https://hotels.api" ], "url_pattern": "/hotels/{id}" }, { "@comment": "This is the index position 1", "host": [ "https://destinations.api" ], "@comment2": "resp0_ is the response of index position 0", "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). ### Sequential proxy and special encodings The sequential proxy parses the response of the previous backend, trying to understand the structure of the returned response so you can access its fields by name. When your backend does not use a parseable encoding, such as `string` or `no-op`, you can still access the responses but differently. When the `encoding` of your backend is `string` (meaning it does not decode the contents and everything comes in a single string), then you can access its contents using `resp0_content`. On the other hand, when your backend is `no-op` you can access the full response using `resp0` (also as a string). ## Passing previous values outside the `url_pattern` By setting the flag `"sequential": true`, the responses of the backends are available in the following `url_pattern`s. But there will be times when you don’t want the value in the `url_pattern`. You can reuse the payload of previous responses elsewhere by propagating the values using the `sequential_propagated_params` attribute. These are the sequential flags you have on the `proxy` configuration: ##### Fields of endpoint's proxy property \* required fields ##### [`sequential`](https://www.krakend.io/docs/endpoints/sequential-proxy/#sequential "Click to copy link to sequential") boolean When set to true, instead of fetching all backend content in parallel, the calls are made in order (sequentially), allowing you to chain backend requests and making calls dependent one of each other. If any of the calls fail, the remaining ones are aborted. Defaults to `false` ##### [`sequential_propagated_params`](https://www.krakend.io/docs/endpoints/sequential-proxy/#sequential_propagated_params "Click to copy link to sequential_propagated_params") array of strings The list of parameters you want to propagate from a previous response to the next request. Parameters are accessible by Lua scripts, CEL, security policies, the body generator, or plugins. When you add a `resp`-like parameter in this list, the parameter becomes available to the components mentioned in subsequent calls, **uppercasing the first letter**. For instance, if you add `resp0_user`, you can access in the second, third, etc. backends in Lua to `req:params('Resp0_user')`.The format of the parameters must start with `respX_` or `respX`, where `X` is the backend index from which you want to take the parameter. If you don’t set the underscore `_`, you set the whole payload as a parameter. For instance, `resp0` sets a parameter `Resp0` to use in Lua or a Body generator and contains **the entire payload of the backend 0** (as a string). In this extreme case, you must use `no-op` in the backend’s output (even the endpoint has a `json` output encoding) and you should access the value in Lua or a plugin. Note that access to **nested parameters** uses a single string with the dot notation inside, e.g.: `req_params['Resp0_f1.f2.f3']` (CEL and Security Policies), or `{{ index .req_params "Resp0_f1.f2.f3" }}` (body generators). Examples: `["resp0"]` , `["resp1_my_field","resp0_user_id"]` Schema: [https://www.krakend.io/schema/v2.13/endpoint\_extra\_config.json](https://www.krakend.io/schema/v2.13/endpoint_extra_config.json "JSON schema definition") ### Example: Setting a previous response as the payload of the next The following example calls a backend and injects its response as the body payload of the next one. To accomplish this behavior, you can see the `sequential_propagated_params`. It saves the parameter `resp0`, which we reuse as `Resp0` in the Lua code of the second backend. As we are taking **the entire payload** of the first backend (not specific fields), we have set a `no-op` encoding in the first backend so as not to parse the response and pass it as is to the next. The Lua code in the second backend modifies the request and sets its body to the full response of the first backend (`req:body(req:params(\"Resp0\")`). You can copy the code of this endpoint and test locally (it requires you to have the [debug endpoint](/docs/endpoints/debug-endpoint/) enabled). ```json { "endpoint": "/sequential/propagate-body", "extra_config": { "proxy": { "sequential": true, "sequential_propagated_params": ["resp0"] } }, "backend": [ { "encoding": "no-op", "url_pattern": "/__debug/", "host": [ "http://localhost:8080" ] }, { "encoding": "json", "url_pattern": "/param_forwarding/", "host": [ "http://127.0.0.1:8081" ], "extra_config": { "modifier/lua-backend": { "allow_open_libs": true, "pre": "local req = request.load(); req:body(req:params(\"Resp0\"))" } } } ] } ``` --- # [Conditional Routing (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/conditional/) The **Conditional Routing** feature enables **dynamic backend or multi-backend selection** based on request headers (including propagated JWT claims) or custom policy expressions. This functionality enhances flexibility and operational control for complex routing scenarios, including AI/LLM integrations, feature flags, and multi-tenant APIs, among others, without requiring the use of [Security Policies](/docs/enterprise/security-policies/) or raising errors in the log. ![Conditional Routing diagram](/images/documentation/diagrams/conditional-routing.mmd.svg) The functionality **checks if a specific rule is met before executing a backend**, and if it is not, it is skipped from the list. The rules can have any nature, whether they are mutually exclusive within the backend set or complementary to each other. In addition, you can define a special type of **fallback backend** that evaluates only when **ALL rules fail**. The Conditional Routing plays well with the [Sequential Proxy](/docs/enterprise/endpoints/sequential-proxy/) and the [Workflows](/docs/enterprise/endpoints/workflows/), so you can evaluate the response of a previous request and decide whether to execute a backend or not. You can also add backends that do not use Conditional Routing and are always executed. ## Configuration of the Conditional Routing To set a `backend` as conditional, you must include in its `extra_config` the namespace `backend/conditional` with the rules that will allow reaching its destination. Here’s a very simple example: ```json { "url_pattern": "/AB-option/1", "extra_config": { "backend/conditional": { "strategy": "policy", "value": "req_headers.containsCookie('ab-test-xyz', '1')" } } } ``` In the configuration above, when the request reaches the backend and contains a Cookie `AB-TEST-XYZ: 1`, it will be directed to `/AB-option/1`. If the condition is not met, then this backend is not executed, and the flow continues (you would probably want an alternative condition or a fallback behavior). Notice that the example above would require `input_headers: ["Cookie"]` in the endpoint so the backend has access to the Cookie values. The configuration options are: ##### Fields of Conditional Routing \* required fields ##### [`name`](https://www.krakend.io/docs/enterprise/backends/conditional/#name "Click to copy link to name") string Only used with the `header` strategy. It is the name of the header you want to use for the evaluation in the [canonical format of the MIME header](https://www.krakend.io/docs/endpoints/parameter-forwarding/#case-sensitive-and-case-insensitive-parameters). **Make sure to declare the header** in the `input_headers` list of the endpoint. Example: `"X-Test"` ##### [`strategy`](https://www.krakend.io/docs/enterprise/backends/conditional/#strategy "Click to copy link to strategy") * Choose `header` when you want to check the value of a specific header, `policy` when you want to write a more complex logical expression, or `fallback` when the backend will execute when all the rest of conditional backends have failed to evaluate to true. Only one fallback can be defined per endpoint. Possible values are: `"header"` , `"policy"` , `"fallback"` ##### [`value`](https://www.krakend.io/docs/enterprise/backends/conditional/#value "Click to copy link to value") string The value according to the strategy. With the `header` strategy, this is the literal value contained in the header (case sensitive). With the `policy` strategy, the [Security Policy expression](https://www.krakend.io/docs/enterprise/security-policies/policy-language/). When using policies you can access to the variables `req` and `req_params` (a previous backend response might be in the latter), and to [advanced macros](https://www.krakend.io/docs/enterprise/security-policies/advanced-policy-macros/). Access to headers require you to add the corresponding `input_headers` in the endpoint. Examples: `"TestA"` , `"hasHeader('X-Test') && req_params.Resp0_message == '0 items left'"` Schema: [https://www.krakend.io/schema/v2.13/backend/conditional.json](https://www.krakend.io/schema/v2.13/backend/conditional.json "JSON schema definition") See the different use cases below for more examples. Best Practices - Keep policies simple and test them thoroughly before production use to avoid unexpected 500 errors. - Remember to use the `fallback` to provide default handling or graceful degradation when no conditions match. - Use `input_headers` to enable headers relevant for Conditional Routing to reduce attack surface and increase performance. ## Conditional Routing based on JWT claims If you want the Conditional Routing to access JWT tokens, make sure to [propagate claims as headers](/docs/authorization/jwt-validation/#propagate-jwt-claims-as-request-headers) and add them to the `input_headers` list of the endpoint. Once this is done, you can access the claim values using their propagated header names. ## Fallback backend, no-routing, and non-conditionals Depending on the configuration rules you use, when you don’t use a `fallback` conditional, it might happen that none of the backends are suitable to retrieve their content. In that case, the endpoint would return a `500` error as there is no possible response. Use the `fallback` strategy to define a backend when none of the other rules have worked, being your go-to solution for impossible evaluations. In addition, if you add backends that do not have any Conditional Routing, they are always executed (unless you add other policies preventing it). If you mix non-conditional with conditional routing, the `fallback` strategy applies when the conditionals evaluate all to `false`, and the non-conditionals are not taken into account. ## AB Testing and Feature Flags using Conditional Routing The AB testing and Feature Flags enablement is one of the many possible use cases. You can use information in a request, like setting a header for the AB Test, a Cookie, or choosing a route based on who the user is (JWT) or even the current time. Whether it’s AB testing or Feature Flags, you might have server-side functionality that flags a user (e.g., introducing a variant as a JWT claim), or client-side functionality like an SDK that sets a header for a specific variant. In any of the cases, you can use a rule to send users to a different backend, or treat the same backend differently (e.g., use the same backend but change the logging behavior) Here’s a simple configuration for an AB test that reads the content of the `X-AB-TEST` headers to route: ```json { "endpoint": "/AB-TESTING", "input_headers": [ "X-Ab-Test" ], "backend": [ { "url_pattern": "/AB-option/A", "extra_config": { "backend/conditional": { "strategy": "header", "name": "X-Ab-Test", "value": "A" } } }, { "url_pattern": "/AB-option/B", "extra_config": { "backend/conditional": { "strategy": "header", "name": "X-Ab-Test", "value": "B" } } }, { "url_pattern": "/default", "extra_config": { "backend/conditional": { "strategy": "fallback" } } } ] } ``` Notice that this approach is similar to the one offered by the [Dynamic Routing](/docs/enterprise/endpoints/dynamic-routing/) but Dynamic does not offer the fallback option: ```json { "@comment": "Dynamic routing alternative with no fallback possible", "host": ["http://host.example.com"], "url_pattern": "/AB-option/{input_headers.X-Ab-Test}", "disable_host_sanitize": true } ``` In the case above, you would need an additional Security Policy to avoid errors when the header is not present. ## AI/LLM Routing The conditional backend is particularly vital for scalable AI/LLM service orchestration and complex multi-backend routing where simple static backend assignments are insufficient. It integrates safely and predictably within KrakenD’s existing configuration schema and operational model. For example, routing requests based on an X-Test header value allows targeting different AI model versions, while a policy checking hasHeader(‘X-Engine’) can gate experimental logic, complemented by fallback logic for any unmatched requests. ## Quota Routing You can use the conditional backend fallback strategy with the [Governance quota](/docs/enterprise/governance/quota/) by adding a quota check to the conditional backend policy. KrakenD then routes to the fallback backend once the quota runs out. The quota macro format is: `quotaProcessor(PROCESSOR_NAME).check(TIER_VALUE_AS, TIER_VALUE, KEY)`. The `TIER_VALUE_AS` argument is optional — when you pass only two arguments to `check`, it defaults to `literal`. For example, the following configuration routes requests to a premium backend while quota remains and falls back to the fallback backend when it runs out: ```json { "$schema": "https://www.krakend.io/schema/krakend.json", "version": 3, "name": "KrakenD Enterprise API Gateway", "port": 8080, "echo_endpoint": true, "extra_config": { "redis": { "connection_pools": [ { "name": "shared_redis_pool", "address": "localhost:6379" } ] }, "governance/processors": { "quotas": [ { "name": "public_plans", "connection_name": "shared_redis_pool", "hash_keys": true, "on_failure_allow": false, "rejecter_cache": { "N": 10000000, "P": 1e-8 }, "rules": [ { "name": "rule_gold", "limits": [ { "amount": 20, "unit": "hour" }, { "amount": 200, "unit": "day" } ] } ] } ] } }, "endpoints": [ { "endpoint": "/test", "input_headers": ["X-User-Id","X-Level"], "backend": [ { "host": ["http://localhost:8080"], "url_pattern": "/__echo/gold", "extra_config": { "backend/conditional": { "strategy": "policy", "value": "hasHeader('X-Level') && hasHeader('X-User-Id') && getHeader('X-Level') == 'gold' && quotaProcessor('public_plans').check('literal', 'gold', getHeader('X-User-Id'))" }, "governance/quota": { "quota_name": "public_plans", "tier_key": "X-Level", "tiers": [ { "rule_name": "rule_gold", "tier_value": "gold", "tier_value_as": "literal", "strategy": "header", "key": "X-User-Id" } ] } } }, { "host": ["http://localhost:8080"], "url_pattern": "/__echo/fallback", "extra_config": { "backend/conditional": { "strategy": "fallback" } } } ] } ] } ``` The configuration defines a `public_plans` quota processor backed by Redis, with a `rule_gold` rule capping gold-tier users at 20 requests per hour and 200 per day. The `/test` endpoint routes to `/__echo/gold` when both the `X-Level: gold` header and the `X-User-Id` header are present and the user still has remaining quota. When any of those conditions fail — including quota exhaustion — KrakenD routes the request to the `/__echo/fallback` backend instead. This configuration uses the echo endpoint for demonstration purposes. ## Geographic Routing Distribute requests to groups of backends based on request attributes like geographic headers or custom expressions. For instance, by enabling the [GeoIP plugin](/docs/enterprise/endpoints/geoip/), you can change the backend datacenter depending on the origin of the request. ```json { "endpoint": "/foo", "backend": [ { "url_pattern": "/bar", "host":["europe.example.com"], "extra_config": { "backend/conditional": { "strategy": "policy", "value": "geoIP().RepresentedCountry.IsInEuropeanUnion" } } }, { "url_pattern": "/bar", "host":["us.example.com"], "extra_config": { "backend/conditional": { "strategy": "fallback" } } } ] } ``` Another useful policy in this example could be `geoIP().Country.IsoCode in ["US","UK","AU"]` ## Differences with similar functionality A [Security Policy](/docs/enterprise/security-policies/) or a [CEL expression](/docs/enterprise/endpoints/common-expression-language-cel/) can abort the execution of an endpoint or the backend, and they allow you to set custom policies to do so. These two components have the mission to protect content from a security point of view. When the rule is met, the execution raises an error and stops. On the other side, the Conditional Routing treats it as a “normal behavior” when a condition is not met and the backend is skipped, without raising an error in the other functionality. The Conditional Routing assimilates the rule into the regular flow of your application, not as an exception. Another close functionality is the [Dynamic Routing](/docs/enterprise/endpoints/dynamic-routing/), which dynamically defines the host or URL. Still, here you choose a completely different set of backend definitions according to rules, not values. --- # [Virtual Hosts (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/service-settings/virtual-hosts/) The Virtual Host server allows you to run different configurations of KrakenD endpoints based on the host used to access the server. For instance, you can declare different endpoint configurations for a path `/foo` that is accessed through `abcd.example.com/foo` or `efgh.example.com/foo`. These two could have different rate limits or authorization rules, for example. ## Virtual host configuration To add virtual hosts, add the component `server/virtualhost` under the service `extra_config` and declare the host names that you will recognize. The hosts can be a simple list or using aliases, as depicted below: ```json { "version": 3, "extra_config": { "server/virtualhost": { "hosts": ["abcd.example.com", "efgh.example.com"] } } } ``` The alias might come in handy to have uncluttered endpoint declarations: ```json { "version": 3, "extra_config": { "server/virtualhost": { "aliased_hosts": { "user_api":"abcd.example.com", "basket_api":"efgh.example.com" } } } } ``` You can use `hosts` or `aliased_hosts`, but not both simultaneously. ##### Fields of VirtualHost \* required fields Minimum configuration needs **one of**: `hosts` , or `aliased_hosts` ##### [`aliased_hosts`](https://www.krakend.io/docs/enterprise/service-settings/virtual-hosts/#aliased_hosts "Click to copy link to aliased_hosts") object A map of all recognized virtual hosts where the key is the alias and the value the host name, including the port if it’s not 443 or 80. The values declared here must match the content of the `Host` header passed by the client. The alias must be an alphanumeric string. Example: `{"user_api":"users.svc.example.com:9000"}` ##### [`[a-z0-9_]+`](#aliased_hosts-%5ba-z0-9_%5d%2b "Click to copy link to [a-z0-9_]+") string The key of this map must compile with the regexp `a-z0-9_` and the host name is the string that matches the value sent by the user in the `Host` header. ##### [`hosts`](https://www.krakend.io/docs/enterprise/service-settings/virtual-hosts/#hosts "Click to copy link to hosts") array of strings All recognized virtual hosts by KrakenD must be listed here. The values declared here must match the content of the `Host` header when passed by the client. Example: `["api-a.example.com","api-b.example.com"]` Schema: [https://www.krakend.io/schema/v2.13/server/virtualhost.json](https://www.krakend.io/schema/v2.13/server/virtualhost.json "JSON schema definition") ## Declaring endpoints When you enable the virtual host plugin, KrakenD first checks the `Host` header and tries to match an endpoint with the format `/__virtual/{host}/path` in the configuration. For instance, if the user is accessing `example.com/path`, you must declare an endpoint `/__virtual/example.com/path`. If the configuration does not have a `/__virtual/` endpoint with the requested `example.com` host, the gateway resolves to the default endpoint `/path` if it exists or returns a `404` otherwise. You can also create an endpoint for all the recognized virtual hosts using a variable like this: `/__virtual/{host}/path`, and unrecognized hosts will fall into `/path`. You can use an alias (using `aliased_hosts`) to declare virtual endpoints easily instead of using the full domain name. E.g., instead of `/__virtual/example.com:9000/path`, you could write `/__virtual/domain1/path`. ### Virtual host example Given the following `extra_config` configuration: ```json { "extra_config": { "server/virtualhost": { "hosts": [ "host-a.example.com", "host-b.example.com" ] } } } ``` And the following `endpoints`: ```json { "version": 3, "endpoints": [ { "endpoint": "/foo", "backend": [ { "url_pattern": "/__debug/no-host", "host": ["http://localhost:8080"] } ] }, { "endpoint": "/__virtual/host-a.example.com/foo", "backend": [ { "url_pattern": "/__debug/host-A", "host": ["http://localhost:8080"] } ] }, { "endpoint": "/__virtual/host-b.example.com/foo", "backend": [ { "url_pattern": "/__debug/host-B", "host": ["http://localhost:8080"] } ] } ] } ``` You can check how `/foo` hits different endpoints in the following order: - `curl -i -H 'Host: anything' http://localhost:8080/foo`: No rewriting placed as `host` is unknown. Hits the first endpoint. - `curl -i -H 'Host: host-a.example.com' http://localhost:8080/foo`: Hits the second endpoint - `curl -i -H 'Host: host-b.example.com' http://localhost:8080/foo`: Hits the third endpoint ## Using hosts with non-default ports Sometimes, you expose the API on a non-default port (different from `80` and `443`). In these cases, the `Host` header comes with the port, for instance, `Host: example.com:1234`. When the header contains the port, you must add it as a different entry in the virtual host configuration and the endpoint definition. If the same hostname (e.g., `example.com`) uses different ports, you must use `aliased_hosts`; otherwise, the gateway will panic during startup. The following example supports `Host` headers with values `Host: example.com:1234` and `Host: example.com`. Notice that it makes use of `aliased_hosts`: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "echo_endpoint": true, "host": ["http://localhost:8080"], "extra_config": { "server/virtualhost": { "aliased_hosts": { "example": "example.com", "example1234": "example.com:1234" } } }, "endpoints": [ { "endpoint": "/default", "backend": [{ "url_pattern": "/__echo/any-non-matching-host", "allow": ["req_uri"] }] }, { "endpoint": "/__virtual/example/default", "backend": [{ "url_pattern": "/__echo/virtualhost-without-port", "allow": ["req_uri"] }] }, { "endpoint": "/__virtual/example1234/default", "backend": [{ "url_pattern": "/__echo/virtualhost-with-port", "allow": ["req_uri"] }] } ] } ``` ## Upgrading from the old virtual host plugin (before v2.4) If you used the static plugin before EE v2.4, eliminate now all entries referring to plugins. This means: - If you don’t use additional plugins, you can get rid of the `plugin` entry in the root level. - You can delete all `plugin/http-server` objects if they only use the static-filesystem - You can delete any `virtualhost` object in the configuration This can be summarized with a diff as: ```diff "version": 3, - "plugin": { - "pattern":".so", - "folder": "/opt/krakend/plugins/" - }, "extra_config": { - "plugin/http-server": { - "name": ["virtualhost"], - "virtualhost": { + "server/virtualhost": { ``` For instance, if you had this configuration: ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "extra_config": { "plugin/http-server": { "name": ["virtualhost", "some-other-plugin" ], "virtualhost": { "hosts": ["host-a.tld", "host-b.tld"] } } } } ``` This now becomes: ```json { "version": 3, "extra_config": { "server/virtualhost": { "hosts": ["host-a.tld", "host-b.tld"] } } } ``` No additional changes are required in the endpoints. --- # [Handling Client Redirects (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/client-redirect/) When a backend returns a redirect, the default behavior of KrakenD is to follow the redirection and serve the final content to the user. When you don’t want this behavior and let the end client follow the redirect, you must enable the `no_redirect` **HTTP client option**. ## No redirect configuration The `no_redirect` option is one in the list of [HTTP client options](/docs/enterprise/backends/http-client/) in the backend. By enabling the flag, you can stop KrakenD from following redirections like a `301`. Enable no-op too You must use the **no-op encoding** if you want the client to receive the status code 301 or 302. Otherwise, the gateway would return a 500 error because it does not get a success (`20x`) code. Example of configuration: ```json { "version": 3, "endpoints": [ { "endpoint": "/redirect", "output_encoding": "no-op", "backend": [ { "url_pattern": "/__debug", "encoding": "no-op", "extra_config": { "backend/http/client": { "no_redirect": true } } } ] } ] } ``` ## Migration from old plugin Prior to v2.3 the `no-redirect` plugin offered this functionality, and is now deprecated as it is supported natively. If you used the plugin you will need to execute the following change in your configuration: ```diff "extra_config": { - "plugin/http-client": { - "name": "no-redirect" + "backend/http/client": { + "no_redirect": true } } ``` --- # [URL Rewrite for API Endpoints (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/url-rewrite/) The `url-rewrite` plugin allows you to define a set of URLs or regular expressions that are handled by an endpoint of your choice. In other words, this plugin allows you to declare additional URLs other than the ones defined under the `endpoints` configuration, used as aliases of existing endpoints. Extendind router capabilities This plugin allows you to overcome some of the routing capabilities of KrakenD, such as declaring endpoints that would be conflicting or unaccepted by the KrakenD core. ## URL rewrite configuration You need to define a URL dictionary specifying how requests map to the existing endpoints to use the plugin. There are two diferent ways of declaring rewrites: - `literal`: The literal match takes precedence and is checked first. If the requested URL is exactly the one defined in the map, then the redirection happens. - `regexp`: The regexp expressions are checked in sequence when there are no literal matches. You can use the capturing groups in the endpoint definition. ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "extra_config": { "plugin/http-server": { "name": ["url-rewrite", "some-other-plugin-here" ], "url-rewrite": { "literal": { "/hi-there": "/hello", "/whatsup": "/hello" }, "regexp": [ ["^/hi-there/([\w\d\s]+)/bar$", "/hello/${1}"], ["^/whats/up/([\w\d\s]+)$", "/hey/${1}/whatsup"] ] } } } } ``` The configuration options inside the `url-rewrite` plugin are: ##### Fields of URL rewrite \* required fields Minimum configuration needs **any of**: `literal` , or `regexp` ##### [`literal`](https://www.krakend.io/docs/enterprise/endpoints/url-rewrite/#literal "Click to copy link to literal") object A map with the exact desired url and its mapping to an endpoint. If the endpoint has `{placeholders}` you need to write them, but the literal value `{placeholders}` is passed. Example: `{"/hi-there":"/hello","/whatsup":"/hello"}` ##### [`regexp`](https://www.krakend.io/docs/enterprise/endpoints/url-rewrite/#regexp "Click to copy link to regexp") array of arrays A list of lists, containing the regular expression that defines the URL to be rewritten, and its endpoint destination. You can use the capturing groups with the syntax `${1}`, `${2}`, etc. Schema: [https://www.krakend.io/schema/v2.13/plugin/url-rewrite.json](https://www.krakend.io/schema/v2.13/plugin/url-rewrite.json "JSON schema definition") All regular expressions are evaluated in order. The first matching expression will rewrite and exit. --- # [Router Options](https://www.krakend.io/docs/service-settings/router-options/) The **optional router configuration** allows you to set global flags that change how KrakenD processes the requests globally at the router layer. Generally speaking **you don’t need this**. But there is an exception in every case, and you might need to change some values. ## Configuration for the router The `router` controls the behavior of KrakenD toward users. Its settings affect all activity in the gateway. For instance, you can **obfuscate the X-KrakenD-version header**, set a **custom body for 404 errors**, **remove the requests from the logs**, or define how to **calculate client IPs** to name a few examples. To change the router behavior, you must add the namespace `router` inside the `extra_config` at the root of the configuration file. For instance: ```json { "version": 3, "extra_config": { "router": { "hide_version_header": true } } } ``` All the options you can set under `router` are: ##### Fields of Router Options \* required fields ##### [`app_engine`](https://www.krakend.io/docs/service-settings/router-options/#app_engine "Click to copy link to app_engine") boolean The app\_engine boolean trusts headers starting with X-AppEngine… for better integration with that PaaS. ##### [`auto_options`](https://www.krakend.io/docs/service-settings/router-options/#auto_options "Click to copy link to auto_options") boolean When true, enables the autogenerated OPTIONS endpoint for all the registered paths ##### [`decompress_gzip`](https://www.krakend.io/docs/service-settings/router-options/#decompress_gzip "Click to copy link to decompress_gzip") integer **Enterprise only.** Decompresses any Gzipped content before sending it to the backend when the `Content-Encoding` has `gzip` in the first position. You can also set this value [per endpoint](https://www.krakend.io/docs/enterprise/endpoints/maximum-request-size/). Defaults to `0` ##### [`disable_access_log`](https://www.krakend.io/docs/service-settings/router-options/#disable_access_log "Click to copy link to disable_access_log") boolean Stops registering access requests to KrakenD in the logs. You can still have a [Backend Log](https://www.krakend.io/docs/logging/#backend) if needed. Defaults to `false` ##### [`disable_gzip`](https://www.krakend.io/docs/service-settings/router-options/#disable_gzip "Click to copy link to disable_gzip") boolean **Enterprise only.** All the output to the end user on the Enterprise Edition uses gzip when accepted by the client. Use this flag to remove gzip compression. Defaults to `false` ##### [`disable_handle_method_not_allowed`](https://www.krakend.io/docs/service-settings/router-options/#disable_handle_method_not_allowed "Click to copy link to disable_handle_method_not_allowed") boolean Whether to checks if another method is allowed for the current route, if the current request can not be routed. If this is the case, the request is answered with Method Not Allowed and HTTP status code 405. If no other Method is allowed, the request is a 404. ##### [`disable_health`](https://www.krakend.io/docs/service-settings/router-options/#disable_health "Click to copy link to disable_health") boolean When true you don’t have any exposed health endpoint. You can still use a TCP checker or build an endpoint yourself. Defaults to `false` ##### [`disable_path_decoding`](https://www.krakend.io/docs/service-settings/router-options/#disable_path_decoding "Click to copy link to disable_path_decoding") boolean Disables automatic validation of the url params looking for url encoded ones. ##### [`disable_redirect_fixed_path`](https://www.krakend.io/docs/service-settings/router-options/#disable_redirect_fixed_path "Click to copy link to disable_redirect_fixed_path") boolean If true, the router tries to fix the current request path, if no handle is registered for it ##### [`disable_redirect_trailing_slash`](https://www.krakend.io/docs/service-settings/router-options/#disable_redirect_trailing_slash "Click to copy link to disable_redirect_trailing_slash") boolean Disables automatic redirection if the current route can’t be matched but a handler for the path with (without) the trailing slash exists. Only works if `disable_redirect_fixed_path` is also set to true. ##### [`error_body`](https://www.krakend.io/docs/service-settings/router-options/#error_body "Click to copy link to error_body") object Sets custom error bodies for 404 and 405 errors. ##### [`404`](#error_body-404 "Click to copy link to 404") object Write any JSON object structure you would like to return to users when they request an endpoint not known by KrakenD. 404 Not Found errors. ##### [`405`](#error_body-405 "Click to copy link to 405") object Write any JSON object structure you would like to return to users ##### [`forwarded_by_client_ip`](https://www.krakend.io/docs/service-settings/router-options/#forwarded_by_client_ip "Click to copy link to forwarded_by_client_ip") boolean When set to true, the client IP will be parsed from the default request’s headers, or the custom ones (`remote_ip_headers`). If the IP has passed through a **trusted proxy** (e.g.: a proxy, load balancer, or a third party application) it will be extracted. If no IP can be fetched, it falls back to the IP obtained from the request’s remote address. When declared **you must** configure `trusted_proxies` too. Defaults to `false` ##### [`health_path`](https://www.krakend.io/docs/service-settings/router-options/#health_path "Click to copy link to health_path") string The path where you’d like to expose the health endpoint. Defaults to `"/__health"` ##### [`hide_version_header`](https://www.krakend.io/docs/service-settings/router-options/#hide_version_header "Click to copy link to hide_version_header") boolean Removes the version of KrakenD used in the X-KrakenD-version headers. Defaults to `false` ##### [`logger_skip_paths`](https://www.krakend.io/docs/service-settings/router-options/#logger_skip_paths "Click to copy link to logger_skip_paths") array of strings Defines the set of paths that are removed from the logging. ##### [`max_multipart_memory`](https://www.krakend.io/docs/service-settings/router-options/#max_multipart_memory "Click to copy link to max_multipart_memory") integer Sets the maxMemory param that is given to http.Request’s Multipart Form method call. ##### [`max_payload`](https://www.krakend.io/docs/service-settings/router-options/#max_payload "Click to copy link to max_payload") integer **Enterprise only.** Limits the maximum number of bytes a user can send to the gateway. `0` means no limit. You can also set this value [per endpoint](https://www.krakend.io/docs/enterprise/endpoints/maximum-request-size/). Defaults to `0` ##### [`remote_ip_headers`](https://www.krakend.io/docs/service-settings/router-options/#remote_ip_headers "Click to copy link to remote_ip_headers") array of strings List of headers used to obtain the client IP when `forwarded_by_client_ip` is set to `true` and the remote address is matched by at least one of the network origins of `trusted_proxies`. ##### [`remove_extra_slash`](https://www.krakend.io/docs/service-settings/router-options/#remove_extra_slash "Click to copy link to remove_extra_slash") boolean A parameter can be parsed from the URL even with extra slashes. Defaults to `false` ##### [`return_error_msg`](https://www.krakend.io/docs/service-settings/router-options/#return_error_msg "Click to copy link to return_error_msg") boolean 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. Defaults to `false` ##### [`trusted_proxies`](https://www.krakend.io/docs/service-settings/router-options/#trusted_proxies "Click to copy link to trusted_proxies") array of strings List of network origins (IPv4 addresses, IPv4 CIDRs, IPv6 addresses or IPv6 CIDRs) from which to trust request’s headers that contain alternative client IP when `forwarded_by_client_ip` is `true`. When declared **you must** configure `forwarded_by_client_ip` set to `true`, and optionally `remote_ip_headers`. Schema: [https://www.krakend.io/schema/v2.13/router.json](https://www.krakend.io/schema/v2.13/router.json "JSON schema definition") ## Return the real client IP The flags `forwarded_by_client_ip`, `remote_ip_headers`, and `trusted_proxies` determine together how you get the client IP address. Dependant flags When your setup requires any attributes `forwarded_by_client_ip` or `trusted_proxies`, you **must declare both**. The attribute `remote_ip_headers` is only necessary when you want to overwrite the default headers. ### How the IP is retrieved internally The flow the gateway follows to extract the client IP is as follows: - The gateway fetches the IP from the connecting remote address - Then it checks the IP from the headers listed under `remote_ip_headers`. When this list is not in the configuration, it looks in `X-Forwarded-For` and `X-Real-IP` (default lookup headers). - Unless you have a single KrakenD exposed to the internet or working locally, the client request will travel through different relays. These relays generally modify the header to append where they received the request from (whether it is the originating client, a load balancer, another proxy, etc.). So, you usually have a comma-separated list of IPs in the header containing the IP as they travel from one relay to the other. For instance, if there is just one hop before KrakenD, the header the gateway sees could look like `X-Forwarded-For: 1.2.3.4,172.20.0.1` where `1.2.3.4` is the real IP of the user, and `172.20.0.1` the last relay seen. - The list of `trusted_proxies` sets which IPs are part of these network hops, and **the last IP before a known trusted proxy** is the actual IP. - If checking the trusted proxies does not work, it will return the remote address in the first step. Here’s an example of behavior. Suppose the gateway receives a header `X-Forwarded-For: A,B,C,D` (IPs are expressed as letters for simplification). If your `trusted_proxies` configuration contains ranges for `C` and `D`, then the returned IP is `B`, as `A` could have been spoofed by the client. **The real IP is stored in the `X-Forwarded-For` header that KrakenD uses.** The following example shows a configuration that takes the user IP from an `X-Forwarded-For` header only, and the network origin has relays in the range `172.16.0.1/12` (IPv4 Private Address Space). The endpoint `/ip` returns the IP received. You can test this locally: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "echo_endpoint": true, "extra_config": { "router":{ "forwarded_by_client_ip": true, "remote_ip_headers": [ "X-Forwarded-For" ], "trusted_proxies": [ "172.16.0.1/12" ] } }, "endpoints": [ { "endpoint": "/ip", "backend": [ { "host": ["http://localhost:8080"], "url_pattern": "/__echo", "allow": ["req_headers.X-Forwarded-For"] } ] } ] } ``` ## Hide the version in the `X-KrakenD-version` header You can remove the KrakenD version your installation uses by adding the `hide_version_header` flag as follows: ```json { "version": 3, "extra_config": { "router": { "hide_version_header": true } } } ``` The banner header will show an `undefined` version when the flag is set to `true`. To remove the header entirely, you must remove it in the CDN or layer in front of KrakenD. ## Custom JSON body for 404 and 405 errors You can also define generic responses for 404 and 405 errors by defining the response object you will return to clients using the `error_body` property. Here is an example: ```json { "version": 3, "extra_config": { "router": { "error_body": { "404": { "msg": "Unknown endpoint", "status": 404 }, "405": { "oh-my-god": "What on earth are you requesting?" } } } } } ``` ## 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 setting `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 the client the reason for the failure. The error is **written in the body as is**. Gateway errors != backend errors This option will return the gateway interpretation of the error (such as there was an *invalid status code*). Nevertheless, when this option is mixed with a backend flag [`return_error_code`](/docs/backends/detailed-errors/#showing-backend-errors) set to `true`, the backend error is returned, but its encoding is lost. Consider using [`return_error_details`](/docs/backends/detailed-errors/#return-backend-errors-in-a-new-key) to create a key with the error and support aggregation, or just use `no-op` encoding if you need errors as returned by the backend. ```json { "version": 3, "extra_config": { "router":{ "return_error_msg":true } } ``` ## Remove requests from logs There are two options to remove content from logs: the `logger_skip_paths` (list of paths you don’t want to see in the logs) and `disable_access_log`, which stops registering access requests. ```json { "version": 3, "extra_config": { "router":{ "logger_skip_paths":[ "/__health" ], "disable_access_log": true } } ``` --- # [HTTP Proxy: Access to backends through a proxy (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/http-proxy/) The HTTP proxy adds an intermediate layer where the connection to the backend goes through an external proxy. There are two different ways to connect to your backends through a corporate proxy: - Connect to all backends using a proxy - Connect to a specific backend using a proxy No longer a plugin Since KrakenD Enterprise v2.3 the HTTP proxy functionality is no longer offered as a plugin, but as a regular component to improve the connectivity options. ## Connecting to all backends using a proxy Passing an environment variable `HTTP_PROXY` when starting the gateway ensures that all backend requests pass through the defined proxy. For instance: Term  ``` $docker run --rm -v "$PWD:/etc/krakend" -e HTTP_PROXY=http://myproxy:8080 krakend/krakend-ee ``` ## Connecting to a specific backend using a proxy If instead, you want to route a single backend connection through a proxy, you must add in the `backend` the `proxy_address` attribute under the `backend/http/client` namespace: ```json { "version": 3, "endpoints": [ { "endpoint": "/health", "output_encoding": "no-op", "backend": [ { "url_pattern": "/__health", "host": ["http://localhost:8080"], "encoding": "no-op", "extra_config": { "backend/http/client": { "proxy_address": "http://myproxy:8080" } } } ] } ] } ``` The configuration flags relative to the proxy functionality is: - `proxy_address` (*string*): The proxy address used to forward the traffic. The address must contain the protocol and the port. The `backend/http/client` offers more options. ## Migration from old plugin Prior to v2.3 the `http-proxy` plugin offered this functionality, and is now deprecated as it is supported natively with the configuration explained above. If you used the plugin you will need to execute the following change in your configuration files: ```diff "extra_config": { - "plugin/http-client": { - "name": "http-proxy", - "proxy_address": "http://myproxy:8080" + "backend/http/client": { + "proxy_address": "http://myproxy:8080" } ``` --- # [HTTP Global Client settings](https://www.krakend.io/docs/service-settings/http-transport-settings/) When KrakenD communicates using HTTP **with all your upstream services**, it implements a concurrent-safe round tripper that supports HTTP, HTTPS, and HTTP proxies, and it caches connections for future re-use. This may leave many open connections when accessing many hosts. You can change the behavior of the transport layer using several settings presented below. The following settings affect **all connections from KrakenD to your services**. If you want to customize any of the settings below, they must be written at the top level of the configuration. ##### Settings for KrakenD to your service communication \* required fields ##### [`client_tls`](https://www.krakend.io/docs/service-settings/http-transport-settings/#client_tls "Click to copy link to client_tls") Allows to set specific transport settings when using TLS in your upstream services. See [TLS Client](https://www.krakend.io/docs/service-settings/tls/) for more settings ##### [`dialer_fallback_delay`](https://www.krakend.io/docs/service-settings/http-transport-settings/#dialer_fallback_delay "Click to copy link to dialer_fallback_delay") string Specifies the length of time to wait before spawning a RFC 6555 Fast Fallback connection. If zero, a default delay of 300ms is used. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"300ms"` ##### [`dialer_keep_alive`](https://www.krakend.io/docs/service-settings/http-transport-settings/#dialer_keep_alive "Click to copy link to dialer_keep_alive") string The interval between keep-alive probes for an active network connection. If zero, keep-alive probes are sent with a default value (currently 15 seconds), if supported by the protocol and operating system. Network protocols or operating systems that do not support keep-alives ignore this field. If negative, keep-alive probes are disabled. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"15s"` ##### [`dialer_timeout`](https://www.krakend.io/docs/service-settings/http-transport-settings/#dialer_timeout "Click to copy link to dialer_timeout") string The timeout of the dial function for creating connections.The default is no timeout. With or without a timeout, the operating system may impose its own earlier timeout. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`disable_compression`](https://www.krakend.io/docs/service-settings/http-transport-settings/#disable_compression "Click to copy link to disable_compression") boolean When true prevents requesting compression with an `Accept-Encoding: gzip` request header when the Request contains no existing Accept-Encoding value. If the Transport requests gzip on its own and gets a gzipped response, it’s transparently decoded. However, if the user explicitly requested gzip it is not automatically uncompressed. Defaults to `false` ##### [`disable_keep_alives`](https://www.krakend.io/docs/service-settings/http-transport-settings/#disable_keep_alives "Click to copy link to disable_keep_alives") boolean When true it disables HTTP keep-alives and will only use the connection to the server for a single HTTP request. Defaults to `false` ##### [`expect_continue_timeout`](https://www.krakend.io/docs/service-settings/http-transport-settings/#expect_continue_timeout "Click to copy link to expect_continue_timeout") string If non-zero, specifies the amount of time to wait for a server’s first response headers after fully writing the request headers if the request has an `Expect: 100-continue` header. Zero means no timeout and causes the body to be sent immediately, without waiting for the server to approve. This time does not include the time to send the request header. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`idle_connection_timeout`](https://www.krakend.io/docs/service-settings/http-transport-settings/#idle_connection_timeout "Click to copy link to idle_connection_timeout") string The maximum number of idle (keep-alive) connections across all hosts. Zero means no limit. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`max_idle_connections`](https://www.krakend.io/docs/service-settings/http-transport-settings/#max_idle_connections "Click to copy link to max_idle_connections") integer The maximum number of idle (keep-alive) connections across all hosts. Zero means no limit. Defaults to `0` ##### [`max_idle_connections_per_host`](https://www.krakend.io/docs/service-settings/http-transport-settings/#max_idle_connections_per_host "Click to copy link to max_idle_connections_per_host") integer If non-zero, controls the maximum idle (keep-alive) connections to keep per-host. If zero, `250` is used instead. Defaults to `250` ##### [`response_header_timeout`](https://www.krakend.io/docs/service-settings/http-transport-settings/#response_header_timeout "Click to copy link to response_header_timeout") string If non-zero, specifies the amount of time to wait for a server’s response headers after fully writing the request (including its body, if any). This time does not include the time to read the response body. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` Schema: [https://www.krakend.io/schema/v2.13/krakend.json](https://www.krakend.io/schema/v2.13/krakend.json "JSON schema definition") Finally, the **TLS Handshake Timeout** is hardcoded to 10 seconds and cannot be changed. ## Override settings using environment vars When you declare in the configuration file any of the HTTP server or transport settings declared above, you can [override its value through environment variables](/docs/configuration/environment-vars/) when starting the server. All the environment variables have the same name as the settings above in uppercase and with the `KRAKEND_` prefix. The following env vars are available: - `KRAKEND_DIALER_TIMEOUT` - `KRAKEND_DIALER_KEEP_ALIVE` - `KRAKEND_DIALER_FALLBACK_DELAY` - `KRAKEND_DISABLE_COMPRESSION` - `KRAKEND_DISABLE_KEEP_ALIVES` - `KRAKEND_MAX_IDLE_CONNECTIONS` - `KRAKEND_MAX_IDLE_CONNECTIONS_PER_HOST` - `KRAKEND_IDLE_CONNECTION_TIMEOUT` - `KRAKEND_RESPONSE_HEADER_TIMEOUT` - `KRAKEND_EXPECT_CONTINUE_TIMEOUT` You can start KrakenD with the desired variables to override what you have in the configuration: Term  ``` $KRAKEND_MAX_IDLE_CONNECTIONS_PER_HOST=200 krakend run -c krakend.json ``` ## Max IDLE connections Having a high number of IDLE connections to every backend affects directly to the performance of the proxy layer. This is why you can control the number using the `max_idle_connections` setting. For instance: ```json { "version": 3, "max_idle_connections": 150 } ``` KrakenD will close connections sitting idle in a “keep-alive” state when `max_idle_connections` is reached. If no value is set in the configuration file, KrakenD will use `250` by default. Every ecosystem needs its own setting, have this in mind: - If you set a number very high for `max_idle_connections` you might exhaust your system’s port limit. - If you set a number very low, new connections will be frequently created and a low rate of connection reuse will take place. --- # [HTTP Per-backend Client settings](https://www.krakend.io/docs/backends/http-client/) The HTTP client namespace allows you to set the behavior of the HTTP connections between KrakenD and your backend service. ### Send the payload on 307 and 308 redirects KrakenD does not duplicate the body of the request when following a redirection because automatically doing it would affect the performance of all requests. In the unusual cases where your backend responds with a `307 Temporary Redirect` or a `308 Permanent Redirect`, enable the following flag to resend the original payload to the final redirected service: ##### Fields of `"backend/http_client"` \* required fields ##### [`send_body_on_redirect`](https://www.krakend.io/docs/backends/http-client/#send_body_on_redirect "Click to copy link to send_body_on_redirect") boolean Post the original body to the final URL after a 307 or a 308 redirection. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/backend/http\_client.json](https://www.krakend.io/schema/v2.13/backend/http_client.json "JSON schema definition") Here is a configuration example: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "endpoints": [ { "endpoint": "/foo", "backend": [ { "host": ["https://api"], "url_pattern": "/url-that-will-redirect-with-307", "extra_config": { "backend/http/client": { "send_body_on_redirect": true } } } ] } ] } ``` ## Avoid HTTP redirection and other options To prevent KrakenD from following redirects, use specific TLS options, and utilize intermediate proxies and other options, refer to the [HTTP Client options in the Enterprise Edition](/docs/enterprise/backends/http-client/). --- # [HTTP Per-backend Client settings (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/http-client/) The HTTP client options allow you to set the client TLS settings for each backend, configure the no-redirect behavior, or connect through an external proxy. You can set the client TLS settings for all backends [as a global setting](/docs/enterprise/service-settings/tls/#client-tls-settings). Nevertheless, you can decide to individually change the client TLS settings for a specific backend. ## HTTP client settings To do so, include the `backend/http/client` namespace in the `backend`’s extra configuration. It accepts the following settings: ##### Fields of HTTP Client options \* required fields ##### [`client_tls`](https://www.krakend.io/docs/enterprise/backends/http-client/#client_tls "Click to copy link to client_tls") Allows to set specific transport settings when using TLS in your upstream services. See the global [Client TLS options](https://www.krakend.io/docs/service-settings/tls/#client-tls-settings) for the list of all supported options. ##### [`no_redirect`](https://www.krakend.io/docs/enterprise/backends/http-client/#no_redirect "Click to copy link to no_redirect") boolean Set `no_redirect` to true if you don’t want KrakenD to follow redirects and let the consuming user to receive the `30x` status code. Defaults to `false` ##### [`proxy_address`](https://www.krakend.io/docs/enterprise/backends/http-client/#proxy_address "Click to copy link to proxy_address") The proxy address used to forward the traffic. The address must contain the protocol and the port. Example: `"http://proxy.corp:9099"` ##### [`send_body_on_redirect`](https://www.krakend.io/docs/enterprise/backends/http-client/#send_body_on_redirect "Click to copy link to send_body_on_redirect") boolean Post the original body to the final URL after a 307 or a 308 redirection. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/backend/http\_client.json](https://www.krakend.io/schema/v2.13/backend/http_client.json "JSON schema definition") ### Example of Client TLS The following configuration defines a custom certificate for any backend in the system through the `ca_certs`, but the `/foo` endpoint uses a different certificate instead. It also prevents the gateway from following the redirections and returns the `302` codes as they are to the client, and finally, it connects using an overridden certificate and through a proxy. ```json { "version": 3, "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "client_tls": { "ca_certs": [ "custom.pem" ] }, "endpoints": [ { "endpoint": "/foo", "output_encoding": "no-op", "backend": [ { "host": ["https://api"], "url_pattern": "/foo", "encoding": "no-op", "extra_config": { "backend/http/client": { "no_redirect": true, "client_tls": { "allow_insecure_connections": true, "ca_certs": [ "other_cert.pem" ] }, "proxy_address": "http://proxy.corporation.com:9099" } } } ] } ] } ``` --- # [Handling Concurrent Requests](https://www.krakend.io/docs/endpoints/concurrent-requests/) The concurrent requests are an excellent technique to **improve the response times and decrease error rates** by requesting in parallel the same information multiple times. Yes, you make the same request to several backends instead of asking to just one. When the first backend returns the information, the remaining requests are canceled. It depends a lot on your configuration, but **improving response times by a 75%** or more in comparison to direct usage without the gateway is not something rare. When using concurrent requests, the backend services **must be able to handle an additional load**, as this technique adds more pressure to the backends. If this is the case, and your requests are idempotent, then you can use `concurrent_calls` as follows: ```json { "endpoints": [ { "endpoint": "/products", "method": "GET", "concurrent_calls": 3, "backend": [ { "host": [ "http://server-01.api.com:8000", "http://server-02.api.com:8000" ], "url_pattern": "/foo" } ] } ] } ``` In the example above, when a user calls the `/products` endpoint, KrakenD opens three different connections to the backends and returns the first fastest successful response. Notice that despite this backend has only two servers to handle the load, the `concurrent_calls` is set to three. The two settings are not related, and KrakenD is going to open three connections against these two servers nevertheless. Which server receives 1,2 or all three depends on the internal load balancer decision. ## What is the ideal number for `concurrent_calls`? There isn’t a recommended number, as this ultimately depends on how your services behave and the number of resources you have for every service. Nevertheless, we could say that if you are interested in this feature, `3` is a good number, as it offers superior results without needing to double your resources. Generally speaking, if you work on the cloud, enabling this feature is safer as you can grow the resources easily (but put an eye on the costs). If your hardware is limited (on-premise), do not activate this feature in production without doing your proper load tests. Concurrent calls works better with encodings different than `no-op`. Endpoints returning chunked responses do not play nice with concurrency when `no-op` is enabled. ## How does `concurrent_calls` work? KrakenD sends up to N `concurrent_calls` to your backends for the **same request** to an endpoint. When the first successful response is received, KrakenD cancels the remaining requests and ignores any previous failures. Only in the case that all `concurrent_calls` fail, the endpoint receives the failure as well. The apparent trade-off of this strategy is the increment of the load in the backend services, so make sure your infrastructure is ready for it. However, your users love it: Fewer errors and faster responses! ### Impact of concurrent requests To demonstrate the impact of this component, let’s imagine two different scenarios: the happy one and the sad one. Here you have the **CDF (cumulative distribution)** and the **PDF (probability distribution)** of these two cases (the time range is just a *placeholder* for whatever your actual response time values are, replace the `100` with your `max_response_time`): ![CDF happy vs. sad](/images/documentation/concurrency/CDF_happy_vs_sad.png) ![PDF happy vs. sad](/images/documentation/concurrency/PDF_happy_vs_sad.png) Just as a reminder: for the CDF graphs, the more to the left the line is, the better. For the PDF graphs, the more to the left and the narrow the peak is, the better. The following graph is the effect of using different concurrency levels for the happy case (notice how the response times concentrate around 20% of the `max_response_time`): ![CDF concurrency happy](/images/documentation/concurrency/CDF_concurrency_happy.png) ![PDF concurrency happy](/images/documentation/concurrency/PDF_concurrency_happy.png) Here, you have the effects for the sad case: ![CDF concurrency sad](/images/documentation/concurrency/CDF_concurrency_sad.png) ![PDF concurrency sad](/images/documentation/concurrency/PDF_concurrency_sad.png) The concurrent request component also reduces the error rate of the exposed endpoint by **orders of magnitude**. ![Error concurrency up to 2](/images/documentation/concurrency/Error_concurrent_requests_up_2.png) ![Error concurrency bigger than 2](/images/documentation/concurrency/Error_concurrent_requests_bigger_2.png) Since the scale of the previous graphs is hiding the huge impact, let’s use a logarithmic scale: ![error concurrent calls](/images/documentation/concurrency/Error_concurrent_calls.png) --- # [Service Discovery Integration](https://www.krakend.io/docs/backends/service-discovery/) The service discovery (`sd`) is an optional attribute of the `backend` section that enables KrakenD to detect and locate services automatically on your enterprise network. Related read You might also want to read the [Load Balancer](/docs/throttling/load-balancing/) documentation The chosen **service discovery strategy** determines how to retrieve (statically or dynamically) the final list of IPs, hostnames, or services pointing to your backends. If your host list is dynamic, you can use an external service discovery provider and let KrakenD interact with it to get the hosts. If your host list is static (it doesn’t change) or you use a service name or an external load balancer, you can use `static` resolution and directly use the values provided under `host[]`. KrakenD must be in a network that can reach any declared hosts. With more than one host, KrakenD [load balances](/docs/throttling/load-balancing/) the connections to the hosts in the list. ## Service discovery configuration The possible configurations and values for `sd` are: ##### Fields of `"backend"` \* required fields ##### [`sd`](https://www.krakend.io/docs/backends/service-discovery/#sd "Click to copy link to sd") The [Service Discovery](https://www.krakend.io/docs/backends/service-discovery/) system to resolve your backend services. Defaults to `static` (no external Service Discovery). Use `dns` to use DNS SRV records. Use `dns-shared` to share the DNS resolution between backends using the same hosts. Possible values are: `"static"` , `"dns"` , `"dns-shared"` Defaults to `"static"` ##### [`sd_scheme`](https://www.krakend.io/docs/backends/service-discovery/#sd_scheme "Click to copy link to sd_scheme") string The [Service Discovery](https://www.krakend.io/docs/backends/service-discovery/) scheme to connect to your backend services. Examples: `"http"` , `"https"` Defaults to `"http"` Schema: [https://www.krakend.io/schema/v2.13/backend.json](https://www.krakend.io/schema/v2.13/backend.json "JSON schema definition") ## Static resolution The `static` resolution is the default service discovery strategy. It implies that you write directly in the configuration the protocol plus the service name, hosts, or IPs you want to connect to. The `static` resolution uses a **list** of hosts to **load balance** (in a Round Robin fashion) all servers in the list, and you should expect more or less an equivalent number of hits on each backend. However, if you use a **Kubernetes service**, then it load-balances itself so that you only need one entry. Declaring hosts on Kubernetes When the consumed hosts are behind a balancer or a service name, write a single entry in the array with that name. To use static resolution, you don’t need to declare anything other than the `host` list. However, you can add the `"sd": "static"` property in the backend configuration for a more explicit reading, as it is the default value when `sd` is not declared). Example: ```json { "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`([see RFC](https://datatracker.ietf.org/doc/html/rfc2782)) is a market standard used by systems such as **Kubernetes, Mesos, Haproxy, Nginx plus, AWS ECS, Linkerd**, and many more. An SRV entry is a custom DNS record that establishes 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. ``` Caching The DNS-based service discovery caches entries for 30 seconds by default, use `dns_cache_ttl`to change it. 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 use dynamic host resolution using the service discovery strategy - `"sd_scheme": "https"`: When the list of hosts provided by the service discovery is offered under HTTPS instead of plain HTTP - `"host": []`: And entry with the service name providing the resolution (e.g., Consul address) 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: ```json { "backend": [ { "url_pattern": "/foo", "sd": "dns", "sd_scheme": "https", "host": [ "_https._tcp.my-application.default.svc.cluster.local" ], "disable_host_sanitize": true } ] } ``` With the configuration above, KrakenD will query every 30 seconds (default behavior) the `_https._tcp.my-application.default.svc.cluster.local` DNS and will apply to the internal balancer any weights and priorities returned by the DNS record. ## DNS Cache The results returned by your Service Discovery are cached in memory, so KrakenD does not constantly hammer on each request for the list of hosts. Nevertheless, you can change this time as per your needs, and you must place the following attribute in the root of the configuration for all services in the configuration to do so. Even though this setting is global, each backend keeps a copy of the list returned by the SD and renews it actively in the background every TTL (even if there are no requests). If, for whatever reason, any renewal of the TTL fails (e.g., the SD is down), the last successful list of hosts is used. For unsafe values under one second, the default is used instead. ##### Override DNS cache \* required fields ##### [`dns_cache_ttl`](https://www.krakend.io/docs/backends/service-discovery/#dns_cache_ttl "Click to copy link to dns_cache_ttl") string Sets the maximum time KrakenD can store the results of a query to the configured Service Discovery returning the available hosts list. For values under `1s` this setting is ignored. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30s"` Schema: [https://www.krakend.io/schema/v2.13/\_root.json](https://www.krakend.io/schema/v2.13/_root.json "JSON schema definition") For instance: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "dns_cache_ttl": "10s" } ``` ## How priority and weight affect balancing When the Service Discovery answers with the list of hosts, **only the lower priority is taken**. For instance, if you have a response like this one: SRV response example  ``` $dig _service._tcp.domain.com SRV +short 10 1 8000 service-1.domain.com. 10 2 8000 service-2.domain.com. 20 1 8000 service-3.domain.com. ``` Which can be read as two services with priority `10` and one with `20`, all using port `8000`. With this configuration, KrakenD removes `service-3` (prio `20`) from the balancing since two entries have lower priority (prio `10`). In addition, the weight of `service-1` is `1`, and `service-2` is `2`, so the final list where KrakenD will load-balance is: `["service-1:8000", "service-2:8000", "service-2:8000"]` --- # [Traffic shadowing and mirroring](https://www.krakend.io/docs/backends/shadow-backends/) There are times when you have been working in a new version of your microservice, a complete refactor, a dangerous change, or any other valuable change that needs being careful, and it’s too risky to put it live as there might be issues that impact your end users. The **traffic shadowing** or **traffic mirroring** functionality allows you to **test new backends in production** by sending them copies of the traffic but **ignore their responses**. When you add a backend to any of your endpoints as a **shadow backend**, KrakenD continues to send the requests to all the backends as usual, but the responses from the ones marked as *shadow* are ignored and never returned or merged in the response. Mirroring the traffic to your microservices allows you to test your new backend from the interesting perspective of seeing behavior in production. For instance, you could: - Test the application errors by examining its logs - Test the performance of the application - Stress a new server - Retrieve any other interesting data that you can only see when you have something running in production. To define a backend as a *shadow backend* you only need to add the flag as follows: ```json { "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: ```json { "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 } } } ] } ``` ## Canary testing and Canary Releases Learn how to do [Canary Testing](/blog/krakend-shadow-testing/), or the different [Canary Releases](/blog/canary-releases/) strategies you can use in our blog post. --- # [Conditional requests and responses with CEL](https://www.krakend.io/docs/endpoints/common-expression-language-cel/) There are times when you might want to incorporate **additional logic** to check if the gateway has to **skip the backend call**. For example, maybe the request from the user is undoubtedly wrong, and there is no point in hitting your backend(s). There are other times that you might need to **skip returning the response** because after parsing it you realize that it is not worth showing it to the user, but rather return an error. In both scenarios where you check requests and responses, the **Common Expression Language (CEL)** implements standard semantics for expression evaluation and is a straightforward and powerful option to have complete control during requests and responses. When the CEL component is enabled, you can set any number of expressions to check both requests and responses, either at the endpoint or backend level. **CEL does not transform the data, but it gives you the control of deciding what to do in the next step.** ## How CEL works In any `endpoint`, `backend`, or `async_agent`, you can define a sequence of expressions you’d like to check using [Google’s CEL spec](https://github.com/google/cel-spec) to write the conditions. During runtime, when an expression returns `false`, KrakenD aborts the execution of that layer and **logs an error** like `request aborted by evaluator #0` (the number of CEL expression that failed). When it fails, it does not return the content or it does not perform the request (depending on the type). Otherwise, KrakenD serves the content if all expressions return `true`. The CEL expressions will sound familiar if you are used to languages like javascript, C, C++, or Java to name a few. The expressions need to represent a boolean condition. For instance: ```js '::1' in req_headers['X-Forwarded-For'] ``` This expression checks that the request comes from localhost by checking that the header array `X-Forwarded-For`. In this example `::1` is the loopback address for IPv6 (`127.0.0.1` in IPv4) ## Types of CEL evaluations You can use CEL expressions in five different places: during the **request** and the **response** of both **backends** and **endpoints** (see the blue dots in the image), and prior to the endpoint call when used as a JWT rejecter. The flow is: ![The 5 CEL places of action](/images/documentation/krakend-cel.png) - **JWT** (token) evaluation (**Note:** you must include the `auth/validator`) - **Endpoint request** evaluation - **Backend request** evaluation (per N backends) - **Backend response** evaluation (per N backends) - **Endpoint response** evaluation (can evaluate all merged data) ## 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](/docs/endpoints/sequential-proxy/), you can skip requesting a backend if a previous call didn’t fulfill your criteria. The configuration is as follows: ```json { "extra_config": { "validation/cel": [ { "check_expr": "CONDITION1 && CONDITION2" }, { "check_expr": "CONDITION3 && CONDITION4" } ] } } ``` **Notice that the CEL object is an array**, even when you need a single evaluation object. If **all stacked conditions** in the array are *true*, the request/response continues. As soon as it finds a *false*, the validation fails. Each object in the array has the following syntax: ##### Fields of Object in array \* required fields ##### [`check_expr`](https://www.krakend.io/docs/endpoints/common-expression-language-cel/#check_expr "Click to copy link to check_expr") * string The expression that evaluates as a boolean, you can write here any conditional. If the result of the expression is `true`, the execution continues. See in the docs how to use additional variables to retrieve data from requests, responses, and tokens. Example: `"int(req_params.Id) % 3 == 0"` Schema: [https://www.krakend.io/schema/v2.13/validation/cel.json](https://www.krakend.io/schema/v2.13/validation/cel.json "JSON schema definition") See the sections below to use **additional variables**. A note on client headers When **client headers** are needed, remember to add them under [`input_headers`](/docs/endpoints/parameter-forwarding/#headers-forwarding) 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 when you are inside the `check_expr` 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** (requires `auth/validator` and being in the `endpoint` context, not `backend`) ### 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](/docs/endpoints/sequential-proxy/#chaining-the-requests) 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']`. Notice that no matter how the header is written, you must access it using the **canonical form** (a header `x-SOME-thing` must be accessed as `X-Some-Thing`). - `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 A note on response metadata The response metadata is only filled for no-op pipes. In non no-op cases it will be always empty, and the pipe will end the execution by itself if the status code is not 200/201. ### Variables for the JWT rejecter You can also use CEL expressions during the JWT token validation. It only works if you have a configured `auth/validator` and when the CEL expression is at the `endpoint` level. Use the `JWT` variable to access its metadata. Here’s an example of expression you could use in an `endpoint`: ```js has(JWT.user_id) && has(JWT.enabled_days) && (timestamp(now).getDayOfWeek() in JWT.enabled_days) ``` This example checks that the JWT token contains the metadata `user_id` and `enabled_days` with the macro `has()`, and then checks that today’s weekday is within one of the allowed days to see the endpoint. And the required configuration would be: ```json { "endpoint": "/nick/{nick}", "extra_config": { "validation/cel": [ { "check_expr": "has(JWT.user_id) && has(JWT.enabled_days) && (timestamp(now).getDayOfWeek() in JWT.enabled_days)" } ], "auth/validator": { "alg": "RS256", "jwk_url": "https://example.com/.well-known/jwks.json", "cache": true } } } ``` Notice that the `JWT` variable is **unset** when you evaluate expressions in the `backend`. If you want to check JWT claims in a `backend` context, you must [propagate their values as headers](/docs/authorization/jwt-validation/#propagate-jwt-claims-as-request-headers), and then work with headers. This is an example of accessing claims in a `backend` expression, through propagated claims: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "endpoints": [ { "endpoint": "/example", "input_headers": ["X-User-Id"], "extra_config": { "auth/validator": { "alg": "RS256", "jwk_url": "https://example.com/.well-known/jwks.json", "cache": true, "propagate_claims": [ [ "user_id","X-User-Id" ] ] } }, "backend": [ { "url_pattern": "/example", "host":["https://example.com"], "extra_config": { "validation/cel": [ { "check_expr": "size(req_headers['X-User-Id']) == 1" } ] } } ] } ] } ``` Notice that because we are in the `backend` context, we do not access `JWT` in the expression, but to the propagated header. The target header declared under `propagate_claims` must be also declared under `input_headers` to work properly. ## CEL Syntax and examples See the CEL [language definition](https://github.com/google/cel-spec/blob/master/doc/langdef.md) for the complete list of supported options. The following example snippets demonstrate how to check requests and responses. ### Example: Discard an invalid request before reaching the backend The following example demonstrates how to reject a user request that does not fulfill a specific expression, checking at the endpoint level that when `/nick/{nick}` is called, a constraining format applies. More specifically, the example requires that the parameter `{nick}` matches the expression `k.*`: ```json { "endpoints": [ { "endpoint": "/nick/{nick}", "extra_config": { "validation/cel": [ { "check_expr": "req_params.Nick.matches('k.*')" } ] } } ] } ``` With this configuration, any request to `/nick/kate` or `/nick/kevin` will make it to the backend, while a request to `/nick/ray` will be immediately rejected (`backend` section omitted intentionally for simplification purposes) ### Example: Check if the backend response has a specific field or abort This example can be copied/pasted into a new configuration. The CEL validation happens at the backend level. After querying the backend, the CEL expression checks that a field `company` exists inside the response body. If the user does not have that field, the call to the endpoint will fail: ```json { "version": 3, "endpoints": [ { "endpoint": "/nick/{nick}", "backend": [ { "host": ["https://api.github.com"], "url_pattern": "/users/{nick}", "allow": ["name","company"], "group": "github", "extra_config": { "validation/cel": [ { "check_expr": "'company' in resp_data.github" } ] } } ] } ] } ``` Also, notice how we are accessing a `github` element in the data, a new attribute added by KrakenD thanks to the `group` functionality (it does not exist in the origin API). The takeaway is that the CEL evaluation is applied **after** KrakenD has processed the backend. ### Example: Match a query string parameter with multiple values This example validates that an array query string parameter contains a given value. Many APIs describe array query string parameters with a `[]` suffix to denote that it’s an array to the backend service. CEL syntax can reference these types of parameters. In this case, an API operation accepts an array query string parameter named `foo`. The backend service’s platform requires this passed to the API as `?foo[]=bar&foo[]=baz` in the query string. KrakenD intercepts the parameter as the suffixed `foo[]`, so that’s what must be allowed in your `input_query_strings` list. Since the parameter name contains the `[]` characters then it must be referred to as a map key instead of dot-notation in CEL syntax. The following config uses CEL validation to block requests that do not have `foo[]` defined in the query string or do not have “bar” in the `foo[]` array: ```json { "endpoint": "/example", "input_query_strings": [ "foo[]" ], "backend": [ { "host": ["api.example.com"], "url_pattern": "/example", "extra_config": { "validation/cel": [ { "check_expr": "has(req_querystring['foo[]']) && 'bar' in req_querystring['foo[]']" } ] } } ] } ``` Note: this snippet applies CEL validation to a single backend. Apply CEL validation to an endpoint to validate across all backends. If your application does not require the `[]` suffix in these parameters (clients pass in `?foo=bar&foo=baz` instead of `?foo[]=bar& foo[]=baz`) then omit the `[]` suffix from the parameter name in your `input_query_strings` list and refer to the parameter as `req_querystring.foo` in your `validation/cel` config. ### Example: Time-based access Let’s close the access to the API endpoint during the weekend: ```json { "endpoint": "/weekdays", "extra_config": { "validation/cel": [ { "check_expr": "(timestamp(now).getDayOfWeek() + 6) % 7 <= 4" } ] } } ``` Note: The function `getDayOfWeek()` starts at `0` (Sunday), so the only days with a `mod <=4` are 0 and 6. ### Example: Use custom data from JWT payload Let’s say that the JWT token the user sent contains an attribute named `enabled_days` in its payload. This attribute lists all the integers representing which days the resource can be accessed: ```json { "endpoint": "/combination/{id}", "extra_config": { "validation/cel": [ { "check_expr": "has(JWT.user_id) && has(JWT.enabled_days) && (timestamp(now).getDayOfWeek() in JWT.enabled_days)" } ] } } ``` The expression checks that the JWT token has both the `user_id` and the `enabled_days` and that today is good. ### Example: Conditional call of sequential backends (a.k.a “skip backends”) The following example is a bit more complex, as it **combines the sequential proxy with the CEL component**. You can copy and paste this example and start KrakenD with the `krakend run -d` flag. ```json { "version": 3, "debug_endpoint": true, "host": [ "http://localhost:8080" ], "endpoints": [ { "endpoint": "/cel", "input_query_strings": [ "foo" ], "backend": [ { "url_pattern": "/__debug/0" }, { "url_pattern": "/__debug/1?ignore={resp0_message}", "group": "sequence1", "extra_config": { "validation/cel": [ { "check_expr": "has(req_params.Resp0_message)" } ] } }, { "url_pattern": "/__debug/2", "group": "sequence2", "extra_config": { "validation/cel": [ { "check_expr": "resp_data.sequence2.message == 'pong'" } ] } }, { "url_pattern": "/__debug/3", "group": "sequence3", "extra_config": { "validation/cel": [ { "check_expr": "has(req_querystring.foo)" } ] } }, { "url_pattern": "/__debug/4", "group": "sequence4", "extra_config": { "validation/cel": [ { "check_expr": "has(req_params.NEVER_CALLED_BACKEND)" } ] } } ], "extra_config": { "proxy": { "sequential": true } } } ] } ``` Here is what it does: - The backend 0 (first item in the `backend` list) calls the URL `/__debug/0`. It returns the object `{"message": "pong"}` as per the [debug endpoint](/docs/endpoints/debug-endpoint/) definition. - KrakenD will execute the rest of the backends one by one in the order defined, as the proxy is sequential. - The next backend 1 will call `/__debug/1?ignore=pong`, as `pong` is the value of `resp0_message`. We are using an `ignore` querystring as if you were unable to modify your backend URL, but it could be part of the URL (e.g: `/__debug/1/{resp0_message}`). You must use at least one `resp_` variable to make KrakenD initialize them properly. In addition, as it has a CEL expression inside, this backend will be called **ONLY** if the backend 0 contains a `message` field. Notice that the backend does not have access to the body of the previous call, but it has access to the parameters in the `url_pattern`. Thus, we can use the `req_params` and access any `{parameter}` as `req_params.Resp0_parameter` (all parameters capitalize the first letter: **R**esp0) - The backend 2 will always be triggered but will return the content only when the backend response has a `pong` string in the response. Notice that since we are working with a `group`ed response, the `sequence2` is inside the expression. - The backend 3 will be called only if the original request contains a querystring \` foo' - The backend 4 will never be called, as the endpoint does not define a `{NEVER_CALLED_BACKEND}` parameter The expected response will be incomplete (as 1 or more backends will fail) and looks like: Response  ``` $curl -iG http://localhost:8080/cel\?foo\=A HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 X-Krakend: Version 2.13.8 X-Krakend-Completed: false Date: Tue, 22 Feb 2022 17:26:12 GMT Content-Length: 114 {"message":"pong","sequence-1":{"message":"pong"},"sequence-2":{"message":"pong"},"sequence-3":{"message":"pong"}} ``` --- # [API Throttling and Timeout Management](https://www.krakend.io/docs/throttling/timeouts/) Being KrakenD, an API Gateway that talks to other services, controlling the waiting times for different aspects is crucial. KrakenD will allow you to fine-tune these settings. The timeouts can apply to: - **The duration of the whole pipe** (from user request to user response) - **The HTTP request related timeouts** Additionally, you can control the number of [**maximum IDLE connections**](/docs/service-settings/http-transport-settings/). ## Default timeout The `timeout` key in the `krakend.json` at the root level is used to apply a **default timeout** for those endpoints that do not specify one. The duration you write in the timeout represents the **whole duration of the pipe**, so it counts the time all your backends take to respond and the processing of all the components involved in the endpoint (the request, fetching data, manipulation, etc.). You can see it is an **end-user timeout**, the maximum amount of time a user will wait for a response. ```json { "version": 3, "timeout": "2000ms" } ``` ## Endpoint timeout Probably the most convenient way to work with timeouts is by having a default `timeout` value in the root level with reasonable values. The default timeout will apply to all endpoints not setting a value and then set a `timeout` to those endpoints having a very different nature (like uploading files or heavy processing). To do so, place it inside the desired endpoint: ```json { "version": 3, "timeout": "2000ms", "endpoints": [ { "endpoint": "/default-timeout" }, { "endpoint": "/different-timeout", "timeout": "1s" } ] } ``` The example above will use **1 second** timeout for the `/different-timeout` endpoint and **2 seconds** (expressed in milliseconds) for any the other endpoint. Note that the `backend` section is omitted for better reading. ## Context deadline exceeded What happens when the timeout is reached? KrakenD will exhaust the specified `timeout` trying to fetch and process the content, but if that happens, the user will receive an HTTP status code `500 Internal Server Error` when there is no content to return. If you connected to more than one backend and you have at least a valid response, the gateway will return a `200` status code with a **partial response** (the backends that worked) The logs will show `context deadline exceeded` every time that you hit a timeout. ## Golden rule for setting timeouts When setting timeouts, you always have to respect the following rule: ``` Client timeout > KrakenD timeout > Your API timeout ``` From the consumer to the provider, the timeouts need to decrease. For instance, your javascript application should have a timeout greater than KrakenD timeouts, and KrakenD should have a greater timeout than your backend API. ## HTTP Server and Transport timeouts In addition to the timeouts, you can configure the specific HTTP transport timeouts and the HTTP server timeout. For more information, see: - [HTTP Server settings](/docs/service-settings/http-server-settings/) - [HTTP Transport settings](/docs/service-settings/http-transport-settings/) All the settings in these sections work just like the pipeline timeouts. However, if you place the values in an endpoint, they will override the default setting. --- # [Non-REST Connectivity](https://www.krakend.io/docs/non-rest-connectivity/) KrakenD supports a range of non-REST connectivity options that open the door to integration with message brokers, event-driven systems, and alternative protocols and communication styles. By providing built-in capabilities to interact with services beyond traditional REST APIs, KrakenD enables applications to handle complex, real-time data flows and asynchronous communication patterns. You can connect to non-REST options using a REST interface and mix multiple services at once. For instance, imagine you have a REST endpoint `/subscribe` targeted for a mobile application that inserts payload directly in a RabbitMQ when called. You could even make the gateway react to a new event on a queue (an Async Agent), regardless of the origin, and call a webhook inside your infrastructure with its payload. These are two examples, but through the documentation, you will find a lot more of the non-REST connectivity features: - **RabbitMQ Consumer and Producer**: KrakenD can both consume from and produce to RabbitMQ queues, supporting bidirectional messaging. When configured as a [RabbitMQ Consumer](/docs/backends/amqp-consumer/), KrakenD retrieves messages directly from RabbitMQ queues and processes them according to your routing configuration. As a [RabbitMQ Producer](/docs/backends/amqp-producer/), KrakenD sends messages to specified RabbitMQ queues, enabling it to trigger downstream processes or distribute messages asynchronously across systems. - **Apache Kafka and other Publisher/Subscriber Patterns**: Aside from RabbitMQ, KrakenD supports [Pub/Sub communication with Apache Kafka](/docs/backends/pubsub/), AWS SNS, SQS, Azure Service Bus, GCP PubSub, or NATS.io. This pattern allows KrakenD to broadcast events to multiple systems without tightly coupling them, making it suitable for applications that require distributed, event-driven workflows. - **GraphQL Support**: In addition to REST, KrakenD offers support for [GraphQL](/docs/backends/graphql/), allowing API clients to request precisely the data they need. With GraphQL, KrakenD is an efficient aggregator for complex data queries, providing clients with a flexible, schema-driven approach that can reduce over-fetching and under-fetching data. - **Lambda Function Integration**: KrakenD can [invoke AWS Lambda functions directly](/docs/backends/lambda/), allowing it to act as a bridge between event sources and serverless compute resources. This integration enables applications to process data or execute business logic asynchronously without requiring dedicated infrastructure, a common pattern in serverless and event-driven architectures. With significant volumes, using KrakenD instead of the native AWS API Gateway can remarkably reduce your bill. - **Event-Driven Async Agents**: KrakenD’s [async agents](/docs/async/) facilitate non-blocking, event-driven processing within the gateway. These agents allow KrakenD to handle events autonomously and asynchronously, making it ideal for workflows that require decoupled, reactive responses to changes in data or state. Async agents improve throughput in distributed systems by offloading tasks without waiting for synchronous HTTP responses. In addition, the **Enterprise Edition**, includes the following options: - **gRPC Client and Server**: KrakenD supports both gRPC client and server functionalities, enabling bidirectional gRPC communication. As a [gRPC client](/docs/enterprise/backends/grpc/), KrakenD can make requests to external gRPC services, allowing it to interact with gRPC-based APIs and fetch or send data as needed. When configured as a [gRPC server](/docs/enterprise/grpc/server/), KrakenD can receive and handle incoming gRPC requests, making it a versatile gateway for gRPC-based interactions. You can transparently convert REST to gRPC and vice versa if needed. - **WebSockets**: KrakenD supports [WebSocket communication](/docs/enterprise/websockets/), facilitating real-time, bidirectional data exchange between clients and servers. This capability allows KrakenD to handle continuous data streams and is suitable for applications that require live updates or continuous interaction, such as chat applications, real-time notifications, and IoT device communication. You can use direct WebSockets or multiplexing, which really optimizes the infrastructure. - **SOAP**: KrakenD can interface with [SOAP-based services](/docs/enterprise/backends/soap/), enabling the gateway to connect with legacy systems that rely on the XML-based SOAP protocol. You can choose then to expose the legacy services as JSON REST automatically, and even convert the methods to GET. Consumers of a SOAP legacy service with KrakenD can experience a modern API without knowing that the underlying data transfer is XML. These non-REST connectivity features in KrakenD allow applications to manage complex data flows and event-based interactions beyond the limitations of HTTP, making KrakenD a versatile option for integrating diverse services within modern and legacy architectures. Follow to the documents under this section to find out more about each one. --- # [SOAP Backend Integration (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/soap/) The SOAP integration is the ideal tool for those companies that rely on SOAP services and would like to have an option to **modernize their interface for the end-user**. KrakenD has the native capability of dealing with multiple encodings, like XML or JSON, and transforming from one to the other transparently before returning the content to the user. In addition, the SOAP integration adds the possibility to **craft the body and XML content** you will send to a SOAP service, injecting **dynamic variables** from places like the body sent by the user, headers, query strings, or parameters of the URL. You can afterward **manipulate the responses back** to get the desired output, regardless of the end user’s encoding or method. The SOAP integration introduces **templates** to craft the body sent as an underlying SOAP XML contract to the server, while the end-user doesn’t see any of that. You can decide to keep the SOAP response modified or “as is”, return JSON content, or another [encoding](/docs/endpoints/content-types/). ### Configuration To use the SOAP integration, you need to: - Decide what content type you are going to return to the user (by default JSON) in the `output_encoding`. - Write the SOAP XML request you will send for this endpoint - Inject any variables you need in the template For example, you want to return `json` content (`output_encoding`) and send to SOAP the body (template) on each request: ```xml {{ .req_params.User }} ``` The `{{ .req_params.User }}` is an injected value from the URL. The different configuration options of the SOAP integration are: ##### Fields of SOAP Template modifier \* required fields Minimum configuration needs **one of**: `path` , or `template` ##### [`content_type`](https://www.krakend.io/docs/enterprise/backends/soap/#content_type "Click to copy link to content_type") string The `Content-Type` used in your template, and that will be sent to the SOAP server. This is not the content-type the end-user sent in the request. Examples: `"application/xml"` , `"text/xml"` Defaults to `"text/xml"` ##### [`debug`](https://www.krakend.io/docs/enterprise/backends/soap/#debug "Click to copy link to debug") boolean When `true`, shows useful information in the logs with `DEBUG` level about the input received and the body generated. Do not enable in production. Debug logs are multiline and designed fore developer readibility, not machine processing. Defaults to `false` ##### [`path`](https://www.krakend.io/docs/enterprise/backends/soap/#path "Click to copy link to path") string The path to the Go template file you want to use to craft the body. Example: `"./path/to.xml"` ##### [`template`](https://www.krakend.io/docs/enterprise/backends/soap/#template "Click to copy link to template") string An inline base64 encoded Go template with the body XML content you want to send to the SOAP service. This option is useful if you don’t want to rely on external files and embed the template in the configuration. Schema: [https://www.krakend.io/schema/v2.13/backend/soap.json](https://www.krakend.io/schema/v2.13/backend/soap.json "JSON schema definition") ## Template definition When you write the content of your body, you do it in a template. The template engine parses the content using the [Go text template](https://pkg.go.dev/text/template) language (similar to Helm, Kubernetes, and other systems). You can insert variables with the format `{{ .variable }}` that will replace the placeholder with the corresponding value. The following variables are available in the template you will use to construct a body: ### `.req_body` It contains the data sent by the user in the body request. You can reuse the body of the user sent in several formats, to compose the final body you will send to the SOAP server. The `.req_body` is initially empty unless the following requirements are met: - The template has at least a `.req_body` declaration - The `Content-Type` is declared in the `input_headers` of the endpoint. The content type is necessary to determine how to parse the request body and make it available to the template. This is not the `content_type` configuration option you will send to the SOAP server. The following content types are the only ones that will work (otherwise, there will be an error): - `application/json` - `application/xml` - `text/xml` - `application/x-www-form-urlencoded` - `multipart/form-data` - `text/plain` For instance, a user request like: Term  ``` $curl -XPOST -d '{"foo": "bar"}' -H'Content-Type: application/json' http://localhost:8080/hello ``` Allows you to use in a soap template `.req_body.foo`, which will translate into `bar`. ### `.req_params` It contains all the parameters you have declared in the endpoint as `{placeholders}`. To access the parameters, use the first letter capitalized. For instance, an endpoint defined like this: ```json { "endpoint": "/foo/{bar}" } ``` Allows you to use in a template `.req_params.Bar` and contains the value of the request in `{bar}`. ### `.req_headers` It contains all the headers allowed in the endpoint, not the ones sent by the user. It means that the endpoint needs to declare in `input_headers` each header you would like to access. For instance: ```json { "endpoint": "/foo/{bar}", "input_headers": ["X-Header"] } ``` Allows you to use in a template `.req_headers["X-Header"]`. Notice that we are accessing differently as the header contains `-`, and that is a minus sign on the template. When `input_headers` is set to `["*"]`, all headers sent by the client are in the variable, although this practice might lead to potential security threads and is discouraged. Add only those that you will actually use. ### `.req_querystring` It contains all the query strings allowed to pass in the endpoint. As with headers, the endpoint must declare the list in `input_query_strings`. For instance ```json { "endpoint": "/foo/{bar}", "input_query_strings": ["query","limit"] } ``` Allows you to use in a template `.req_querystring.query` or `.req_querystring.limit`. When `input_query_strings` is set to `["*"]`, then all query strings sent by the client are in the variable, although this practice might lead to potential security threads and is discouiraged. ### `.req_path` The path that KrakenD will use to connect the SOAP server. It matches the `url_pattern` of the configuration. ## SOAP integration example Let’s show how this works with a testable example. We have a public SOAP service that returns information about a country, and when we make the right POST request, it returns the following content: ```xml http://www.oorsprong.org/WebSamples.CountryInfo/Flags/USA.jpg ``` What we want to achieve with this example is that our end-users receive this content as (method GET + JSON + transformation): ```json { "flag_url":"http://www.oorsprong.org/WebSamples.CountryInfo/Flags/USA.jpg" } ``` You can reproduce this test with the following `krakend.json` configuration that is explained below: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "endpoints": [ { "endpoint": "/country/{country}", "method": "GET", "backend": [ { "host": ["http://webservices.oorsprong.org"], "url_pattern": "/websamples.countryinfo/CountryInfoService.wso", "method": "POST", "encoding": "xml", "extra_config": { "backend/soap": { "path": "./soap_flag_request.xml" } }, "target": "Envelope.Body.CountryFlagResponse", "mapping": { "CountryFlagResult": "flag_url" }, "deny": [ "-m" ] } ] } ] } ``` The content of the external `soap_flag_request.xml` file is: ```xml {{ .req_params.Country }} ``` When we start the server with access to these two files, we get beautiful JSON content from a dynamic SOAP request. These are the important takeaways from this configuration: - The `output_encoding` defines how you would like to offer the content to the end user. As the configuration does not specifies it, the default `json` prevails, but we could still return XML or RSS. - The `host` and the `url_pattern` define the SOAP endpoint. - The `method` we are sending to the SOAP service is a `POST`, but we allow a GET to the end-user, e.g., `GET /country/US` - The `path` defines where the external template is, using a relative dir `./` based on KrakenD’s [working directory](/docs/enterprise/configuration/working-directory/), but it can also be an absolute path. Finally, there are a few additional [basic manipulation](/docs/backends/data-manipulation/) options, that strip undesired content: - `target` traverses the response to a specific path and ignores everything else. - `mapping` renames the field to something friendlier - `deny` prevents undesired fields from showing up As you can see, there is a variable `{{ .req_params.Country }}` in the template that the integration replaces by the value of the parameter in the request URL `/country/{country}`. Notice that the first letter in the template is in upper case. To summarize, the host `http://webservices.oorsprong.org` receives a POST request with the crafted body, and all the variables replaced, despite the user sending none. The `target` option tells KrakenD that the starting content is after traversing the XML in the path `Envelope.Body.CountryFlagResponse`, ignoring everything else. After finding the content, we finally renamed `CountryFlagResult` to `flag_url`, and the end-user received the response as we designed. ## Helping your SOAP development While working with the SOAP integration, you might find it useful to set the `debug` flag to true. This flag (that you should not use in production) outputs the following information in the console (when the [debug level](/docs/logging/) is `DEBUG`): - All the variables available in the template - The final generated body, after compiling the template and injecting the variables - The content type to send to the SOAP server For instance: ```log yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [BACKEND: /websamples.countryinfo/CountryInfoService.wso][SOAP] Template variables: { "req_headers": { "Accept": [ "*/*" ], "User-Agent": [ "curl/7.74.0" ], "X-Forwarded-For": [ "127.0.0.1" ], "X-Forwarded-Host": [ "localhost:8080" ], "X-Forwarded-Via": [ "KrakenD Version 2.1.2-ee" ] }, "req_params": { "Country": "DE" }, "req_path": "/websamples.countryinfo/CountryInfoService.wso", "req_querystring": {} } yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [BACKEND: /websamples.countryinfo/CountryInfoService.wso][SOAP] Generated content-type: text/xml yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [BACKEND: /websamples.countryinfo/CountryInfoService.wso][SOAP] Generated body: DE yyyy/mm/dd hh:mm:ss [AccessLog] | 200 | 151.883ms | 127.0.0.1 | GET /country/DE ``` Use the flag for faster development! --- # [WebSockets Integration (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/websockets/) KrakenD Enterprise supports communications using the WebSocket Protocol (RFC-6455) to enable two-way communication between a client and a backend host through the API gateway. This technology aims to provide a mechanism for applications that need two-way communication with servers that do not rely on opening multiple HTTP connections. KrakenD can work with Websockets using two different strategies: - Using [**multiplexing**](/docs/enterprise/websockets/#multiplexing) (default and recommended) - Using [**direct communication**](/docs/enterprise/websockets/#direct-communication) ## Multiplexing When using multiplexing (the default behavior), each end client (e.g., Desktop or mobile device) establishes a connection with the gateway, and KrakenD opens **a single channel with the backend host** to handle all its connected clients. All the communication between the gateway and the backend utilizes a straightforward **message format** that wraps the content with additional information about the origin or destination of the message. For instance, you might have 1000 concurrent users in a chat room (an endpoint `/chat`) with 1000 sockets opened against KrakenD, but KrakenD still communicates with your backend using one single channel. Each message your backend receives contains metainformation about the initiating user and other parameters. The following diagram shows the different WebSockets channels opened: ![diagram of multiplexed websockets](/images/documentation/diagrams/websockets-multiplexing.mmd.svg) ### Message format The message format is the mechanism that the gateway uses to identify who and to whom. The format applies to the **bidirectional communication between KrakenD and the backend** (the clients do not use this format) and is a JSON object with the following fields: - `body`: The content represented in **base64**. - `session`: The session is a filter that determines the message’s sender or receiver. - `url`: The affected KrakenD endpoint. #### KrakenD to backend When the client interacts with KrakenD, the gateway sends messages to the backend wrapped in an envelope with a structure like the following: ```json { "url": "/chat/krakend", "session": { "uuid": "0b251b07-5611-49e5-b69f-cf2cb8d339d6", "Room":"krakend", }, "body": "SGVsbG8gV29ybGQh" } ``` The client typed *“Hello World!”*, but KrakenD delevers to the backend what you can see above, with the contextual metadata for your convenience, so the backend can determine who is doing the call and from which originating endpoint. Essential observations are: - The `body` is **base64** encoded. - The `session` contains information about the client making the request. At least you will always receive an `uuid` randomly assigned by KrakenD to the client when a new session starts. The same `uuid` is kept for the whole session. - If your endpoint contains placeholders (e.g., as in `/chat/{room}`), the placeholder parameters are available under `session`, but using the first letter uppercased. In this example, `Room` holds the value of `{room}`). #### Backend to KrakenD You might need to communicate back with the users connected to the gateway differently: - Send a message to all your clients (**broadcast**) - Send a message only to some users (**multicast**) - Send a message only to one user (**unicast**) You decide which clients get the message by writing the appropiate message. By default, when the backend sends to the gateway unrecognized messages (without format or with an unknown format) they are broadcasted to all connected clients. A controlled response for multicast or unicast communication needs the poper format, using the same format that KrakenD sent to the backend. **The response `body` is mandatory**, and additionally, you can add filters you want to pass. We are talking about broadcasting if you don’t give any filters. The filters are a combination of the `session` and the `url`. If, for instance, you only want to communicate with a specific user, you would produce an answer like this one: ```json { "session": { "uuid": "0b251b07-5611-49e5-b69f-cf2cb8d339d6"}, "body": "SGVsbG8gV29ybGQh" } ``` If you want to communicate with all users connected to an endpoint, then you could use: ```json { "url": "/chat/krakend", "body": "SGVsbG8gV29ybGQh" } ``` And a broadcast: ```json { "body": "SGVsbG8gV29ybGQh" } ``` Notice that when the JSON fields `url` or `session` exist, the `body` is sent to the specific subgroup instead of being broadcasted. ### Handshaking Before you can start using the message format, the gateway makes sure your backend understands the subprotocol. To do that, the gateway sends an opening handshake layered over TCP with a very basic message. The handshake process is straightforward but necessary for KrakenD to determine if the backend server is alive. Returning an ‘OK’ is mandatory The WebSocket server must reply with an `OK` string. KrakenD requires this string to make sure you are aware that a multiplexed connection requires you to deal with an envelope from now on. KrakenD opens a WebSocket connection against the backend server with a fixed JSON message `{"msg":"KrakenD WS proxy starting"}`, and expects to find the `OK` as response: ![Sequence of handshake](/images/documentation/diagrams/websockets-format.seq.mmd.svg) After the successful ping/pong, KrakenD is ready to start serving and communicating with WS. ## Direct communication When you **disable multiplexing** by setting the flag `enable_direct_communication` to `true`, for each connected end client, KrakenD opens a connection to the backend server too. This option is less optimal and increases the load your backend and KrakenD will handle, as the management of all individual threads comes at a cost. It handles **WS binary** transparently. ![diagram of direct websockets](/images/documentation/diagrams/websockets-direct.mmd.svg) When you use direct communication, you lose features like sending one message to multiple clients, and the backend needs to handle broadcast and multicast messages by itself N-times. Direct communication is less efficient than multiplexing The direct communication brings little value at the gateway level, because for each connection from the client, the gateway needs to communicate using another channel with the backend. When using direct communication (as a simple proxy), more connections are handled internally and there is extra CPU, memory, and network use. When you use direct communication, there are no handshake requirements with the backend neither a message format, and you can use `subprotocols` if needed (e.g., to connect to [Streamlit](https://streamlit.io/)) When the gateway fails to deliver the message from a client to the backend because the connection is unavailable, **kicks the user out.** ## Websockets configuration The configuration to enable WebSockets is straightforward; the only requirement is to include the `websocket` namespace at the `endpoint` level, and that you declare at least one backend `host` using the `ws://` or `wss://` schemas. For each `endpoint`, KrakenD will open a single connection against one of the `host`s. The hosts are **load balanced randomly** but the session once is established is kept permamently. The flag `"disable_host_sanitize": true` is also necessary for the backend. Here there is an example (multiplexing): ```json { "endpoint": "/ws/{room}", "input_query_strings": ["*"], "backend": [ { "url_pattern": "/ws", "disable_host_sanitize": true, "host": [ "ws://localhost:8081", "ws://localhost:8082", ] } ], "extra_config": { "websocket": { "input_headers": [ "Cookie", "Authorization" ], "connect_event": true, "disconnect_event": true, "read_buffer_size": 4096, "write_buffer_size": 4096, "message_buffer_size": 4096, "max_message_size": 3200000, "write_wait": "10s", "pong_wait": "60s", "ping_period": "54s", "max_retries": 0, "backoff_strategy": "exponential" } } } ``` All the fields inside `websocket` are optional, allowing you to declare an empty object `"websocket": {}`. The additional options are: ##### Fields of Schema definition for Websockets \* required fields ##### [`backoff_strategy`](https://www.krakend.io/docs/enterprise/websockets/#backoff_strategy "Click to copy link to backoff_strategy") When the connection to your event source gets interrupted for whatever reason, KrakenD keeps trying to reconnect until it succeeds or until it reaches the max\_retries. The backoff strategy defines the delay in seconds in between consecutive failed retries. Defaults to ‘fallback’ Possible values are: `"linear"` , `"linear-jitter"` , `"exponential"` , `"exponential-jitter"` , `"fallback"` Defaults to `"fallback"` ##### [`connect_event`](https://www.krakend.io/docs/enterprise/websockets/#connect_event "Click to copy link to connect_event") boolean Whether to send notification events to the backend or not when a user establishes a new Websockets connection. Defaults to `false` ##### [`disable_otel_metrics`](https://www.krakend.io/docs/enterprise/websockets/#disable_otel_metrics "Click to copy link to disable_otel_metrics") boolean Disables the OpenTelemetry metrics for the websocket connections. Defaults to `false` ##### [`disconnect_event`](https://www.krakend.io/docs/enterprise/websockets/#disconnect_event "Click to copy link to disconnect_event") boolean Whether to send notification events to the backend or not when users disconnect from their Websockets connection. Defaults to `false` ##### [`enable_direct_communication`](https://www.krakend.io/docs/enterprise/websockets/#enable_direct_communication "Click to copy link to enable_direct_communication") boolean When the value is set to `true` the communication is set one to one, and disables multiplexing. One client to KrakenD opens one connection to the backend. This mode of operation is sub-optimal in comparison to multiplexing. Defaults to `false` ##### [`input_headers`](https://www.krakend.io/docs/enterprise/websockets/#input_headers "Click to copy link to input_headers") array of strings Defines which input headers are allowed to pass to the backend. You don’t need to declare the `input_headers` at the endpoint. Use `*` to pass all headers (not recommended, use explicit values instead). There are a few headers that won’t be propagated regardless of your configuration, which are: `Upgrade`, `Connection`, `Sec-Websocket-Extensions`, `Sec-Websocket-Version`, and `Sec-Websocket-Key`. Examples: `["Authorization","User-Agent","Accept"]` , `["*"]` Defaults to `[]` ##### [`max_message_size`](https://www.krakend.io/docs/enterprise/websockets/#max_message_size "Click to copy link to max_message_size") integer Sets the maximum size of messages **in bytes** sent by or returned to the client. Messages larger than this value are discarded by KrakenD and the client disconnected. Defaults to `512` ##### [`max_retries`](https://www.krakend.io/docs/enterprise/websockets/#max_retries "Click to copy link to max_retries") integer The maximum number of times you will allow KrakenD to retry reconnecting to a broken websockets server. When the maximum retries are reached, the gateway gives up the connection for good. Minimum value is `1` retry, or use `<= 0` for unlimited retries. Defaults to `0` ##### [`message_buffer_size`](https://www.krakend.io/docs/enterprise/websockets/#message_buffer_size "Click to copy link to message_buffer_size") integer Sets the maximum number of messages **each end-user** can have in the buffer waiting to be processed. As this is a per-end-user setting, you must forecast how many consumers of KrakenD websockets you will have. The default value may be too high (memory consumption) if you expect thousands of clients consuming simultaneously. Defaults to `256` ##### [`ping_period`](https://www.krakend.io/docs/enterprise/websockets/#ping_period "Click to copy link to ping_period") Sets the time between pings checking the health of the system. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"54s"` ##### [`pong_wait`](https://www.krakend.io/docs/enterprise/websockets/#pong_wait "Click to copy link to pong_wait") Sets the maximum time KrakenD will until the pong times out. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"60s"` ##### [`read_buffer_size`](https://www.krakend.io/docs/enterprise/websockets/#read_buffer_size "Click to copy link to read_buffer_size") integer Connections buffer network input and output to reduce the number of system calls when reading messages. You can set the maximum buffer size for reading in bytes. Defaults to `1024` ##### [`return_error_details`](https://www.krakend.io/docs/enterprise/websockets/#return_error_details "Click to copy link to return_error_details") boolean Provides an error `{'error':'reason here'}` to the client when KrakenD was unable to send the message to the backend. Defaults to `false` ##### [`subprotocols`](https://www.krakend.io/docs/enterprise/websockets/#subprotocols "Click to copy link to subprotocols") array of strings The list of subprotocols that the client can use to connect to the websocket. Example: `["streamlit"]` Defaults to `[]` ##### [`timeout`](https://www.krakend.io/docs/enterprise/websockets/#timeout "Click to copy link to timeout") Sets the read timeout for the backend. After a read has timed out, the websocket connection is terminated and KrakenD will try to reconnect according the `backoff_strategy`. Minimum accepted time is one minute. This flag only applies when you use ’ enable\_direct\_communication\`. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"5m"` ##### [`write_buffer_size`](https://www.krakend.io/docs/enterprise/websockets/#write_buffer_size "Click to copy link to write_buffer_size") integer Connections buffer network input and output to reduce the number of system calls when writing messages. You can set the maximum buffer size for writing in bytes. Defaults to `1024` ##### [`write_wait`](https://www.krakend.io/docs/enterprise/websockets/#write_wait "Click to copy link to write_wait") Sets the maximum time KrakenD will wait until the write times out. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"10s"` Schema: [https://www.krakend.io/schema/v2.13/websocket.json](https://www.krakend.io/schema/v2.13/websocket.json "JSON schema definition") ### Retries and backoff strategies Generally speaking, end-users have the WebSockets server always available in KrakenD regardless of the WebSockets status in the backend server. KrakenD keeps buffering the messages sent by the users, and retrying automatically the connections until it succeeds or it has exhausted the `max_retries`. The `backoff_strategy` setting defines how KrakenD keeps trying to reconnect to the backend until it succeeds or until it reaches the `max_retries`. The backoff strategy defines the delay in seconds in between consecutive failed retries, and defaults to `fallback`. These are the possible strategies you can set: - `linear`: The delay time (`d`) grows linearly after each failed retry (`r`) using the formula `d = r`. E.g., 1st failure retries in 1s, 2nd failure in 2s, 3rd in 3s, and so on. - `linear-jitter`: Similar to `linear` but adds or subtracts a random number: `d = r ± random`. The randomness prevents all agents connected to a mutual service from retrying simultaneously as all have a slightly different delay. The random number never exceeds `±r*0.33` - `exponential`: Multiplicatively increase the time between retries using `d = 2^r`. E.g: `2s`, `4s`, `8s`, `16s`… - `exponential-jitter`: Same as exponential, but adds or subtracts a random number up to 33% of the value using `d = 2^r ± random`. This is the **preferred strategy** when you want to protect the system you are consuming. - **Fallback**: When the strategy is missing or none of the above (e.g.:`fallback`) then it will use **constant** backoff strategy `d=1`. Will retry after one second every time. Independently on the strategy you choose, when you set the `max_retries` value, think that multiplexing and direct communication have different implications. On a **multiplexing scenario**, KrakenD deals with a single connection with the backend. If this connection dies and all the retries exhausted, your WebSocket backend is gone and the KrakenD WebSocker service too (you would need to restart or redeploy when the WS). All attempts to connect to WebSockets will receive a `502 Bad Gateway` status error. An **unlimited retry strategy** *usually* makes sense on this scenario because you generally don’t want to restart KrakenD because the backend server went down for a long period. On a **direct communication** strategy, if a client connects to KrakenD and the connection with the WS server goes down, you usually don’t want more than a few retries before kicking the user. In a scenario like this, you’ll want a small number of retires (but remember that `0` means infinite retries!) ### Understanding WebSockets logs The nature of WebSocket connections is that they have kind of a “state” and use a lasting connection. Therefore, there are implications to be aware of when connectivity issues or downtimes arise. Generally speaking, you can read the different levels of errors as: - `WARNING`: There are connectivity issues with the backend - `ERROR`: There are problems renegotiating the connection - `CRITICAL`: The WebSocket connection is lost for good #### WS Issues during startup The most visible problem of all. If, for whatever reason, the WebSocket on the backend server is not available **during KrakenD startup**, KrakenD starts and keeps retrying the connection until it exhausts the number of configured retries. In such event, the console shows a `CRITICAL` message like this one: ```log ▶ CRITICAL [SERVICE: Websocket] websocket.Dial ws://localhost:8081/ws: dial tcp [::1]:8081: connect: connection refused ``` #### WS Issues during operation If, on the other hand, the handshake succeeded, but at a given point in time, the backend server or the network connection with the WS dies, the affected endpoint becomes non-operational. When the WS connection with the backend is lost you’ll see in the logs: ```log KRAKEND WARNING: [SERVICE: Websocket][Client] Reading from the connection: websocket: close 1006 (abnormal closure): unexpected EOF ``` All clients connected to KrakenD during the downtime of your backend’s WebSocket keep their connection with KrakenD, even though KrakenD cannot pass any data from/to the backend server. This happens both in multiplexing and direct communication. KrakenD will keep retrying broken connections as defined through `max_retries` and `backoff_strategy`, and when the `max_retries` are exhausted all clients receive one response for each message. While KrakenD is disconnected from the backend, the log will show `WARNING` messages when clients demand information from it, for instance: ```log ▶ KRAKEND WARNING: [SERVICE: Websocket][Client] Reading from the connection: EOF ``` Following the `backoff_strategy`, KrakenD will keep trying to fix this problem, but for each failed retry, KrakenD will show an `ERROR` in the log: ```log ▶ KRAKEND ERROR: [SERVICE: Websocket][Client] Unable to renew the connection: websocket.Dial ws://localhost:8888/ws: dial tcp 127.0.0.1:8888: connect: connection refused ``` While the connection with the backend is retrying, all writes remain in queue. If you have set a limited number of `max_retries` (greater than `0`), when KrakenD has exhausted all the retries, KrakenD will stop trying, and KrakenD will forget the WebSocket connection. You can see this state with a `CRITICAL` in the logs. ```log ▶ KRAKEND CRITICAL: [SERVICE: Websocket][Client] Unable to reconnect to the backend: websocket.Dial ws://localhost:8888/ws: dial tcp 127.0.0.1:8888: connect: connection refused ``` In addition, all remaining queued messages will show an error after the critical, as well as new ones: ```log ▶ KRAKEND ERROR: [SERVICE: Websocket] Writing request: empty connection ``` The client will receive an error too: ```json {"error":"empty connection"} ``` At this point, KrakenD stops trying, and you must restart the service. Of course, you can always set `max_retries` to `0` to keep trying indefinitely. Another log you can see is: ```log KRAKEND WARNING: [SERVICE: Websocket][Client] Reading from the connection: websocket: read limit exceeded ``` When you see the log above is because the client or the backend sent a message larger than permitted by the configuration. The offender will receive a `close 1009 (message too big)` followed by a disconnect. #### Example of failing websocket with `max_retries=1` The following is an example log of a websocket that failed and couldn’t reconnect on the single retry we allowed in the configuration (`max_retries=1`) ```log ▶ KRAKEND WARNING: [SERVICE: Websocket][Client] Reading from the connection: EOF ▶ KRAKEND ERROR: [SERVICE: Websocket][Client] Unable to renew the connection: websocket.Dial ws://localhost:8888/ws: dial tcp 127.0.0.1:8888: connect: connection refused ▶ KRAKEND CRITICAL: [SERVICE: Websocket][Client] Unable to reconnect to the backend: websocket.Dial ws://localhost:8888/ws: dial tcp 127.0.0.1:8888: connect: connection refused ▶ KRAKEND ERROR: [SERVICE: Websocket] Writing request: empty connection ``` ## Integrating KrakenD with Socket.IO [Socket.IO](https://socket.io/) is a popular library to use bidirectional communication. Although Socket.IO name might sound as a WebSockets implementation, the reality is that Socket.IO operates on a **custom protocol** layered over WebSockets that is incompatible with plain WebSockets clients using the [WebSockets API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications) (the one native in the JS standard lib). To connect to a Sockets.IO server you cannot use a WebSockets client, you must use a Sockets.IO client. KrakenD uses a pure WebSocket Protocol (RFC-6455) to connect to servers, but the Socket.IO protocol requires specific signaling to establish and maintain connections. By default, it attempts to **use the same endpoint for both HTTP and WebSocket communication**, and the connection details passed on a query string (e.g., `?EIO=4&transport=websocket`). This design can cause confusion when integrating with KrakenD, which manages HTTP and WebSocket traffic separately. Make sure to use `websockets` only when passing through KrakenD. Socket.IO also requires **dedicated connections for each client**. This approach is **incompatible with KrakenD’s multiplexing system**, which optimizes resource usage by sharing WebSocket connections among multiple clients, so you are limited to use **direct WebSockets only**. Needles to say that handling individual client connections, leads to a much higher resource consumption. Integrating KrakenD with Socket.IO can open up powerful real-time communication features, but it comes with trade-offs. The need for dedicated per-client connections, the additional dependency footprint, and challenges in maintaining asynchronous logic and multi-threaded execution must be considered before committing to this setup. In all, if used with KrakenD make sure to: - Set as `url_pattern` the value `/socket.io/?EIO=4&transport=websocket` - Make sure the client uses ONLY the `websocket` transport In the examples repository you will find a running demo: [Socket.IO demo](https://github.com/krakend/examples/tree/main/websockets/socket.io "Socket.IO demo") --- # [Introduction to gRPC and the service catalog (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/grpc/) [gRPC is a protocol](https://grpc.io/docs/what-is-grpc/introduction/) that creates a channel connection between a client and a server and allows calling methods to send and receive payloads serialized with [protocol buffers](https://protobuf.dev/overview/). KrakenD supports **Unary RPCs** requests as a backend but not streaming connections (server, client, or bidirectional streaming), as we don’t see fit in the context of an API Gateway. The gRPC integration **serves a double purpose** (server and client) that you can use separately or together. As a **gRPC client**, it enables KrakenD to **consume content from a gRPC upstream**, independently of how you return it to the end-user, whether you continue doing it as gRPC or transform it into regular REST content. As a **gRPC server**, you can **expose a gRPC service** to your end-users, independently of the data you consume from your upstream services, gRPC or not. You can combine both if needed and introduce other protocols into the mix! ![grpc-server.mmd diagram](/images/documentation/diagrams/grpc-server.mmd.svg) ## gRPC use cases Because KrakenD is much more than a proxy but a powerful transformation machine, you can create a gRPC server out of the blue when you don’t have a backend supporting gRPC. Or you can expose a regular REST API that takes data from a gRPC service, hiding the complexity from the end user. Some of the **use cases** you can enable with this integration are: - Offer a gRPC service to your consumers when your upstream services do not support it yet (gRPC server). - Convert a gRPC upstream into a regular REST API, hiding complexity to consumers (gRPC client). - Enable gRPC to gRPC communication through the gateway (gRPC server + gRPC client) ## Catalog definition Whether you use the client or server gRPC, you should start configuring your integration by creating a list of directories or files containing the **protocol buffer definitions**. We use the `catalog` entry in the configuration to express this list. The `catalog` contains the available services, their exposed endpoints, and the input and output messages. These definitions are written in `.proto` files that are used to generate **client** and **server** code in different languages using the [**Protocol Buffer Compiler**](https://grpc.io/docs/protoc-installation/). Both `proto2` and `proto3` are supported. KrakenD does not directly use `.proto` files but their binary counterpart, the `.pb` files. You can generate the binary `.pb` files with a one-liner using the same Protocol Buffer Compiler you are using today (see below). ### Generating binary protocol buffer files (.pb) You can create `.pb` files with a command like the one below: ```sh protoc --descriptor_set_out=file.pb file.proto ``` Nevertheless, you can use two different approaches to provide the necessary `.pb` information: 1. Create multiple `.pb` files, one for each `.proto` file you have 2. Gather all `.proto` files and create a single `.pb` file that contains all the definitions. ### Multiple `.pb` files example This script assumes that you execute it from the root directory where you have all the `.proto` files you want to collect and places the `.pb` files under a `../defs` directory. ```bash #!/bin/bash DSTDIR='../defs' for pfile in $(find . -name '*.proto') do reldir=$(echo $(dirname $pfile) | sed "s,^\.,$DSTDIR,g") mkdir -p $reldir fname=$(basename $pfile) fname=$(echo "${reldir}/${fname%.proto}.pb") protoc --descriptor_set_out=$fname $pfile done ``` ### A single `.pb` file If you have all your `.proto` files under the same directory, it is easy to create a single `.pb` file that contains all the definitions: ```bash mkdir -p ./defs cd contracts && \ protoc \ --descriptor_set_out=../fullcatalog.pb \ $$(find . -name '*.proto') ``` ### Handling dependencies KrakenD needs to know about each of the services you want to call and their dependencies. If you `import` other definitions in your `.proto` files, the `.pb` file will also need to be used for those imported types. For example, if you have a code like this: ```proto syntax = "proto3"; import "mylib/protobuf/something.proto"; ``` As you import another `.proto`, you must have the `something.pb` binary definition to send or receive that data. Missing definitions will result in data not being filled (it will not fail, but the data will not be there). KrakenD emits **warning logs** for **missing message type definitions**. ### Well-known types In the official [Protocol Buffers](https://github.com/protocolbuffers/protobuf) repository, under `src/google/protobuf` folder, you can find some common message definitions, like `timestamp.proto` or `duration.proto`. If you `include` those types in your message definitions, you might want to collect those to create their binary `.pb` counterparts to be used by KrakenD. This is an example of how to get the `.proto` files for those “well known types” from the `protoc` GitHub repo, assuming you have `./contracts` dir, where you want to store the files (that can be the same place where you store your own `.proto` files): ```bash mkdir -p ./tmp && \ cd ./tmp && \ git clone --depth=1 https://github.com/protocolbuffers/protobuf.git mv ./tmp/protobuf/src/google ./contracts rm -rf ./contracts/google/protobuf/compiler find ./contracts/google -type f | grep -v '\.proto' | xargs rm find ./contracts/google -type f | grep 'unittest' | xargs rm find ./contracts/google -type d -empty -delete rm -rf ./tmp ``` As you can see in the script above, we get rid of all `unittest` proto definitions. **You are advised to create your script to collect from different directories or repositories**. KrakenD internally transforms some well-known types to its JSON representation: - `timestamp.proto` - `duration.proto` The `timestamp` is the most frequently used across all applications. For the rest of the well-known types, the structure remains in the response as it is defined in the protobuf file. For example, an `Any` type is returned as an `URL` and a `bytes` field, but it does not resolve to a new message. ## gRPC client and server configuration When you have the `catalog` defined, it’s time to decide whether you want to expose gRPC to your consumers (gRPC server), or to consume data from gRPC backends (gRPC client). You have the complete freedom to decide the format to consume, and the format to expose, as the transformation is handled automatically. You must place the catalog definition at the service level under the `grpc` extra configuration key, and it requires to contain a list of definitions to load into KrakenD: ##### Fields of `"grpc"` \* required fields ##### [`catalog`](https://www.krakend.io/docs/enterprise/grpc/#catalog "Click to copy link to catalog") * array of strings The paths to the different `.pb` files you want to load, or the paths to directories containing `.pb` files. All content is scanned in the order of the list, and after fetching all files it resolves the dependencies of their imports. The order you use here is not important to resolve imports, but it matters when there are conflicts (different files using the same namespace and package type). Example: `["./grpc/flights.pb","./grpc/definitions","/etc/krakend/grpc"]` Schema: [https://www.krakend.io/schema/v2.13/grpc.json](https://www.krakend.io/schema/v2.13/grpc.json "JSON schema definition") While loading the catalog, if there are conflicts, the log will show messages with a WARNING log level. Continue now to: - [gRPC server](/docs/enterprise/grpc/server/) - [gRPC client](/docs/enterprise/backends/grpc/) --- # [gRPC client and gRPC to REST conversion (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/grpc/) ![](/images/documentation/grpc.png) The gRPC client integration allows KrakenD to connect to upstream services using gRPC. If you haven’t enabled the [gRPC server](/docs/enterprise/grpc/server/) functionality, then the content is automatically transformed to a regular REST API, as shown in the picture. If, on the other hand, you enable a gRPC server, then you can have a gRPC-to-gRPC communication. ## gRPC client configuration To use the gRPC client, you must declare two extra configuration entries in the settings: 1. `grpc`: The catalog with all the protocol buffer definitions, **at the service level**. See the [catalog definition](/docs/enterprise/grpc/#catalog-definition). 2. `backend/grpc`: This is the connection to the specific gRPC services in the catalog at the **backend level**. For example: ```json { "version": 3, "extra_config": { "grpc": { "@comment": "The catalog loads all .pb files passed or contained in directories", "catalog": [ "grpcatalog/flights/fligths.pb", "grpcatalog/known_types", "grpcatalog/third_parties" ] } }, "endpoints": [ { "@comment": "Feature: GRPC", "endpoint": "/flights", "input_query_strings": ["*"], "backend": [ { "host": ["localhost:4242"], "url_pattern": "/flight_finder.Flights/FindFlight", "extra_config": { "backend/grpc": {} } } ] } ] } ``` At the backend level, the inclusion of the `backend/grpc` object **with no content** is enough, although there are more **optional** settings you can add, both for the TLS connection with the gRPC service and how to treat the input parameters or backend response: ##### Fields of gRPC backend connection \* required fields ##### [`client_tls`](https://www.krakend.io/docs/enterprise/backends/grpc/#client_tls "Click to copy link to client_tls") Enables specific TLS connection options when using the gRPC service. Supports all options under [TLS client settings](https://www.krakend.io/docs/service-settings/tls/#client-tls-settings). ##### [`disable_query_params`](https://www.krakend.io/docs/enterprise/backends/grpc/#disable_query_params "Click to copy link to disable_query_params") boolean When `true`, it does not use URL parameters (`{placeholders}` in endpoints) or query strings to fill the gRPC payload to send. If `use_request_body` is not set, or set to `false`, and this option is set to `true`, there will be no input used for the gRPC message to send. That is still a valid option, when we just want to send the message with its default values, or when the input for the gRPC calls is just the [empty message](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/empty.proto). Defaults to `false` ##### [`header_mapping`](https://www.krakend.io/docs/enterprise/backends/grpc/#header_mapping "Click to copy link to header_mapping") object A dictionary that rename the received header (key) to a new header name (value). If the header starts with `grpc` they will be renamed to `in-grpc-*` as the word is reserved. Example: `{"X-Tenant":"customerid"}` Properties of `header_mapping` can use a name matching the pattern `.*`, and their content is an `string` ##### [`idle_conn_disconnect_time`](https://www.krakend.io/docs/enterprise/backends/grpc/#idle_conn_disconnect_time "Click to copy link to idle_conn_disconnect_time") string The maximum time a connection is kept without being used. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"10m"` ##### [`input_assume_bytes`](https://www.krakend.io/docs/enterprise/backends/grpc/#input_assume_bytes "Click to copy link to input_assume_bytes") When set to true, when the backend has to fill a bytes field for a grpc protobuf payload to send, first tries to decode the input data (the one coming from either a json body field, a query param or header string) from base64: if it succeeds it fills the field to send with that binary / bytes data. If the incoming field is not a valid base64 decoded field (the one used in jsonpb), it will fill the binary field with the verbatim conversion of the incoming string to bytes. Defaults to `false` ##### [`input_mapping`](https://www.krakend.io/docs/enterprise/backends/grpc/#input_mapping "Click to copy link to input_mapping") object A dictionary that converts query string parameters and parameters from `{placeholders}` into a different field during the backend request. When passing parameters using `{placeholder}` the parameter capitalizes the first letter, so you receive `Placeholder`. Example: `{"lat":"where.latitude","lon":"where.longitude"}` Properties of `input_mapping` can use a name matching the pattern `.*`, and their content is an `string` ##### [`max_call_recv_msg_size`](https://www.krakend.io/docs/enterprise/backends/grpc/#max_call_recv_msg_size "Click to copy link to max_call_recv_msg_size") number Specifies the maximum size (in bytes) for messages the grpc client is allowed to receive. If the value is unset, or `0`, it uses its default (4MB) Examples: `33554432` , `4194304` , `0` ##### [`output_duration_as_string`](https://www.krakend.io/docs/enterprise/backends/grpc/#output_duration_as_string "Click to copy link to output_duration_as_string") boolean Well-known Duration types (`google.protobuf.Duration`) are returned as a struct containing fields with `seconds` and `nanos` fields (flag set to `false`). Setting this flag to `true` transforms the timestamps into a string representation in seconds. Defaults to `false` ##### [`output_enum_as_string`](https://www.krakend.io/docs/enterprise/backends/grpc/#output_enum_as_string "Click to copy link to output_enum_as_string") boolean Enum types are returned as numeric values (flag set to `false`). Set this flag to `true` to return the string representation of the enum value. For instance, an enum representing allergies, such as `['NUTS', 'MILK', ' SOY', 'WHEAT']` would return a value `SOY` when this flag is `true`, or `2` when `false`. Defaults to `false` ##### [`output_remove_unset_values`](https://www.krakend.io/docs/enterprise/backends/grpc/#output_remove_unset_values "Click to copy link to output_remove_unset_values") boolean This attribute defines what to do when a field that is declared in the definition does not exist in the backend response. When the flag is `true`, any fields in the definition that are not present in the backend response are removed before returning the content to the user. When the flag is `false` missing fields are returned but set with a zeroed-value depending on its type (zero, nil, false, etc). Defaults to `false` ##### [`output_timestamp_as_string`](https://www.krakend.io/docs/enterprise/backends/grpc/#output_timestamp_as_string "Click to copy link to output_timestamp_as_string") boolean Well-known Timestamp types (`google.protobuf.Timestamp`) are returned as a struct containing fields with `seconds` and `nanos` fields (flag set to `false`). Setting this flag to `true` transforms the timestamps into a string representation in RFC3999 format. Defaults to `false` ##### [`read_buffer_size`](https://www.krakend.io/docs/enterprise/backends/grpc/#read_buffer_size "Click to copy link to read_buffer_size") number Specifies the size of the client buffer reading the gRPC communication in bytes. If the value is unset, or `0`, it uses its default (32KB). Use a negative value to disable the buffer, and if you do there won’t be memory pre-allocation to read. To determine the right number, calculate the average size of the responses the gRPC client will receive. Examples: `32768` , `8192` , `-1` ##### [`request_naming_convention`](https://www.krakend.io/docs/enterprise/backends/grpc/#request_naming_convention "Click to copy link to request_naming_convention") Defines the naming convention used to format the request. Applies to query strings and JSON field names. By default, the gateway uses `snake_case` which makes use of the standard `encoding/json` package, while when you choose `camelCase` the `protobuf/encoding` deserialization is used instead. Possible values are: `"camelCase"` , `"snake_case"` Defaults to `"snake_case"` ##### [`response_naming_convention`](https://www.krakend.io/docs/enterprise/backends/grpc/#response_naming_convention "Click to copy link to response_naming_convention") Defines the naming convention used to format the returned data. By default, the gateway uses `snake_case` which makes use of the standard `encoding/json` package, while when you choose `camelCase` the `protobuf/encoding` deserialization is used instead. Possible values are: `"camelCase"` , `"snake_case"` Defaults to `"snake_case"` ##### [`use_alternate_host_on_error`](https://www.krakend.io/docs/enterprise/backends/grpc/#use_alternate_host_on_error "Click to copy link to use_alternate_host_on_error") boolean When `true`, before sending a message to a host, it checks if the connection status is in a “transient failure” or “failure” state and tries to use a different host (from the service discovery or randomly from the list of hosts). If the connection is in a valid state, but an error happens when sending the gRPC message, it also tries to use a different host to retry sending the message. Depending on the host list, the retry attempts may go to the same host initially in a “bad state”. ##### [`use_request_body`](https://www.krakend.io/docs/enterprise/backends/grpc/#use_request_body "Click to copy link to use_request_body") boolean Enables the use of the sent body to fill the gRPC request. Take into account that when you set this flag to `true` a body is expected, and this body is **consumed** in the first backend. If the endpoint that uses this gRPC backend has additional backends (either gRPC or HTTP) that also expect to consume the payload, these requests might fail. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/backend/grpc.json](https://www.krakend.io/schema/v2.13/backend/grpc.json "JSON schema definition") A few important requirements you should have in mind when adding `backend` configurations: - `host` The array of hosts does not have a protocol prefix (no `http` or `https`, just `host:port`). - `url_pattern`: Cannot contain variables and must be the full name of the gRPC service and method call (for example: `"/pizzeria_sample.Pizzeria/ListMenu"`) Ignored attributes (their values or presence is ignored): - `method` - `is_collection` Regarding the `endpoint` configuration, the `output_encoding` you set in the endpoint must be different than `no-op`. The backend response after the gRPC interaction **accepts all the manipulation options** as with REST calls. For instance: ```json { "@comment": "Feature: GRPC", "endpoint": "/flights", "input_query_strings": ["*"], "backend": [ { "host": ["localhost:4242"], "url_pattern": "/flight_finder.Flights/FindFlight", "extra_config": { "backend/grpc": { "client_tls": { "@comment": "Allow untrusted certificates in development stage", "allow_insecure_connections": true } } } } ] } ``` ### Passing user parameters To pass dynamic parameters from the user to your gRPC service, you must use query strings, a body, headers (**sent as metadata**), or `{placeholders}` in the endpoints. When you use query strings, you must always include the necessary `input_query_strings`. You can even pass all the query strings with a wildcard `*` because only those that match the gRPC call will pass. Headers must be listed in the endpoint’s `input_headers` array when you use them. In addition, the field `header_mapping` under the `backend/grpc` section allows you to rename those headers to gRPC metadata. All headers in the gRPC backend will be sent with their original (but lowercased) name or using the provided `header_mapping`. When you use a `{placeholder}` in the URL of the endpoint, the variable name **capitalizes the first letter**. You must refer to a variable `{foo}` as `Foo`. For instance: ```json { "@comment": "Feature: GRPC", "endpoint": "/flights/{date}", "input_query_strings": ["*"], "input_headers": ["*"], "backend": [ { "host": ["localhost:4242"], "url_pattern": "/flight_finder.Flights/FindFlight", "extra_config": { "backend/grpc": { "header_mapping": { "X-Tenant": "customerid" }, "client_tls": { "@comment": "Allow untrusted certificates in development stage", "allow_insecure_connections": true } } } } ] } ``` In addition, you can map the original input query string parameters to other keys using the `input_mapping` property. For instance, the following example takes a request with `?lat=123&lon=456` and sends a request to the gRPC service with a renamed object. The `Date` (notice the first letter is uppercased) is taken from the URL, while the coordinates are taken from the query string: ```json { "@comment": "Feature: GRPC", "endpoint": "/flights/{date}", "input_query_strings": ["*"], "backend": [ { "host": ["localhost:4242"], "url_pattern": "/flight_finder.Flights/FindFlight", "extra_config": { "backend/grpc": { "input_mapping": { "lat": "where.latitude", "lon": "where.longitude", "Date": "when.departure" } } } } ] } ``` The gRPC service receives: ```json { "where": { "latitude": 123, "longitude": 456 }, "when": { "departure": "2023-07-09" } } ``` In addition to query parameters, you can pass a body through the flag `use_request_body`. When both the params/query strings and the body input are enabled, the parameters and query string are set first and the JSON body second. This means the body will overwrite colliding values set by the params/query strings (you pass the same fields in both). Or said otherwise: params/query values will only apply when those values are not set in the body payload. Finally, fields for the GRPC body can also be taken from the headers data (but query string values and URL parameters take precedence if provided over the values found in the headers). #### Passing well-known types When one of the inputs for a well known type of `Timestamp` or `Duration` is a string instead of fields for the object (like seconds and nanoseconds for a `Timestamp`), if valid, we can interpret the string as a RFC3339 time value for `Timestamp`, or a duration string. #### Limitations For child messages (or child objects), the input params are expected to be in dot notation, e.g., `some_field.child.grand_child=10`. It accepts lists (“repeated” in gRPC nomenclature) of basic types with repeated notation: `a=1&a=2&a=3` Or also when the array is inside an object, e.g., `a.b.c=1&a.b.c=2&a.b.c=3` But it cannot fill arrays that contain other arrays or arrays that contain other objects (that could include other arrays) because it is difficult to know where a value should be put. For example, for an object ```json { "a": { "b": [ { "x": [1, 2] }, { "x": [5, 6] } ], "c": "something" } } ``` What should be the output for `a.b.x=1&a.b.x=2`? Should it be: `{"a": {"b": [{"x": [1, 2]}]}}` or `{"a": {"b": [{"x": [1]}, {"x": [2]}]}}`? --- # [gRPC Server (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/grpc/server/) The gRPC server integration allows you to expose an additional gRPC service under the same HTTP port. The gRPC service can consume data from mixed upstream services, whether they are also gRPC, HTTP, Lambdas, queues, or any other supported type. ![Diagram of gRPC server to many](/images/documentation/diagrams/grpc-server.mmd.svg) Due to its nature, when you access the gateway through gRPC, you cannot use HTTP components in the user request (but you can work with the HTTP layer when connecting to your services). With this in mind, HTTP-specific components like [CORS](/docs/enterprise/service-settings/cors/) or [HTTP Security](/docs/enterprise/service-settings/security/) aren’t loaded in this context. ## gRPC server configuration To configure a gRPC server, you must define the services available and which methods you want to offer. The services and methods are defined in the [`catalog` definition](/docs/enterprise/grpc/#catalog-definition), and in the `services` section, you decide which ones to expose and where to connect them. Each method is connected to one or more backends, which do not need to be gRPC. The example below reads Protobuff definitions from the local folder `./grpc/definitions` and decides to expose the method `FindFlight` service that belongs to the `flight_finder.Flights` service: ```json { "version": 3, "extra_config": { "grpc": { "catalog": [ "./grpc/definitions" ], "server": { "services": [ { "name": "flight_finder.Flights", "methods": [ { "name": "FindFlight", "input_headers": [ "*" ], "payload_params": { "page.cursor": "cursor" }, "backend": [ { "host": [ "example.com:4242" ], "url_pattern": "/flight_finder.Flights/FindFlight", "extra_config": { "backend/grpc": { "use_request_body": true } } }, { "method": "GET", "host": [ "http://example.com:8000" ], "url_pattern": "/articles.json?q={cursor}" } ] } ] } ] } } } } ``` As you can see in the example above, this gRPC server fetches content from one HTTP service and one gRPC service (the flight finder). The upstream gRPC service uses a [gRPC Client configuration](/docs/enterprise/backends/grpc/). The accepted parameters are as follows: ##### Fields of gRPC server \* required fields ##### [`catalog`](https://www.krakend.io/docs/enterprise/grpc/server/#catalog "Click to copy link to catalog") * array of strings The paths to the different `.pb` files you want to load, or the paths to directories containing `.pb` files. All content is scanned in the order of the list, and after fetching all files it resolves the dependencies of their imports. The order you use here is not important to resolve imports, but it matters when there are conflicts (different files using the same namespace and package type). Example: `["./grpc/flights.pb","./grpc/definitions","/etc/krakend/grpc"]` ##### [`server`](https://www.krakend.io/docs/enterprise/grpc/server/#server "Click to copy link to server") object Defines the gRPC server properties. ##### [`opentelemetry`](#server-opentelemetry "Click to copy link to opentelemetry") object Overrides [OpenTelemetry settings](https://www.krakend.io/docs/enterprise/telemetry/opentelemetry-layers-metrics/) for the gRPC server. ##### [`disable_metrics`](#opentelemetry-disable_metrics "Click to copy link to disable_metrics") boolean Whether you want to disable all metrics happening in the gRPC server. Defaults to `false` ##### [`disable_traces`](#opentelemetry-disable_traces "Click to copy link to disable_traces") boolean Whether you want to disable all traces happening in the gRPC server. Defaults to `false` ##### [`services`](#server-services "Click to copy link to services") array of objects Defines one object per available gRPC service. Each item of services accepts the following properties: ##### [`methods`](#services-methods "Click to copy link to methods") array of objects Show properties The gRPC methods available for this service. The `methods` entry is the gRPC equivalent of the HTTP `endpoints`, meaning that the `backend` object under `methods` can use the same configuration as HTTP, including all its `extra_config` parameters. Each item of methods accepts the following properties: ##### [`backend`](#methods-backend "Click to copy link to backend") array of objects An array with all the [backend objects](https://www.krakend.io/docs/backends/) mapped to this method ##### [`extra_config`](#methods-extra_config "Click to copy link to extra_config") object Extra configuration for this method ##### [`auth/validator`](#extra_config-auth%2fvalidator "Click to copy link to auth/validator") object The JWT validation for this method. The configuration underneath is exactly as in the [JWT validator](https://www.krakend.io/docs/enterprise/authorization/jwt-validation/) ##### [`input_headers`](#methods-input_headers "Click to copy link to input_headers") array of strings Defines the list of all client headers that you can use as gRPC metadata. By default, KrakenD won’t pass any header from the client to the backend. This list is **case-insensitive**. You can declare headers in lowercase, uppercase, or mixed. An entry `["X-Something"]` forwards a single `X-Something` header to the backend, ignoring everything else. A single star element `["*"]` as value forwards everything to the backend (**it’s safer to avoid this option**). Examples: `["X-Custom-Trace"]` , `["*"]` Defaults to `[]` ##### [`name`](#methods-name "Click to copy link to name") string The name of the published gRPC method. Example: `"FindFlight"` ##### [`payload_params`](#methods-payload_params "Click to copy link to payload_params") object Maps a property of the gRPC incoming payload to a `{parameter}` that you can inject and reuse in a `url_pattern`. It supports dot nation to access nested objects. Example: `{"some.grpc.object":"param1"}` ##### [`name`](#services-name "Click to copy link to name") string The name of the published gRPC service. Example: `"flight_finder.Flights"` Schema: [https://www.krakend.io/schema/v2.13/grpc.json](https://www.krakend.io/schema/v2.13/grpc.json "JSON schema definition") ## gRPC server authorization If your gRPC server requires authenticated users using JWT, then you need to add the information to the gRPC server under the `extra_config` of the method, exactly as you do for [JWT validation in endpoints](/docs/enterprise/authorization/jwt-validation/). Here is an example, notide the placement of the `auth/validator` namespace: ```json { "version": 3, "extra_config": { "grpc": { "catalog": [ "./grpc/definitions" ], "server": { "services": [ { "name": "flight_finder.Flights", "methods": [ { "name": "FindFlight", "input_headers": [ "*" ], "extra_config": { "auth/validator": { "alg": "RS256", "audience": [ "http://api.example.com" ], "roles_key": "http://api.example.com/custom/roles", "roles": [ "user", "admin" ], "jwk_url": "https://identity.example.com/.well-known/jwks.json", "cache": true } }, "payload_params": { "page.cursor": "cursor" }, "backend": [ { "host": [ "example.com:4242" ], "url_pattern": "/flight_finder.Flights/FindFlight", "extra_config": { "backend/grpc": { "use_request_body": true } } } ] } ] } ] } } } } ``` ## gRPC server introspection Reflection is a protocol that allows gRPC servers to expose their Protobuf-defined APIs over a standardized RPC service and is available automatically when using the gRPC server. While Protobuf accelerates communication, it complicates debugging or manual server interaction. For instance, without reflection (before v2.8), users would need to: - Understand the server’s exposed RPC services. - Decode the Protobuf definitions of request and response messages, including all nested types. - Craft binary request payloads manually and decode responses. Now, with the introspection, you can query the gRPC server to understand the available services and their definitions, and it simplifies the debugging process because popular tools use reflection to interact with servers dynamically, making it easier to test and troubleshoot APIs. You don’t need to set anything in the configuration to enable reflection. Read the [gRPC reflection documentation](https://grpc.io/docs/guides/reflection/) --- # [HTTP Streaming and Server-Sent Events (SSE) (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/streaming/) KrakenD supports HTTP streaming to enable real-time data delivery between servers and clients through persistent HTTP connections. This page covers how to implement generic **HTTP streaming** and one of its subtypes: **Server-Sent Events (SSE)**. HTTP streaming is a technique where **a server sends data chunks to the client as they become available**. To do that, unlike regular REST endpoints that close the connection after delivering the payload, the streaming connection between the client and server remains open for as long as the session makes sense. Streaming is usually applied to real-time logs, video (streaming), continuous data feeds, multipart responses (such as progressive image loading), LLM interaction, and other long-polling style APIs, to name a few examples. KrakenD support for streaming is based on the principle that you won’t need to manipulate the content of the different data chunks returned by the server. No manipulation available Because of the nature of streaming, KrakenD **will act as a proxy only** and won’t be compatible with any components that perform response manipulation. While you can add validations such as authorization, IP validation, and similar components, response payloads are not available to transformation or inspection. ## HTTP Streaming configuration Streaming endpoints are declared as any other endpoint and do not require an additional namespace or configuration to function. At the configuration level, there are basically three things you need to take into account when writing the `endpoint` configuration: 1. `output_encoding`: Endpoints [must use `no-op`](/docs/endpoints/no-op/) (proxy only - no merging of responses possible) 2. `timeout`: The timeout you add in the endpoint must cover enough time to maintain the persistent connection between the client and backend alive without closing prematurely. 3. `input_headers`: If the server requires special headers, let them pass, or statically set them using a [Martian modifier](/docs/backends/martian/) That would be the important stuff to keep in mind in terms of configuration. The following example is for an endpoint that **streams videos** of a short duration (timeout set to max. 5 minutes) and serves it to the client as chunks become available: ```json { "$schema": "https://www.krakend.io/schema/krakend.json", "version": 3, "endpoints": [ { "timeout": "5m", "endpoint": "/video/{id}", "method": "GET", "output_encoding": "no-op", "input_headers": [ "Content-Type" ], "backend": [ { "url_pattern": "/video/{id}.mkv", "encoding": "no-op", "host": [ "https://videos.example.com/" ] } ] } ] } ``` When you add streaming to your API, always **set the long timeout inside the streaming endpoint**. Never add long-pooled timeouts at the service level, affecting all endpoints. ## Server-Sent Events (SSE) configuration ![streaming-sse.mmd diagram](/images/documentation/diagrams/streaming-sse.mmd.svg) SSE is a protocol that allows servers to push real-time updates to browsers or clients over a single HTTP connection, using a lightweight, text-based stream. SSE is designed to continuously send updates like notifications, live feeds, or real-time metrics without the overhead of [WebSockets](/docs/enterprise/websockets/) complexity. Unlike WebSockets, SSE uses simple HTTP and is easier to implement for unidirectional real-time data flow. From a configuration perspective, SSE is not different than the generic HTTP Streaming configuration defined above, as it is a subtype of it. More specifically, as SSE formats the content as `text/event-stream`, you will probably need to pass the `Content-Type` to your server to make it work (whether in `input_headers` or setting it using [Martian](/docs/backends/martian/)) and also tune the `timeout` with the time you consider is necessary for the majority of your SSE sessions. Here’s an example consuming SSE from a local weather service: ```json { "$schema": "https://krakend.io/schema/krakend.json", "version": 3, "endpoints": [ { "endpoint": "/weather-stream", "input_headers": [ "Content-Type" ], "timeout": "300s", "method": "POST", "output_encoding": "no-op", "backend": [ { "host": ["https://weather.example.com"], "url_pattern": "/api/agents/weatherAgent/stream", "encoding": "no-op" } ] } ] } ``` With the approach above, KrakenD handles SSE streams by transparently proxying them with `no-op`, and: - It keeps the HTTP connection open between the client and the backend SSE server. - It forwards event messages as-is without modifying or buffering them. - Manipulation, such as filtering or enrichment, is not supported. Another typical use case for SSE is the consumption of **AI generative models**. For example, the following is an example connecting to **Gemini using SSE**, as you can see, it has no difference with the weather service above: ```json { "$schema": "https://www.krakend.io/schema/krakend.json", "version": 3, "name": "KrakenD AI SSE support", "timeout": "60s", "endpoints": [ { "endpoint": "/stream", "method": "POST", "output_encoding": "no-op", "input_headers": [ "Content-Type" ], "backend": [ { "url_pattern": "/v1beta/models/gemini-2.0-flash:streamGenerateContent?alt=sse&key=xxx", "method": "POST", "encoding": "no-op", "host": [ "https://generativelanguage.googleapis.com/" ] } ] } ] } ``` Notes on the example configuration: - `endpoint`: Represents the public-facing path where clients connect to receive SSE. - `method`: Usually POST for SSE requests, although this varies per implementation. - `backend[].url_pattern`: Proxy route on the backend SSE server for event streaming. - `backend[].host`: The HTTP server serving SSE events. ## Operational considerations When implementing streaming, the following considerations are worth noticing: - Connection persistence: Streaming connections are long-lived. Ensure your KrakenD deployment and underlying infrastructure support stable, persistent HTTP connections. Do not unnecessarily set extremely high timeouts, as this can have unintended side effects. - No payload processing: KrakenD does not alter or buffer streaming data in this mode, so backends are responsible for generating valid and properly formatted streams. - Scalability: Streaming connections consume resources continuously and keep the server busy at all times. - Timeouts: Configure appropriate timeouts on clients, KrakenD, and backend services to avoid premature disconnections. - Redeploys: If you have long sessions, a redeployment might wait a long time to drain connections. In that case, remember you can kill sessions once the draining has waited for a configured number of seconds using [`max_shutdown_wait_time`](/docs/service-settings/http-server-settings/#max_shutdown_wait_time) - Lost connections: if for whatever reason, the connection between the client and the server is lost, KrakenD does not have a mechanism to reach the client and reconnect automatically. Clients must deal this reconnection. --- # [AMQP Consumer Integration in the API Gateway (RabbitMQ)](https://www.krakend.io/docs/backends/amqp-consumer/) The AMQP component allows to **send and receive messages to and from a queue** through the API Gateway. The configuration of the queue is a straightforward process. To connect the endpoints to the messaging system you only need to include the `extra_config` key with the namespaces `backend/amqp/consumer` or `backend/amqp/producer`. To create Async agents that consume messages asynchronously and without requiring a user request, see [Async Agents](/docs/async/). The parameters of this integration follow the AMQP specification. To understand what are the implications of a certain parameter, see the [**AMQP Complete Reference Guide**](https://www.rabbitmq.com/amqp-0-9-1-reference.html). **KrakenD creates both the exchange and the queue for you**. ## 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](/docs/async/) to consume messages without an endpoint. The needed configuration to run a consumer is: ```json { "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, "auto_ack": false, "backoff_strategy": "exponential-jitter" } } }] } ``` ##### Fields of AMQP Consumer \* required fields ##### [`auto_ack`](https://www.krakend.io/docs/backends/amqp-consumer/#auto_ack "Click to copy link to auto_ack") boolean When KrakenD retrieves the messages, regardless of the success or failure of the operation, it marks them as `ACK`nowledge. Defaults to `false` ##### [`backoff_strategy`](https://www.krakend.io/docs/backends/amqp-consumer/#backoff_strategy "Click to copy link to backoff_strategy") When the connection to your event source gets interrupted for whatever reason, KrakenD keeps trying to reconnect until it succeeds or until it reaches the `max_retries`. The backoff strategy defines the delay in seconds in between consecutive failed retries. [Check the meaning of each strategy](https://www.krakend.io/docs/async/#backoff-strategies). Possible values are: `"linear"` , `"linear-jitter"` , `"exponential"` , `"exponential-jitter"` , `"fallback"` Defaults to `"fallback"` ##### [`delete`](https://www.krakend.io/docs/backends/amqp-consumer/#delete "Click to copy link to delete") boolean When `true`, AMQP deletes the queue when there are no remaining connections. This option is **not recommended** in most of the scenarios. If for instance, the connectivity between KrakenD and AMQP is lost for whatever reason and it’s the only client, AMQP will delete the queue no matter the number of messages there are inside, and when KrakenD gets the connection again the queue won’t exist and future connections will recreate it again. Defaults to `false` ##### [`durable`](https://www.krakend.io/docs/backends/amqp-consumer/#durable "Click to copy link to durable") boolean Durable queues will survive server restarts and remain when there are no remaining consumers or bindings. `true` is recommended, but depends on the use case. Defaults to `false` ##### [`exchange`](https://www.krakend.io/docs/backends/amqp-consumer/#exchange "Click to copy link to exchange") * string The exchange name (must have a **topic** type if already exists). Example: `"some-exchange"` ##### [`exclusive`](https://www.krakend.io/docs/backends/amqp-consumer/#exclusive "Click to copy link to exclusive") boolean When `true`, AMQP will allow **a single KrakenD instance** to access the queue. This option is **not recommended** in environments where the gateway needs high availability and you have several instances running. Defaults to `false` ##### [`max_retries`](https://www.krakend.io/docs/backends/amqp-consumer/#max_retries "Click to copy link to max_retries") integer The maximum number of times you will allow KrakenD to retry reconnecting to a broken messaging system. During startup KrakenD will wait for a maximum of 3 retries before starting to use this policy. Use 0 for unlimited retries. Defaults to `0` ##### [`nack_discard`](https://www.krakend.io/docs/backends/amqp-consumer/#nack_discard "Click to copy link to nack_discard") boolean When `true`, messages that cannot be processed are discarded instead of being sent back to the queue. This is useful for scenarios where you want to avoid reprocessing failed messages. Defaults to `false` ##### [`name`](https://www.krakend.io/docs/backends/amqp-consumer/#name "Click to copy link to name") * string Queue name. ##### [`no_local`](https://www.krakend.io/docs/backends/amqp-consumer/#no_local "Click to copy link to no_local") boolean The no\_local flag is not supported by RabbitMQ. ##### [`no_wait`](https://www.krakend.io/docs/backends/amqp-consumer/#no_wait "Click to copy link to no_wait") boolean 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`](https://www.krakend.io/docs/backends/amqp-consumer/#prefetch_count "Click to copy link to prefetch_count") integer The number of messages you want to prefetch prior to consume them. Defaults to `0` ##### [`routing_key`](https://www.krakend.io/docs/backends/amqp-consumer/#routing_key "Click to copy link to routing_key") * array of strings The list of routing keys you will use to consume messages. Example: `["#"]` Schema: [https://www.krakend.io/schema/v2.13/backend/amqp/consumer.json](https://www.krakend.io/schema/v2.13/backend/amqp/consumer.json "JSON schema definition") ## Connection retries The gateway will try to reconnect to the AMQP server if the connection is lost for any reason. You can set the [back-off strategy](/docs/async/#backoff-strategies) that better fits your needs. --- # [AMQP Producer Integration in the API Gateway (RabbitMQ)](https://www.krakend.io/docs/backends/amqp-producer/) The AMQP producer component allows to **send messages to a queue** through the API Gateway. The configuration of the queue is a straightforward process. To connect the endpoints to the messaging system you only need to include the `extra_config` key with the namespace `backend/amqp/producer`. The parameters of this integration follow the AMQP specification. To understand what are the implications of a certain parameter, see the [**AMQP Complete Reference Guide**](https://www.rabbitmq.com/amqp-0-9-1-reference.html). **KrakenD creates both the exchange and the queue for you**. ## Producer Configuration ```json { "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": "#", "mandatory": false, "immediate": false, "backoff_strategy": "exponential-jitter" } } }] } ``` ##### Fields of AMQP Producer \* required fields ##### [`backoff_strategy`](https://www.krakend.io/docs/backends/amqp-producer/#backoff_strategy "Click to copy link to backoff_strategy") When the connection to your event source gets interrupted for whatever reason, KrakenD keeps trying to reconnect until it succeeds or until it reaches the `max_retries`. The backoff strategy defines the delay in seconds in between consecutive failed retries. [Check the meaning of each strategy](https://www.krakend.io/docs/async/#backoff-strategies). Possible values are: `"linear"` , `"linear-jitter"` , `"exponential"` , `"exponential-jitter"` , `"fallback"` Defaults to `"fallback"` ##### [`delete`](https://www.krakend.io/docs/backends/amqp-producer/#delete "Click to copy link to delete") boolean When `true`, AMQP deletes the queue when there are no remaining connections. This option is **not recommended** in most of the scenarios. If for instance, the connectivity between KrakenD and AMQP is lost for whatever reason and it’s the only client, AMQP will delete the queue no matter the number of messages there are inside, and when KrakenD gets the connection again the queue won’t exist and future connections will recreate it again. Defaults to `false` ##### [`durable`](https://www.krakend.io/docs/backends/amqp-producer/#durable "Click to copy link to durable") boolean 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. Defaults to `false` ##### [`exchange`](https://www.krakend.io/docs/backends/amqp-producer/#exchange "Click to copy link to exchange") * string The exchange name (must have a topic type if already exists). Example: `"some-exchange"` ##### [`exclusive`](https://www.krakend.io/docs/backends/amqp-producer/#exclusive "Click to copy link to exclusive") boolean When `true`, AMQP will allow **a single KrakenD instance** to access the queue. This option is **not recommended** in environments where the gateway needs high availability and you have several instances running. Defaults to `false` ##### [`exp_key`](https://www.krakend.io/docs/backends/amqp-producer/#exp_key "Click to copy link to exp_key") string Take a parameter from a `{placeholder}` in the endpoint definition to use as the expiration key. The key must have the first letter uppercased. For instance, when an endpoint parameter is defined as `{id}`, you must write `Id`. Defaults to `""` ##### [`immediate`](https://www.krakend.io/docs/backends/amqp-producer/#immediate "Click to copy link to immediate") boolean A consumer must be connected to the queue when true. Defaults to `false` ##### [`mandatory`](https://www.krakend.io/docs/backends/amqp-producer/#mandatory "Click to copy link to mandatory") boolean The exchange must have at least one queue bound when true. Defaults to `false` ##### [`max_retries`](https://www.krakend.io/docs/backends/amqp-producer/#max_retries "Click to copy link to max_retries") integer The maximum number of times you will allow KrakenD to retry reconnecting to a broken messaging system. During startup KrakenD will wait for a maximum of 3 retries before starting to use this policy. Use 0 for unlimited retries. Defaults to `0` ##### [`msg_id_key`](https://www.krakend.io/docs/backends/amqp-producer/#msg_id_key "Click to copy link to msg_id_key") string Take a parameter from a `{placeholder}` in the endpoint definition to use as the message identifier. The key must have the first letter uppercased. For instance, when an endpoint parameter is defined as `{id}`, you must write `Id`. Defaults to `""` ##### [`name`](https://www.krakend.io/docs/backends/amqp-producer/#name "Click to copy link to name") * string Queue name. ##### [`no_local`](https://www.krakend.io/docs/backends/amqp-producer/#no_local "Click to copy link to no_local") boolean The no\_local flag is not supported by RabbitMQ. ##### [`no_wait`](https://www.krakend.io/docs/backends/amqp-producer/#no_wait "Click to copy link to no_wait") boolean 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. ##### [`priority_key`](https://www.krakend.io/docs/backends/amqp-producer/#priority_key "Click to copy link to priority_key") string Take a parameter from a `{placeholder}` in the endpoint definition to use as the reply key. The key must have the first letter uppercased. For instance, when an endpoint parameter is defined as `{id}`, you must write `Id`. Defaults to `""` ##### [`reply_to_key`](https://www.krakend.io/docs/backends/amqp-producer/#reply_to_key "Click to copy link to reply_to_key") string Take a parameter from a `{placeholder}` in the endpoint definition to use as the reply key. The key must have the first letter uppercased. For instance, when an endpoint parameter is defined as `{id}`, you must write `Id`. Defaults to `""` ##### [`routing_key`](https://www.krakend.io/docs/backends/amqp-producer/#routing_key "Click to copy link to routing_key") * string The routing key you will use to send messages, case sensitive. Defaults to `"#"` ##### [`static_routing_key`](https://www.krakend.io/docs/backends/amqp-producer/#static_routing_key "Click to copy link to static_routing_key") boolean Defines whether the `routing_key` will have a static value or not, instead of taking the value from a parameter. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/backend/amqp/producer.json](https://www.krakend.io/schema/v2.13/backend/amqp/producer.json "JSON schema definition") 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**. Parameters’ first character uppercased Notice the **capitalization** of the first letter of the parameter names at the configuration below used in `exp_key`, `reply_to_key`, `msg_id_key`, `priority_key`, and `routing_key`. 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: ```json { "backend/amqp/producer": { "@comment": "Rest of the settings omitted", "exp_key":"A", "reply_to_key":"B", "msg_id_key":"Id", "priority_key":"Prio", "routing_key":"Route" } } ``` ## Connection retries The gateway will try to reconnect to the AMQP server if the connection is lost for any reason. You can set the [back-off strategy](/docs/async/#backoff-strategies) that better fits your needs. --- # [Publisher/Subscribe with Kafka, NATS and cloud systems](https://www.krakend.io/docs/backends/pubsub/) You 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: - NATS.io - RabbitMQ - Apache Kafka Cloud-specific: - AWS SNS (Simple Notification Service) - AWS SQS (Simple Queueing Service) - Azure Service Bus Topics and Subscriptions - GCP PubSub ## 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. The `url_pattern` is a mandatory field according to its Schema, but it is not used and you can type any value in it: For a **subscriber**: ```json { "host": ["schema://"], "url_pattern": "/ignored", "disable_host_sanitize": true, "extra_config": { "backend/pubsub/subscriber": { "subscription_url": "url" } } } ``` ##### Fields of Pubsub subscriber \* required fields ##### [`subscription_url`](https://www.krakend.io/docs/backends/pubsub/#subscription_url "Click to copy link to subscription_url") * string Subscription URL according to the selected driver Schema: [https://www.krakend.io/schema/v2.13/backend/pubsub/subscriber.json](https://www.krakend.io/schema/v2.13/backend/pubsub/subscriber.json "JSON schema definition") For a **publisher**: ```json { "host": ["schema://"], "url_pattern": "/ignored", "disable_host_sanitize": true, "extra_config": { "backend/pubsub/publisher": { "topic_url": "url" } } } ``` ##### Fields of Pubsub publisher \* required fields ##### [`topic_url`](https://www.krakend.io/docs/backends/pubsub/#topic_url "Click to copy link to topic_url") * string Topic URL according to the selected driver Schema: [https://www.krakend.io/schema/v2.13/backend/pubsub/publisher.json](https://www.krakend.io/schema/v2.13/backend/pubsub/publisher.json "JSON schema definition") See the specification of each individual technology. **Example** (RabbitMQ): Set the envvar `RABBIT_SERVER_URL='guest:guest@localhost:5672'` and add in the configuration: ```json { "host": ["amqp://"], "url_pattern": "/ignored", "disable_host_sanitize": true, "extra_config": { "backend/pubsub/subscriber": { "subscription_url": "myexchange" } } } ``` ## Supported PubSub Drivers ### GCP PubSub [Google’s Cloud Pub/Sub](https://cloud.google.com/pubsub/) 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](https://cloud.google.com/docs/authentication/production/). Example: ```json { "host": ["gcppubsub://"], "url_pattern": "/ignored", "disable_host_sanitize": true, "extra_config": { "backend/pubsub/subscriber": { "subscription_url": "projects/myproject/subscriptions/mysub" } } } ``` ### NATS [NATS.io](https://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: ```json { "host": ["nats://"], "url_pattern": "/ignored", "disable_host_sanitize": true, "extra_config": { "backend/pubsub/subscriber": { "subscription_url": "mysubject" } } } ``` ### AWS SNS [Amazon Simple Notification Service (SNS)](https://aws.amazon.com/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: ```json { "host": ["awssns:///arn:aws:sns:us-east-2:123456789012:mytopic"], "url_pattern": "/ignored", "disable_host_sanitize": true, "extra_config": { "backend/pubsub/subscriber": { "subscription_url": "?region=us-east-2" } } } ``` ### AWS SQS [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/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` ```json { "host": ["awssqs://sqs.us-east-2.amazonaws.com/123456789012"], "url_pattern": "/ignored", "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](https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-queues-topics-subscriptions) 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: ```json { "host": ["azuresb://"], "url_pattern": "/ignored", "disable_host_sanitize": true, "extra_config": { "backend/pubsub/subscriber": { "subscription_url": "mytopic" } } } ``` ### RabbitMQ [RabbitMQ](https://www.rabbitmq.com/) is one of the most popular open source message brokers. Rabbit can alternatively be configured using the [AMQP component](/docs/backends/amqp-consumer/). Configuration: - `host`: `rabbit://` - Environment variable: `RABBIT_SERVER_URL` - `url` for topics: `myexchange` - `url` for subscriptions: `myqueue` No query parameters are supported. Example: ```json { "host": ["rabbit://"], "url_pattern": "/ignored", "disable_host_sanitize": true, "extra_config": { "backend/pubsub/subscriber": { "subscription_url": "myexchange" } } } ``` ### Kafka [Apache Kafka](https://kafka.apache.org/) 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**: ```json { "host": ["kafka://"], "url_pattern": "/ignored", "disable_host_sanitize": true, "extra_config": { "backend/pubsub/subscriber": { "subscription_url": "group?topic=mytopic" } } } ``` **Kafka topics**: ```json { "host": ["kafka://"], "url_pattern": "/ignored", "disable_host_sanitize": true, "extra_config": { "backend/pubsub/publisher": { "topic_url": "mytopic" } } } ``` --- # [Kafka PubSub with Extended Connectivity (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/) **Kafka PubSub** enables KrakenD to publish messages to and consume messages from Kafka topics directly from backend configurations. Use this component when you need to secure your Kafka connections with mutual TLS (mTLS) or SASL authentication. It provides two backend namespaces: `backend/pubsub/publisher/kafka` for publishing and `backend/pubsub/subscriber/kafka` for subscribing. ## Publisher Configuration Add the `backend/pubsub/publisher/kafka` namespace under a backend’s `extra_config` to publish messages to a Kafka topic. KrakenD converts the backend’s `input_headers` into message metadata. The following configuration enables publishing to the `orderplacement` topic with mTLS authentication: ```json { "backend/pubsub/publisher/kafka": { "success_status_code": 201, "writer": { "topic": "orderplacement", "key_meta": "X-My-Key", "cluster": { "brokers": [ "localhost:49092" ], "client_id": "krakend_async_agent", "client_tls": { "allow_insecure_connections": false, "ca_certs": [ "./config/ca/cacert.pem" ], "client_certs": [ { "certificate": "./config/certs/client/client.signed.pem", "private_key": "./config/certs/client/client.key" } ] } }, "producer": { "idempotent": true } } } } ``` The top-level fields for `backend/pubsub/publisher/kafka` are: ##### Fields of PubSub Kafka Publisher EE \* required fields ##### [`success_status_code`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#success_status_code "Click to copy link to success_status_code") number HTTP status code to return for a successful write in the queue Defaults to `200` ##### [`writer`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#writer "Click to copy link to writer") * object Define how to write messages to a kafka topic Schema: [https://www.krakend.io/schema/v2.13/backend/pubsub/kafka/publisher.json](https://www.krakend.io/schema/v2.13/backend/pubsub/kafka/publisher.json "JSON schema definition") The `writer` field contains the configuration for writing to a Kafka topic: ##### Fields of Kafka Writer \* required fields ##### [`cluster`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#cluster "Click to copy link to cluster") * object Settings to establish the connection to the kafka cluster ##### [`key_meta`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#key_meta "Click to copy link to key_meta") string Name of the header where the kafka message key value is written ##### [`producer`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#producer "Click to copy link to producer") object Details about how to write to a topic ##### [`topic`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#topic "Click to copy link to topic") * string Topic to write to Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/writer.json](https://www.krakend.io/schema/v2.13/messaging/kafka/writer.json "JSON schema definition") The `cluster` field describes details about the connection to the Kafka cluster. It is the same configuration used for the Kafka PubSub subscriber and the Kafka async agent: ##### Fields of Kafka Cluster Connection \* required fields ##### [`brokers`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#brokers "Click to copy link to brokers") * array of strings ##### [`channel_buffer_size`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#channel_buffer_size "Click to copy link to channel_buffer_size") number The number of events to buffer in internal and external channels. This permits the producer and consumer to continue processing some messages in the background while user code is working, greatly improving throughput Defaults to `256` ##### [`client_id`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#client_id "Click to copy link to client_id") string A name to give to the client stablishing the connection Defaults to `"KrakenD v[X].[Y].[Z]"` ##### [`client_tls`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#client_tls "Click to copy link to client_tls") object Enables specific TLS connection options when connecting to the Kafke brokers. Supports all options under [TLS client settings](https://www.krakend.io/docs/service-settings/tls/#client-tls-settings). ##### [`dial_timeout`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#dial_timeout "Click to copy link to dial_timeout") string Dial timeout for establishing new connections Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30s"` ##### [`keep_alive`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#keep_alive "Click to copy link to keep_alive") string Time to maintain an idle connection open Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`metadata_retry_backoff`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#metadata_retry_backoff "Click to copy link to metadata_retry_backoff") string The time between attempts to reconnect when disconnected Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"250ms"` ##### [`metadata_retry_max`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#metadata_retry_max "Click to copy link to metadata_retry_max") number When a disconnection happens, the client needs to refresh its metadata to know the current state of the kafka cluster (effectively the number of attempts to reconnect) Defaults to `3` ##### [`rack_id`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#rack_id "Click to copy link to rack_id") string A name to identify the rack we are connecting from Defaults to `""` ##### [`read_timeout`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#read_timeout "Click to copy link to read_timeout") string Maximum time allowed to read from broker Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30s"` ##### [`sasl`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#sasl "Click to copy link to sasl") object Enables to authenticate to the Kafka service using user and password ##### [`write_timeout`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#write_timeout "Click to copy link to write_timeout") string Maximum time allowed to write to broker Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30s"` Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/cluster.json](https://www.krakend.io/schema/v2.13/messaging/kafka/cluster.json "JSON schema definition") Reconnection parameters The `metadata_retry_max` and `metadata_retry_backoff` play a **very important role in reconnection** in case you have network issues. By default, its values allow to recovery only from connections lost during less that 750ms (3 retries with 250ms backoff). We strongly advise incrementing the number of retries (and increasing the backoff to 1sec), if you want to protect yourself from longer disconnection times. The `cluster` field includes a `client_tls` field that allows you to authenticate with the Kafka service, as described in [Service Client TLS Settings](/docs/enterprise/service-settings/tls/#client-tls-settings). You can also use user and password authentication with the SASL config: ##### Fields of Kafka SASL \* required fields ##### [`auth_identity`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#auth_identity "Click to copy link to auth_identity") string Auth Identity is an (optional) authorization identity (authzid) to use for SASL/PLAIN authentication (if different from User) when an authenticated user is permitted to act as the presented alternative user. See RFC4616 for details ##### [`azure_event_hub`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#azure_event_hub "Click to copy link to azure_event_hub") boolean Kafka > 1.x should use SASL V1, except on Azure EventHub which uses V0 Defaults to `false` ##### [`disable_hanshake`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#disable_hanshake "Click to copy link to disable_hanshake") boolean Whether or not to send the Kafka SASL handshake first if enabled. You should only set this to false if you’re using a non-Kafka SASL proxy Defaults to `true` ##### [`mechanism`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#mechanism "Click to copy link to mechanism") Name of the enabled SASL mechanism Possible values are: `"PLAIN"` , `"OAUTHBEARER"` Defaults to `"PLAIN"` ##### [`password`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#password "Click to copy link to password") string Password for SASL/PLAIN authentication ##### [`scram_auth_id`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#scram_auth_id "Click to copy link to scram_auth_id") string Authz id used for SASL/SCRAM authentication ##### [`user`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#user "Click to copy link to user") string Authentication identity (authcid) to present for SASL/PLAIN or SASL/SCRAM authentication Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/sasl.json](https://www.krakend.io/schema/v2.13/messaging/kafka/sasl.json "JSON schema definition") The `producer` field has details about how to write messages: ##### Fields of Kafka Producer Details \* required fields ##### [`compression_codec`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#compression_codec "Click to copy link to compression_codec") Type of compression to use on messages (defaults to no compression). Similar to `compression.codec` setting of the JVM producer. Possible values are: `"none"` , `"gzip"` , `"snappy"` , `"lz4"` , `"zstd"` Defaults to `"none"` ##### [`compression_level`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#compression_level "Click to copy link to compression_level") string Level of compression to use on messages. The meaning depends on the actual compression type used and defaults to default compression level for the codec. ##### [`idempotent`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#idempotent "Click to copy link to idempotent") boolean If enabled, the producer will ensure that exactly one copy of each message is written Defaults to `false` ##### [`max_message_bytes`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#max_message_bytes "Click to copy link to max_message_bytes") number Maximum permitted size of a message. Should be set equal to or smaller than the broker’s `message.max.bytes`. ##### [`partitioner`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#partitioner "Click to copy link to partitioner") Select behaviour for choosing the partition to send messages (similar to the `partitioner.class` setting for the JVM producer). The options are: \- `sarama`: **DEPRECATED** uses a Partitioner which behaves as follows: If the message’s key is nil then a random partition is chosen. Otherwise the `FNV-1a` hash of the encoded bytes of the message key is used, modulus the number of partitions. This ensures that messages with the same key always end up on the same partition. \- `standard` is like `sarama` except that it handles absolute values in the same way as the reference Java implementation. `sarama` was supposed to do that but it had a mistake and now there are people depending on both behaviours. This will all go away on the next major version bump. \- `random` uses a Partitioner which chooses a random partition each time. \- `roundrobin` uses a Partitioner which walks through the available partitions one at a time. Possible values are: `"sarama"` , `"standard"` , `"random"` , `"roundrobin"` Defaults to `"standard"` ##### [`required_acks`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#required_acks "Click to copy link to required_acks") string Level of acknowledgement reliability needed from the broker. Equivalent to the `request.required.acks` setting of the JVM producer. Can be a positibe number (as a string), or one of hte following values: `no_response` (no required acks), `wait_for_local` (waits for only the local commit to succeed before responding), `wait_for_all` (waits for all in-sync replicas to commit before responding). Defaults to `"wait_for_local"` ##### [`required_acks_timeout`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#required_acks_timeout "Click to copy link to required_acks_timeout") string Maximum duration the broker will wait the receipt of the number of `required_acks`. This is only relevant when `required_acks` is set to WaitForAll or a number > 1. Only supports millisecond resolution, nanoseconds will be truncated. Equivalent to the JVM producer’s `request.timeout.ms` setting. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"10s"` ##### [`retry_backoff`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#retry_backoff "Click to copy link to retry_backoff") string How long to wait for the cluster to settle between retries (similar to the `retry.backoff.ms` setting of the JVM producer. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"100ms"` ##### [`retry_max`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#retry_max "Click to copy link to retry_max") number The total number of times to retry sending a message. Similar to the `message.send.max.retries` setting of the JVM producer. Defaults to `3` Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/producer.json](https://www.krakend.io/schema/v2.13/messaging/kafka/producer.json "JSON schema definition") ### OpenTelemetry **Metrics** When OpenTelemetry is enabled, KrakenD reports the following metrics for all messages: - `messaging.write.body.size`: histogram of body sizes in bytes (does not include metadata). - `messaging.write.body.duration`: histogram of duration taken to write a message. - `messaging.write.failure.count`: histogram of the time taken to fail to write a message. All metrics include the following attributes: - `kind`: identifies the messaging system (in this case `kafka`). - `topic`: the topic where the message is written to. **Traces** KrakenD adds information about the write process as a new span if a trace is active, with the following fields: - `kind`: identifies the messaging system (in this case `kafka`). - `topic`: the topic where the message is written to. - `messaging.write.body.size`: size of the message body. - `messaging.write.body.duration`: time taken to write the message in seconds. ## Subscriber Configuration Add the `backend/pubsub/subscriber/kafka` namespace under a backend’s `extra_config` to consume messages from a Kafka topic. It contains a `reader` field with the same configuration as the Kafka async agent driver. The following configuration subscribes to the `orderplacement` topic with mTLS authentication: ```json { "backend/pubsub/subscriber/kafka": { "reader": { "topics": ["orderplacement"], "key_meta": "X-Idempotency-Key", "cluster": { "brokers": [ "localhost:49092" ], "client_id": "krakend_async_agent", "client_tls": { "allow_insecure_connections": false, "ca_certs": [ "./config/ca/cacert.pem" ], "client_certs": [ { "certificate": "./config/certs/client/client.signed.pem", "private_key": "./config/certs/client/client.key" } ] } }, "group": { "id": "k_endpoint_read", "isolation_level": "read_commited" } } } } ``` ##### Fields of PubSub Kafka Subscriber EE \* required fields ##### [`reader`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#reader "Click to copy link to reader") * object Define how to read messages from a kafka topic Schema: [https://www.krakend.io/schema/v2.13/backend/pubsub/kafka/subscriber.json](https://www.krakend.io/schema/v2.13/backend/pubsub/kafka/subscriber.json "JSON schema definition") KrakenD consumes messages following the “latest” offset policy, meaning it processes only messages produced after KrakenD starts. The `reader` field defines how to read from a Kafka cluster: ##### Fields of Kafka Reader \* required fields ##### [`cluster`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#cluster "Click to copy link to cluster") * object Settings to stablish the connection to the kafka cluster ##### [`group`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#group "Click to copy link to group") object Details about the consumer croup ##### [`key_meta`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#key_meta "Click to copy link to key_meta") string Name of the header where the kafka message key value is written ##### [`topics`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#topics "Click to copy link to topics") * array of strings List of topics to read from Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/reader.json](https://www.krakend.io/schema/v2.13/messaging/kafka/reader.json "JSON schema definition") The `cluster` field describes details about the connection to the Kafka cluster. It is the same configuration used for the Kafka PubSub publisher and the Kafka async agent: ##### Fields of Kafka Cluster Connection \* required fields ##### [`brokers`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#brokers "Click to copy link to brokers") * array of strings ##### [`channel_buffer_size`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#channel_buffer_size "Click to copy link to channel_buffer_size") number The number of events to buffer in internal and external channels. This permits the producer and consumer to continue processing some messages in the background while user code is working, greatly improving throughput Defaults to `256` ##### [`client_id`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#client_id "Click to copy link to client_id") string A name to give to the client stablishing the connection Defaults to `"KrakenD v[X].[Y].[Z]"` ##### [`client_tls`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#client_tls "Click to copy link to client_tls") object Enables specific TLS connection options when connecting to the Kafke brokers. Supports all options under [TLS client settings](https://www.krakend.io/docs/service-settings/tls/#client-tls-settings). ##### [`dial_timeout`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#dial_timeout "Click to copy link to dial_timeout") string Dial timeout for establishing new connections Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30s"` ##### [`keep_alive`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#keep_alive "Click to copy link to keep_alive") string Time to maintain an idle connection open Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`metadata_retry_backoff`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#metadata_retry_backoff "Click to copy link to metadata_retry_backoff") string The time between attempts to reconnect when disconnected Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"250ms"` ##### [`metadata_retry_max`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#metadata_retry_max "Click to copy link to metadata_retry_max") number When a disconnection happens, the client needs to refresh its metadata to know the current state of the kafka cluster (effectively the number of attempts to reconnect) Defaults to `3` ##### [`rack_id`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#rack_id "Click to copy link to rack_id") string A name to identify the rack we are connecting from Defaults to `""` ##### [`read_timeout`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#read_timeout "Click to copy link to read_timeout") string Maximum time allowed to read from broker Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30s"` ##### [`sasl`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#sasl "Click to copy link to sasl") object Enables to authenticate to the Kafka service using user and password ##### [`write_timeout`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#write_timeout "Click to copy link to write_timeout") string Maximum time allowed to write to broker Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30s"` Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/cluster.json](https://www.krakend.io/schema/v2.13/messaging/kafka/cluster.json "JSON schema definition") Reconnection parameters The `metadata_retry_max` and `metadata_retry_backoff` play a **very important role in reconnection** in case you have network issues. By default, its values allow to recovery only from connections lost during less that 750ms (3 retries with 250ms backoff). We strongly advise incrementing the number of retries (and increasing the backoff to 1sec), if you want to protect yourself from longer disconnection times. The `cluster` field includes a `client_tls` field that allows you to authenticate with the Kafka service, as described in [Service Client TLS Settings](/docs/enterprise/service-settings/tls/#client-tls-settings). You can also use user and password authentication with the SASL config: ##### Fields of Kafka SASL \* required fields ##### [`auth_identity`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#auth_identity "Click to copy link to auth_identity") string Auth Identity is an (optional) authorization identity (authzid) to use for SASL/PLAIN authentication (if different from User) when an authenticated user is permitted to act as the presented alternative user. See RFC4616 for details ##### [`azure_event_hub`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#azure_event_hub "Click to copy link to azure_event_hub") boolean Kafka > 1.x should use SASL V1, except on Azure EventHub which uses V0 Defaults to `false` ##### [`disable_hanshake`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#disable_hanshake "Click to copy link to disable_hanshake") boolean Whether or not to send the Kafka SASL handshake first if enabled. You should only set this to false if you’re using a non-Kafka SASL proxy Defaults to `true` ##### [`mechanism`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#mechanism "Click to copy link to mechanism") Name of the enabled SASL mechanism Possible values are: `"PLAIN"` , `"OAUTHBEARER"` Defaults to `"PLAIN"` ##### [`password`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#password "Click to copy link to password") string Password for SASL/PLAIN authentication ##### [`scram_auth_id`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#scram_auth_id "Click to copy link to scram_auth_id") string Authz id used for SASL/SCRAM authentication ##### [`user`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#user "Click to copy link to user") string Authentication identity (authcid) to present for SASL/PLAIN or SASL/SCRAM authentication Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/sasl.json](https://www.krakend.io/schema/v2.13/messaging/kafka/sasl.json "JSON schema definition") The `group` field has details about the Kafka consumer group: ##### Fields of Kafka Consumer Details \* required fields ##### [`fetch_default`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#fetch_default "Click to copy link to fetch_default") number The default number of message bytes to fetch from the broker in each request (default 1MB). This should be larger than the majority of your messages, or else the consumer will spend a lot of time negotiating sizes and not actually consuming. Similar to the JVM’s `fetch.message.max.bytes` ##### [`heartbeat_interval`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#heartbeat_interval "Click to copy link to heartbeat_interval") string Expected time between heartbeats to the consumer coordinator when using Kafka’s group management facilities. Heartbeats are used to ensure that the consumer’s session stays active and to facilitate rebalancing when new consumers join or leave the group. The value must be set lower than Consumer.Group.Session.Timeout, but typically should be set no higher than 1/3 of that value. It can be adjusted even lower to control the expected time for normal rebalances Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"3s"` ##### [`id`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#id "Click to copy link to id") string Name of the consumer group to use ##### [`instance_id`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#instance_id "Click to copy link to instance_id") string Support KIP-345 ##### [`isolation_level`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#isolation_level "Click to copy link to isolation_level") Supports 2 modes: `read_commited` to consume and return all messages in message channel, and `read_uncommited` to hide messages that are part of an aborted transaction Possible values are: `"read_commited"` , `"read_uncommited"` Defaults to `"read_commited"` ##### [`rebalance_strategies`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#rebalance_strategies "Click to copy link to rebalance_strategies") array Priority-ordered list of client-side consumer group balancing strategies that will be offered to the coordinator. The first strategy that all group members support will be chosen by the leader. Options are: `range`, `roundrobin`, and `sticky` Defaults to `["range"]` ##### [`rebalance_timeout`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#rebalance_timeout "Click to copy link to rebalance_timeout") string Maximum allowed time for each worker to join the group once a rebalance has begun. This is basically a limit on the amount of time needed for all tasks to flush any pending data and commit offsets. If the timeout is exceeded, then the worker will be removed from the group, which will cause offset commit failures. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"60s"` ##### [`session_timeout`](https://www.krakend.io/docs/enterprise/backends/pubsub/kafka/#session_timeout "Click to copy link to session_timeout") string Timeout used to detect consumer failures when using Kafka’s group management facility. The consumer sends periodic heartbeats to indicate its liveness to the broker. If no heartbeats are received by the broker before the expiration of this session timeout, then the broker will remove this consumer from the group and initiate a rebalance. Note that the value must be in the allowable range as configured in the broker configuration by `group.min.session.timeout.ms` and `group.max.session.timeout.ms` Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"10s"` Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/group.json](https://www.krakend.io/schema/v2.13/messaging/kafka/group.json "JSON schema definition") ### OpenTelemetry **Metrics** When OpenTelemetry is enabled, KrakenD reports the following metrics for all messages read: - `messaging.read.body.size`: histogram of body sizes in bytes (does not include metadata). - `messaging.read.body.duration`: histogram of the duration taken to read a message. - `messaging.read.ack.duration`: histogram of the time taken to acknowledge the message. - `messaging.read.failure.count`: histogram of time taken to fail reading a message. All metrics include the following attributes: - `kind`: identifies the messaging system (for example `kafka`). - `topic`: the topic where the message is read from. - `origin`: always set to `subscriber`, to differentiate reading metrics from those of an async agent (which can take longer as it waits for new events). **Traces** KrakenD adds information about the read process as a new span if a trace is active, with the following fields: - `kind`: identifies the messaging system (for example `kafka`). - `topic`: the topic where the message is read from. - `messaging.read.body.size`: size of the read message body. - `messaging.read.body.duration`: time taken to read a message in seconds. ### ACK Behaviour in Kafka Kafka has no per-message ACK: acknowledging a Kafka message **commits the client’s partition offset**. KrakenD ACKs every message read, even when the response of the pipeline results in a non-successful status code. --- # [GraphQL Backend Integration](https://www.krakend.io/docs/backends/graphql/) The **GraphQL integration** allows you to work in two different modes: 1. Apply gateway functionality in the middle of a GraphQL client and its GraphQL servers (just proxy) 2. Convert REST endpoints to GraphQL calls (adapter/transformer). KrakenD offers a simple yet powerful way of consuming GraphQL content from your distributed graphs. The main benefits of using KrakenD as a **GraphQL Gateway** are: - Simple **GraphQL Federation**: chop your monolithic GraphQL server into different services and aggregate them in the gateway. - User validation: Handle the authorization in the gateway before adding any load to your GraphQL servers. - Protect and secure internal GraphQL endpoints - Rate limit GraphQL usage - Prepare aggregated data for external caching - Hide complexity to the clients by providing a REST interface ## REST to GraphQL transformation In this scenario, **the end-user consumes traditional REST content**, without even knowing that there is a GraphQL server behind: ![](/images/documentation/graphql/krakend-rest-to-graphql-transformation.png "REST to Graphql") **KrakenD can use the variables in the body or in the endpoint URL** to generate the final GraphQL query it will send to the GraphQL server. The query is loaded from an external file or declared inline in the configuration and contains any variables needing replacement with the user input. **KrakenD acts as the GraphQL client**, negotiating the content with the GraphQL server and hiding its complexity from the end-user. The end-user consumes REST content and retrieves the data in JSON, XML, RSS, or any other format supported by KrakenD. The configuration to consume GraphQL content from your GraphQL graphs could look like this: ```json { "endpoint": "/marketing/{user_id}", "method": "POST", "backend": [ { "timeout": "4100ms", "url_pattern": "/graphql?timeout=4s", "extra_config": { "backend/graphql": { "type": "mutation", "query_path": "./graphql/mutations/marketing.graphql", "variables": { "user":"{user_id}", "other_static_variables": { "foo": false, "bar": true } }, "operationName": "addMktPreferencesForUser" } } } ] } ``` The configuration for the namespace `backend/graphql` has the following structure: ##### Fields of GraphQL \* required fields Minimum configuration needs **one of**: `type` + `query` , or `type` + `query_path` ##### [`operationName`](https://www.krakend.io/docs/backends/graphql/#operationName "Click to copy link to operationName") string A meaningful and explicit name for your operation, required in multi-operation documents and for helpful debugging and server-side logging. Example: `"addMktPreferencesForUser"` ##### [`query`](https://www.krakend.io/docs/backends/graphql/#query "Click to copy link to query") string An inline GraphQL query you want to send to the server. Use this attribute for simple and inline queries, use `query_path` instead for larger queries. Use escaping when needed. Example: `"{ \n find_follower(func: uid(\"0x3\")) {\n name \n }\n }"` ##### [`query_path`](https://www.krakend.io/docs/backends/graphql/#query_path "Click to copy link to query_path") string Path to the file containing the query. This file is loaded during startup and never checked again, if it changes KrakenD will be unaware of it. Example: `"./graphql/mutations/marketing.graphql"` ##### [`type`](https://www.krakend.io/docs/backends/graphql/#type "Click to copy link to type") The type of query you are declaring, `query` (read), or `mutation` (write). Possible values are: `"query"` , `"mutation"` ##### [`variables`](https://www.krakend.io/docs/backends/graphql/#variables "Click to copy link to variables") object A dictionary defining all the variables sent to the GraphQL server. You can use `{placeholders}` to inject parameters from the endpoint URL. Schema: [https://www.krakend.io/schema/v2.13/backend/graphql.json](https://www.krakend.io/schema/v2.13/backend/graphql.json "JSON schema definition") When using an inline `query` (instead of a file from the `query_path,` which does this job automatically), use escape when needed. Examples: - `"query": "{ \n find_follower(func: uid(\"0x3\")) {\n name \n }\n }"`. - `"query": "{ q(func: uid(1)) { uid } }"` The combination of `type` and the endpoint/backend `method` define how you accept parameters from the users and how you send the query to the server, respectively: - A type `query` means you accept user parameters in the query string. - A type `mutation` means you accept user parameters in the body. - A method `GET` sends the query to the GraphQL server as an **autogenerated query string**. - A method `POST` sends the query to the GraphQL server as an **autogenerated body**. When the user and the KrakenD configuration define the same variables (collision), the user variables take preference. In all, the following combinations are possible: - `method=GET` + `type=query`: Generates a query string using any `{variables}` in the endpoint, but you don’t have any data in a possible body. - `method=GET` + `type=mutation`: Generates a query string including any variables in the body of the REST call (if present), but you cannot have `{variables}` from the URL - `method=POST` + `type=query`: Generates a body using any `{variables}` in the endpoint, but it does not use the user’s body to form the new body. - `method=POST` + `type=mutation`: Generates a body including any variables in the REST body plus the ones in the configuration, but you cannot have `{variables}` from the URL Automatic content-type setting Since KrakenD CE v2.3.3 the content-type header the GraphQL receives is `application/json`, and is not longer needed to pass it from the client. ## Examples of GraphQL request generation ### POST + mutation Suppose the end-user makes the following request to KrakenD, which contains a body with a JSON containing review information to `/review/{id_show}`: Query to graphql endpoint  ``` $curl -XPOST -d '{ "review": { "stars": 5, "commentary": "This is a great movie!" } }' http://krakend/review/1500 ``` The mutation is stored in an external file called `review.graphql` and has the following content: ```graphql mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { createReview(episode: $ep, review: $review) { stars commentary } } ``` The configuration of KrakenD for this example is as follows: ```json { "endpoint": "/review/{id_show}", "method": "POST", "backend": [ { "timeout": "4100ms", "url_pattern": "/graphql?timeout=4s", "extra_config": { "backend/graphql": { "type": "mutation", "query_path": "./graphql/mutations/review.graphql", "variables": { "review": { "stars": 3, "commentary": "meh" }, "ep": "JEDI", "id_show": "{id_show}" }, "operationName": "CreateReviewForEpisode" } } } ] } ``` With the example and the configuration of KrakenD above, when the user sends a body, it will be sent to the backend as it is. However, if the user does not include any of the `variables` in the body, it will add them to the final request to the backend. So, with this example, any `review` will receive 3 stars and a “meh” comment if the end-user does not pass it. With the cURL request in the example above, the backend receives the following JSON payload: ```json { "query": "mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {\ncreateReview episode: $ep, review: $review) {\nstars\ncommentary\n}\n}\n", "variables": { "ep": "JEDI", "review": { "stars": 5, "commentary": "This is a great movie!" }, "id_show": "{id_show}" }, "operationName": "CreateReviewForEpisode" } ``` - The `query` contains the content of the external file defining the GraphQL you want to execute. - The `variables` section contains the following: - The variable `id_show` does not replace the value of `{id_show}`, as it is a POST + mutation - The `ep` field is passed as it is in the configuration because the user did not pass it. - The `review` variable is used because the POST has data and is included in the final body, which is also passed to the GraphQL server. ### GET + mutation In case the method is a GET instead of a body. The configuration we will use is as follows: ```json { "endpoint": "/review/{stars}", "method": "GET", "backend": [ { "timeout": "4100ms", "url_pattern": "/graphql?timeout=4s", "extra_config": { "backend/graphql": { "type": "mutation", "query_path": "./graphql/mutations/review.graphql", "variables": { "review": { "stars": "{stars}" }, "ep": "JEDI", "id_show": "1500" }, "operationName": "CreateReviewForEpisode" } } } ] } ``` And we call the endpoint like this (notice that because this is a mutation, we must pass a body): Query to graphql endpoint  ``` $curl -d '' -G http://krakend/review/5 ``` In this case, the GraphQL server receives a URL-encoded query with all the variables, where: - The variable `{stars}` is replaced by its value `5` as passed in the URL - The `review`, and `ep` fields are passed as they are in the configuration. ### POST + Query In this example, we want to do a query instead of a mutation, and we have a `query_path` file with the following content: ```graphql query Hero($episode: Episode, $withFriends: Boolean!) { hero(episode: $episode) { name friends @include(if: $withFriends) { name } } } ``` And a KrakenD configuration like this: ```json { "endpoint": "/hero/{episode}", "method": "POST", "backend": [ { "timeout": "4100ms", "url_pattern": "/graphql?timeout=4s", "extra_config": { "backend/graphql": { "type": "query", "query_path": "./graphql/mutations/hero.graphql", "variables": { "episode": "{episode}", "withFriends": false }, "operationName": "Hero" } } } ] } ``` Query to graphql endpoint  ``` $curl -XPOST http://krakend/hero/JEDI ``` The GraphQL receives a body with the following content: ```json { "query": "query Hero($episode: Episode, $withFriends: Boolean!) {\n hero(episode: $episode) {\n name\n friends @include(if: $withFriends) {\n name\n }\n }\n}", "variables": { "episode": "JEDI", "withFriends": false }, "operationName": "Hero" } ``` As you can see, even though this is a POST, **the body is ignored**. - The `episode` variable is taken from the endpoint parameter, as defined in the configuration - The `withFriends` variable is hardcoded in the configuration. ### GET + Query The final example of GET + query. We have the following query in the `query_path` contents: ```graphql query Hero($episode: Episode, $withFriends: Boolean!) { hero(episode: $episode) { name friends @include(if: $withFriends) { name } } } ``` On KrakenD configuration: ```json { "endpoint": "/hero/{episode}", "method": "GET", "backend": [ { "timeout": "4100ms", "url_pattern": "/graphql?timeout=4s", "extra_config": { "backend/graphql": { "type": "query", "query_path": "./graphql/mutations/hero.graphql", "variables": { "episode": "{episode}", "withFriends": false }, "operationName": "Hero" } } } ] } ``` Query to graphql endpoint  ``` $curl -XGET http://krakend/hero/JEDI ``` In this case, the GraphQL server receives a URL-encoded query with all the variables, where: - The variable `{episode}` is replaced by its value `JEDI` - The `withFriends` field is passed as it is in the configuration. ## GraphQL gateway as a proxy In this approach, KrakenD gets in the middle to validate or rate limit requests, but the request is forwarded to the GraphQL servers, who receive the original GraphQL query from the end user. ![](/images/documentation/graphql/krakend-graphql-proxy.png "Graphql") When working in this mode, you only need to configure the GraphQL endpoint and add your GraphQL as the backend. An example: ```json { "endpoint": "/graphql", "method": "POST", "input_query_strings":[ "query", "operationName", "variables" ], "backend": [ { "timeout": "4100ms", "host": ["http://your-graphql.server:4000"], "url_pattern": "/graphql?timeout=4s" } ] } ``` The previous example uses a set of recognized query strings to pass to the GraphQL server. You can also use `"input_query_strings":["*"]` to forward any query string. The exact configuration works with a `POST` method. As the configuration above does not use \`no-op, ’ you can take the opportunity to connect to more servers in the same endpoint by adding additional backend objects in the configuration. In addition, if you’d like to use your GraphQL from a browser like **Apollo Studio**, you will need to add two additional components to your configuration: - Accept the `OPTIONS` method, adding the [flag `auto_options`](/docs/service-settings/router-options/#auto_options) - Add [CORS](/docs/service-settings/cors/) ## GraphQL Federation KrakenD’s principles are working with simultaneous aggregation of data. In that sense, consuming multiple subgraphs (or back-end services) comes naturally. However, using the REST to GraphQL capabilities, you can federate data using a simple strategy: define the subgraphs in the configuration instead of moving this responsibility to the consumer. It is a simplistic but still potent approach, as you can define templates with queries and let krakend aggregate the responses. Create rest endpoints with fixed graphs you want to consume in the configuration. Then, in each back-end query (subgraph), you decide what transformation rules to apply, the validation, rate-limiting, etc., and even connect your endpoints with other services like queues. The following example is a REST endpoint consuming data from 2 different subgraphs in parallel. Of course, you could add here any other KrakenD components you could need: ```json { "endpoint": "/user-data/{id_user}", "backend": [ { "timeout": "3100ms", "url_pattern": "/graphql?timeout=3s", "group": "user", "method": "GET", "host": ["http://user-graph:4000"], "extra_config": { "backend/graphql": { "type": "query", "query_path": "./graphql/queries/user.graphql", "variables": { "user":"{user_id}" }, "operationName": "getUserData" } } }, { "timeout": "2100ms", "url_pattern": "/graphql?timeout=2s", "group": "user_metadata", "method": "GET", "host": ["http://metadata:4000"], "extra_config": { "backend/graphql": { "type": "query", "query_path": "./graphql/queries/user_metadata.graphql", "variables": { "user":"{user_id}" }, "operationName": "getUserMetadata" } } } ] } ``` --- # [AWS Lambda Integration](https://www.krakend.io/docs/backends/lambda/) The Lambda integration allows you to **invoke Amazon Lambda functions** on a KrakenD endpoint call. The content returned by the lambda function can be treated and manipulated as any other backend. The **payload** that is sent to the Lambda function comes from the request and depends on the method used by the `endpoint`: - Method `GET`: The payload contains all the request parameters. - Non-`GET` methods: The payload is defined by the content of the **body** in the request. You don’t need to set an Amazon API Gateway in the middle, as KrakenD does this job for you. ## Lambda configuration Dummy hosts and url\_pattern Notice in the examples that the `host` and `url_pattern` are needed as per the [backend definition](/docs/backends/), but KrakenD will never use them when connecting to a Lambda. Feel free to add any value in there, but the entry must be present. 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: ##### Fields of AWS Lambda functions \* required fields ##### [`endpoint`](https://www.krakend.io/docs/backends/lambda/#endpoint "Click to copy link to endpoint") string An optional parameter to customize the Lambda endpoint to call. Useful when Localstack is used for testing instead of direct AWS usage. ##### [`function_name`](https://www.krakend.io/docs/backends/lambda/#function_name "Click to copy link to function_name") string Name of the lambda function as saved in the AWS service. You have to choose between function\_name and function\_param\_name but not both. ##### [`function_param_name`](https://www.krakend.io/docs/backends/lambda/#function_param_name "Click to copy link to function_param_name") string The endpoint {placeholder} that sets the function name, with the **first letter uppercased**. You have to choose between `function_name` and `function_param_name` but not both. If your endpoint defines the route `/foo/{bar}` the value of `function_param_name` must be `Bar` with the uppercased B. ##### [`max_retries`](https://www.krakend.io/docs/backends/lambda/#max_retries "Click to copy link to max_retries") integer Maximum times you want to execute the function until you have a successful response. The value -1 defers the max retry setting to the service specific configuration. Defaults to `0` ##### [`region`](https://www.krakend.io/docs/backends/lambda/#region "Click to copy link to region") string The AWS identifier region Examples: `"us-east-1"` , `"eu-west-2"` Schema: [https://www.krakend.io/schema/v2.13/backend/lambda.json](https://www.krakend.io/schema/v2.13/backend/lambda.json "JSON schema definition") Parameters’ first character uppercased Notice the capitalization of the first letter of the parameter names at the configuration in the examples below. For instance, when an endpoint parameter is defined as `{route}`, define it in the config as `Route`. When passing the lambda function name, you can use any of the following formats: - Name only: `my-function` - Name and version: `my-function:v1` - Function ARN: `arn:aws:lambda:us-west-2:123456789012:function:my-function` - Partial ARN: `123456789012:function:my-function` ### Authentication and connectivity The KrakenD machine needs connectivity with your AWS account and the credentials to do so. There are several ways you can achieve this: - Copying your AWS credentials in the default file, `~/.aws/credentials` (and maybe an additional `~/.aws/config` and the env var `AWS_PROFILE` if you have several profiles) - Passing the environment variables with at least `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` (and maybe `AWS_REGION`) when starting KrakenD. - Having an IAM user with a policy and execution role that lets you invoke the function from the machine When setting the credentials, ensure the Lambda is callable within the KrakenD box with the selected method. If your machine has the AWS CLI installed, you can test your Lambda: Invoking a Lambda function  ``` $aws lambda invoke --region us-east-1 --function-name myLambdaFunction --output json result.json ``` #### Authentication examples Mounting an existing `.aws` directory with the credentials in it (notice that the home of the Docker user is `krakend`): Term  ``` $docker run --rm -it -p "8080:8080" \ -e "AWS_PROFILE=default" \ -v "/home/user/.aws:/home/krakend/.aws:ro" \ -v "$PWD:/etc/krakend" krakend:2.13.8 ``` Passing the credentials directly: Term  ``` $docker run --rm -it -p "8080:8080" \ -e "AWS_ACCESS_KEY_ID=XXX" \ -e "AWS_SECRET_ACCESS_KEY=XXX" \ -e "AWS_REGION=eu-west-1" \ -v "$PWD:/etc/krakend" krakend:2.13.8 ``` ### Header forwarding **The official library of AWS has a limitation preventing KrakenD from sending any headers**, yet it is impossible to forward them even when you add them under `input_headers`. If you require such functionality, you should create a custom payload ([like this one](https://github.com/awsdocs/aws-lambda-developer-guide/blob/main/sample-apps/nodejs-apig/event.json)) with additional data containing the headers (and anything else than the body itself). ### Canary testing of Lambda functions You can easily do **Canary** or **A/B testing** with Lambda functions in combination with a Lua script like the one provided below. For example, the following configuration takes the function name from a parameter that is not defined anywhere. Instead, a Lua script will set it as per our AB test or canary rules. The configuration would be: ```json { "endpoint": "/call-a-lambda", "backend": [ { "host": ["ignore"], "url_pattern": "/ignore", "extra_config": { "backend/lambda": { "function_param_name": "Function_name", "region": "eu-west-1", "max_retries": 1 }, "modifier/lua-backend": { "sources": ["canary.lua"], "pre": "canaryLambda(request.load())", "allow_open_libs": true } } } ] } ``` And the content of the `canary.lua` file would be: ```lua function canaryLambda( req ) local rand = math.random(0, 100) if rand < 20 then req:params("Function_name", "my-function:3") else req:params("Function_name", "my-function:2") end end ``` As you can read from the code, 20% of the requests will invoke `my-function:3`, while the 80% remaining will fall into the older `my-function:2`. For more Canary Release examples (not only Lambda) see this [blog post](/blog/canary-releases/) ## Example: Associate a lambda to a backend When you associate a KrakenD endpoint to a unique lambda function, use this configuration: ```json { "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): ```js exports.handler = async (event) => { // TODO implement const response = { "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: ```json { "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 } } } ] } ``` In this example, the `function_param_name` tells us which is the placeholder in the endpoint setting the Lambda. In this case, `{lambda}`. Following the code, a call `GET /call-a-lambda/my-lambda` would produce a call to the `My-lambda` function in AWS. --- # [Event-Driven API Gateway: Async Agents](https://www.krakend.io/docs/async/) **Async agents** are routines listening to queues or PubSub systems that react to new events and push data to your backends. Through async agents, you can start a lot of consumers to process your events autonomously. As opposed to endpoints, async agents do not require users to request something to trigger an action. Instead, the agents connect to an event messaging system and fire an action when an event is delivered. **An async agent can do everything an endpoint can do**. You can use plugins, apply transformations and manipulations, scripting, stub data, parallel or sequential calls to multiple backends, jsonschema, OAuth2 client credentials, rate limiting, circuit breaking, validations, lambda, and a long long etcetera. The obvious limitation is that you cannot use HTTP request functionality (e.g: CORS or JWT validation) as you don’t have any user doing an HTTP request, but an automatic trigger from KrakenD when an event pops in. ## When do you need Async Agents You are trying to implement an event based pattern, such as: - The Saga Pattern - Event sourcing ## How Async agents work When KrakenD starts, it reads the `async_agent` list in the configuration and creates the declared agents. An agent is an application thread that can use one or multiple workers connecting to a queue or PubSub system (consumers). KrakenD contacts the defined backend(s) list passing the event data when a new message kicks in. You might decide to add manipulations, validations, filtering, or any other backend functionality supported by KrakenD. The backend(s) receive the event from the agent as part of the body. Depending on the driver and configuration, when a backend fails to process the request, you can tell KrakenD to reinject the message (`Nack`) to retry the message later by any other worker. Notice that when working with Nack, if KrakenD is the only consumer and your backend fails to process the message continuously, KrakenD will reinsert the message into the queue over and over, and could lead to an infinite loop of messages if no consumer empties these messages. To avoid this, set `nack_discard` to `true`. Notice that as it happens with the endpoints, the messages you consume can be sent in parallel or sequentially to multiple services. ## Configuration The `async_agent` entry is **an array** with all the different agents you want to have running. Each configuration needs to declare in the `extra_config` the connection driver you want to use. like this: ```json { "version": 3, "async_agent": [ { "name": "cool-agent", "connection": { "max_retries": 10, "backoff_strategy":"exponential-jitter", "health_interval": "10s" }, "consumer": { "topic": "*", "workers": 1, "timeout": "150ms", "max_rate": 0.5 }, "backend": [ { "host": [ "http://127.0.0.1:8080" ], "url_pattern": "/__debug/" } ], "extra_config": { "async/amqp": { "host": "amqp://guest:guest@localhost:5672/", "name": "krakend", "exchange": "foo", "durable": true, "delete": false, "exclusive": false, "no_wait": true, "prefetch_count": 5, "auto_ack": false, "no_local": true } } } ] } ``` The configuration accepts the following parameters: ##### Properties of an Async Agent object \* required fields ##### [`backend`](https://www.krakend.io/docs/async/#backend "Click to copy link to backend") * array The [backend definition](https://www.krakend.io/docs/backends/) (as you might have in any endpoint) indicating where the event data is sent. It is a full backend object definition, with all its possible options, transformations, filters, validations, etc. ##### [`connection`](https://www.krakend.io/docs/async/#connection "Click to copy link to connection") object A key defining all the connection settings between the agent and your messaging system. ##### [`backoff_strategy`](#connection-backoff_strategy "Click to copy link to backoff_strategy") When the connection to your event source gets interrupted for whatever reason, KrakenD keeps trying to reconnect until it succeeds or until it reaches the `max_retries`. The backoff strategy defines the delay in seconds in between consecutive failed retries. Possible values are: `"linear"` , `"linear-jitter"` , `"exponential"` , `"exponential-jitter"` , `"fallback"` Defaults to `"fallback"` ##### [`health_interval`](#connection-health_interval "Click to copy link to health_interval") The time between pings checking that the agent is connected to the queue and alive. Regardless of the health interval, if an agent fails, KrakenD will restart it again immediately as defined by `max_retries`and `backoff_strategy`. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1s"` ##### [`max_retries`](#connection-max_retries "Click to copy link to max_retries") integer The maximum number of times you will allow KrakenD to retry reconnecting to a broken messaging system. Use 0 for unlimited retries. Defaults to `0` ##### [`consumer`](https://www.krakend.io/docs/async/#consumer "Click to copy link to consumer") * Defines all the settings for each agent consuming messages. ##### [`max_rate`](#consumer-max_rate "Click to copy link to max_rate") number The maximum number of messages you allow each worker to consume per second. Use any of `0` or `-1` for unlimited speed. Defaults to `0` ##### [`timeout`](#consumer-timeout "Click to copy link to timeout") The maximum time the agent will wait to process an event sent to the backend. If the backend fails to process it, the message is reinserted for later consumption. Defaults to the timeout in the root level, or to `2s` if no value is declared. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). ##### [`topic`](#consumer-topic "Click to copy link to topic") * string The topic name you want to consume. The syntax depends on the driver. Examples for AMQP: `*`, `mytopic`, `lazy.#`, `*`, `foo.*`. ##### [`workers`](#consumer-workers "Click to copy link to workers") integer The number of workers (consuming processes) you want to start simultaneously for this agent. Defaults to `1` ##### [`encoding`](https://www.krakend.io/docs/async/#encoding "Click to copy link to encoding") Informs KrakenD how to parse the responses of your services. Possible values are: `"json"` , `"safejson"` , `"xml"` , `"rss"` , `"string"` , `"no-op"` Defaults to `"json"` ##### [`extra_config`](https://www.krakend.io/docs/async/#extra_config "Click to copy link to extra_config") * Defines the driver that connects to your queue or PubSub system. In addition, you can place other middlewares to modify the request (message) or the response, apply logic or any other endpoint middleware, but adding the driver is mandatory. \* Required **one of**: ( `async/amqp` ) , or ( `async/kafka` ) ##### [`async/amqp`](#extra_config-async%2famqp "Click to copy link to async/amqp") [See the configuration for async/amqp](https://www.krakend.io/docs/async/amqp/) ##### [`async/kafka`](#extra_config-async%2fkafka "Click to copy link to async/kafka") config details to consume from kafka topics ##### [`name`](https://www.krakend.io/docs/async/#name "Click to copy link to name") * string A unique name for this agent. KrakenD shows it in the health endpoint and logs and metrics. KrakenD does not check collision names, so make sure each agent has a different name. Schema: [https://www.krakend.io/schema/v2.13/async\_agent.json](https://www.krakend.io/schema/v2.13/async_agent.json "JSON schema definition") Do not forget to [set the driver for the Async agent](/docs/async/amqp/) inside the `extra_config`. When agents are defined, their activity is shown in the health endpoint with the name of the agent you have chosen. The health endpoint will show for each agent, when was the last time the agent reported itself as alive. The frequency of this checking is as defined in the `health_interval`. Check [how agents report in the health endpoint](/docs/service-settings/health/) ### Backoff strategies The `backoff_strategies` you can set are defined below: - `linear`: The delay time (`d`) grows linearly after each failed retry (`r`) using the formula `d = r`. E.g.: 1st failure retries in 1s, 2nd failure in 2s, 3rd in 3s, and so on. - `linear-jitter`: Similar to `linear` but adds or subtracts a random number: `d = r ± random`. The randomness prevents all agents connected to a mutual service from retrying simultaneously as all have a slightly different delay. The random number never exceeds `±r*0.33` - `exponential`: Multiplicatively increase the time between retries using `d = 2^r`. E.g: `2s`, `4s`, `8s`, `16s`… - `exponential-jitter`: Same as exponential, but adds or subtracts a random number up to 33% of the value using `d = 2^r ± random`. This is the **preferred strategy** when you want to protect the system you are consuming. - **Fallback**: When the strategy is missing or none of the above (e.g.:`fallback`) then it will use **constant** backoff strategy `d=1`. Will retry after one second every time. --- # [AMQP driver for the Asynchronous Agent](https://www.krakend.io/docs/async/amqp/) The AMQP driver for **Async agents** allows you to have KrakenD consuming AMQP queues autonomously. Routines listening to AMQP queues will react by themselves to new events and push data to your backends. This driver is different from the [AMQP backend consumer](/docs/backends/amqp-consumer/). As opposed to endpoints, async agents do not require users to request something to trigger an action. Instead, the agents connect to the queue and fire an action when an event is delivered. ## Async/AMQP Driver Configuration The AMQP driver has to be placed inside the `extra_config` of the [async component](/docs/async/) and allows you connect to an AMQP queue (e.g: RabbitMQ). The settings are as follows: ```json { "async/amqp": { "host": "amqp://guest:guest@localhost:5672/", "name": "krakend", "exchange": "foo", "durable": true, "delete": false, "exclusive": false, "no_wait": true, "prefetch_count": 5, "auto_ack": false, "no_local": true } } ``` ##### Fields of Async AMQP Driver \* required fields ##### [`auto_ack`](https://www.krakend.io/docs/async/amqp/#auto_ack "Click to copy link to auto_ack") boolean When KrakenD retrieves the messages, regardless of the success or failure of the operation, it marks them as ACK. When auto ACK is not used, only successful backend responses do the ACK, and failing messages are requeued. Defaults to `false`. Defaults to `false` ##### [`delete`](https://www.krakend.io/docs/async/amqp/#delete "Click to copy link to delete") boolean When `true`, AMQP deletes the queue when there are no remaining connections. This option is **not recommended** in most of the scenarios. If for instance, the connectivity between KrakenD and AMQP is lost for whatever reason and it’s the only client, AMQP will delete the queue no matter the number of messages there are inside, and when KrakenD gets the connection again the queue won’t exist and future connections will recreate it again. Defaults to `false` ##### [`durable`](https://www.krakend.io/docs/async/amqp/#durable "Click to copy link to durable") boolean Durable queues will survive server restarts and remain when there are no remaining consumers or bindings. Most of the times `true` is recommended, but depends on the use case. Defaults to `false` ##### [`exchange`](https://www.krakend.io/docs/async/amqp/#exchange "Click to copy link to exchange") * string The entity name where messages are retrieved (it will be created, or it must have a **topic** type if already exists). Example: `"some-exchange"` ##### [`exclusive`](https://www.krakend.io/docs/async/amqp/#exclusive "Click to copy link to exclusive") boolean When `true`, AMQP will allow **a single KrakenD client** to access the queue. This option is **not recommended** in environments where the gateway needs high availability and you have several instances running. Defaults to `false` ##### [`host`](https://www.krakend.io/docs/async/amqp/#host "Click to copy link to host") * string The connection string, ends in slash. E.g: `amqp://user:password@host:5672/`. ##### [`nack_discard`](https://www.krakend.io/docs/async/amqp/#nack_discard "Click to copy link to nack_discard") boolean When `true`, messages that cannot be processed are discarded instead of being sent back to the queue. This is useful for scenarios where you want to avoid reprocessing failed messages. Defaults to `false` ##### [`name`](https://www.krakend.io/docs/async/amqp/#name "Click to copy link to name") * string The queue name. ##### [`no_local`](https://www.krakend.io/docs/async/amqp/#no_local "Click to copy link to no_local") boolean The no\_local flag is not supported by RabbitMQ. ##### [`no_wait`](https://www.krakend.io/docs/async/amqp/#no_wait "Click to copy link to no_wait") boolean 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`](https://www.krakend.io/docs/async/amqp/#prefetch_count "Click to copy link to prefetch_count") integer The number of messages you want to prefetch prior to consume them. Defaults to `10` ##### [`prefetch_size`](https://www.krakend.io/docs/async/amqp/#prefetch_size "Click to copy link to prefetch_size") integer The number of bytes you want to use to prefetch messages. Defaults to `0` Schema: [https://www.krakend.io/schema/v2.13/async/amqp.json](https://www.krakend.io/schema/v2.13/async/amqp.json "JSON schema definition") --- # [Kafka Driver for the Asynchronous Agent (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/async/kafka/) The Kafka driver for **async agents** allows KrakenD to consume Kafka queues autonomously. Routines listening to Kafka topics react to new events and push data to your backends. This driver differs from the [Kafka backend consumer](/docs/backends/pubsub/kafka/#consumer). Unlike endpoints, async agents do not require a client request to trigger an action. Instead, the agent subscribes to the topics and fires an action when an event is delivered. ## Driver Configuration Place the Kafka driver inside the `extra_config` of the [async component](/docs/async/) to subscribe to one or more topics: ```json { "async/kafka": { "cluster": { "brokers": [ "localhost:9092" ], "client_id": "cid_stocksconsumer" }, "group": { "group_id": "my_group_id", "isolation_level": "read_commited" }, "key_meta": "Message-Id" } } ``` KrakenD consumes messages following the “latest” offset policy, meaning it processes only messages produced after startup. The Kafka driver configuration accepts the same fields as the `reader` field in [backend kafka consumer](/docs/enterprise/backends/pubsub/kafka/) (`backend/pubsub/kafka/subscriber`), except for the `topics` field that is defined in the general [async agent configuration](/docs/enterprise/async/): ##### Fields of Kafka Reader \* required fields ##### [`cluster`](https://www.krakend.io/docs/enterprise/async/kafka/#cluster "Click to copy link to cluster") * object Settings to stablish the connection to the kafka cluster ##### [`group`](https://www.krakend.io/docs/enterprise/async/kafka/#group "Click to copy link to group") object Details about the consumer croup ##### [`key_meta`](https://www.krakend.io/docs/enterprise/async/kafka/#key_meta "Click to copy link to key_meta") string Name of the header where the kafka message key value is written Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/async\_reader.json](https://www.krakend.io/schema/v2.13/messaging/kafka/async_reader.json "JSON schema definition") The `cluster` field describes details about the connection to the Kafka cluster. It is the same configuration used for the Kafka PubSub [publisher](/docs/enterprise/backends/pubsub/kafka/#publisher-configuration) and [subscriber](/docs/enterprise/backends/pubsub/kafka/#subscriber-configuration): ##### Fields of Kafka Cluster Connection \* required fields ##### [`brokers`](https://www.krakend.io/docs/enterprise/async/kafka/#brokers "Click to copy link to brokers") * array of strings ##### [`channel_buffer_size`](https://www.krakend.io/docs/enterprise/async/kafka/#channel_buffer_size "Click to copy link to channel_buffer_size") number The number of events to buffer in internal and external channels. This permits the producer and consumer to continue processing some messages in the background while user code is working, greatly improving throughput Defaults to `256` ##### [`client_id`](https://www.krakend.io/docs/enterprise/async/kafka/#client_id "Click to copy link to client_id") string A name to give to the client stablishing the connection Defaults to `"KrakenD v[X].[Y].[Z]"` ##### [`client_tls`](https://www.krakend.io/docs/enterprise/async/kafka/#client_tls "Click to copy link to client_tls") object Enables specific TLS connection options when connecting to the Kafke brokers. Supports all options under [TLS client settings](https://www.krakend.io/docs/service-settings/tls/#client-tls-settings). ##### [`dial_timeout`](https://www.krakend.io/docs/enterprise/async/kafka/#dial_timeout "Click to copy link to dial_timeout") string Dial timeout for establishing new connections Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30s"` ##### [`keep_alive`](https://www.krakend.io/docs/enterprise/async/kafka/#keep_alive "Click to copy link to keep_alive") string Time to maintain an idle connection open Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`metadata_retry_backoff`](https://www.krakend.io/docs/enterprise/async/kafka/#metadata_retry_backoff "Click to copy link to metadata_retry_backoff") string The time between attempts to reconnect when disconnected Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"250ms"` ##### [`metadata_retry_max`](https://www.krakend.io/docs/enterprise/async/kafka/#metadata_retry_max "Click to copy link to metadata_retry_max") number When a disconnection happens, the client needs to refresh its metadata to know the current state of the kafka cluster (effectively the number of attempts to reconnect) Defaults to `3` ##### [`rack_id`](https://www.krakend.io/docs/enterprise/async/kafka/#rack_id "Click to copy link to rack_id") string A name to identify the rack we are connecting from Defaults to `""` ##### [`read_timeout`](https://www.krakend.io/docs/enterprise/async/kafka/#read_timeout "Click to copy link to read_timeout") string Maximum time allowed to read from broker Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30s"` ##### [`sasl`](https://www.krakend.io/docs/enterprise/async/kafka/#sasl "Click to copy link to sasl") object Enables to authenticate to the Kafka service using user and password ##### [`write_timeout`](https://www.krakend.io/docs/enterprise/async/kafka/#write_timeout "Click to copy link to write_timeout") string Maximum time allowed to write to broker Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30s"` Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/cluster.json](https://www.krakend.io/schema/v2.13/messaging/kafka/cluster.json "JSON schema definition") Reconnection parameters The `metadata_retry_max` and `metadata_retry_backoff` play a **very important role in reconnection** in case you have network issues. By default, its values allow to recovery only from connections lost during less that 750ms (3 retries with 250ms backoff). We strongly advise incrementing the number of retries (and increasing the backoff to 1sec), if you want to protect yourself from longer disconnection times. The `cluster` field includes a `client_tls` field that allows you to authenticate with the Kafka service, as described in [Service Client TLS Settings](/docs/enterprise/service-settings/tls/#client-tls-settings). You can also use **Kafka user and password authentication** with the SASL config: ```json { "cluster": { "brokers": [ "localhost:9092" ], "sasl": { "user": "johnsmith", "password": "myp4ssword" } } } ``` ##### Fields of Kafka SASL \* required fields ##### [`auth_identity`](https://www.krakend.io/docs/enterprise/async/kafka/#auth_identity "Click to copy link to auth_identity") string Auth Identity is an (optional) authorization identity (authzid) to use for SASL/PLAIN authentication (if different from User) when an authenticated user is permitted to act as the presented alternative user. See RFC4616 for details ##### [`azure_event_hub`](https://www.krakend.io/docs/enterprise/async/kafka/#azure_event_hub "Click to copy link to azure_event_hub") boolean Kafka > 1.x should use SASL V1, except on Azure EventHub which uses V0 Defaults to `false` ##### [`disable_hanshake`](https://www.krakend.io/docs/enterprise/async/kafka/#disable_hanshake "Click to copy link to disable_hanshake") boolean Whether or not to send the Kafka SASL handshake first if enabled. You should only set this to false if you’re using a non-Kafka SASL proxy Defaults to `true` ##### [`mechanism`](https://www.krakend.io/docs/enterprise/async/kafka/#mechanism "Click to copy link to mechanism") Name of the enabled SASL mechanism Possible values are: `"PLAIN"` , `"OAUTHBEARER"` Defaults to `"PLAIN"` ##### [`password`](https://www.krakend.io/docs/enterprise/async/kafka/#password "Click to copy link to password") string Password for SASL/PLAIN authentication ##### [`scram_auth_id`](https://www.krakend.io/docs/enterprise/async/kafka/#scram_auth_id "Click to copy link to scram_auth_id") string Authz id used for SASL/SCRAM authentication ##### [`user`](https://www.krakend.io/docs/enterprise/async/kafka/#user "Click to copy link to user") string Authentication identity (authcid) to present for SASL/PLAIN or SASL/SCRAM authentication Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/sasl.json](https://www.krakend.io/schema/v2.13/messaging/kafka/sasl.json "JSON schema definition") The `group` field has details about the Kafka consumer group behaviour: ##### Fields of Kafka Consumer Details \* required fields ##### [`fetch_default`](https://www.krakend.io/docs/enterprise/async/kafka/#fetch_default "Click to copy link to fetch_default") number The default number of message bytes to fetch from the broker in each request (default 1MB). This should be larger than the majority of your messages, or else the consumer will spend a lot of time negotiating sizes and not actually consuming. Similar to the JVM’s `fetch.message.max.bytes` ##### [`heartbeat_interval`](https://www.krakend.io/docs/enterprise/async/kafka/#heartbeat_interval "Click to copy link to heartbeat_interval") string Expected time between heartbeats to the consumer coordinator when using Kafka’s group management facilities. Heartbeats are used to ensure that the consumer’s session stays active and to facilitate rebalancing when new consumers join or leave the group. The value must be set lower than Consumer.Group.Session.Timeout, but typically should be set no higher than 1/3 of that value. It can be adjusted even lower to control the expected time for normal rebalances Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"3s"` ##### [`id`](https://www.krakend.io/docs/enterprise/async/kafka/#id "Click to copy link to id") string Name of the consumer group to use ##### [`instance_id`](https://www.krakend.io/docs/enterprise/async/kafka/#instance_id "Click to copy link to instance_id") string Support KIP-345 ##### [`isolation_level`](https://www.krakend.io/docs/enterprise/async/kafka/#isolation_level "Click to copy link to isolation_level") Supports 2 modes: `read_commited` to consume and return all messages in message channel, and `read_uncommited` to hide messages that are part of an aborted transaction Possible values are: `"read_commited"` , `"read_uncommited"` Defaults to `"read_commited"` ##### [`rebalance_strategies`](https://www.krakend.io/docs/enterprise/async/kafka/#rebalance_strategies "Click to copy link to rebalance_strategies") array Priority-ordered list of client-side consumer group balancing strategies that will be offered to the coordinator. The first strategy that all group members support will be chosen by the leader. Options are: `range`, `roundrobin`, and `sticky` Defaults to `["range"]` ##### [`rebalance_timeout`](https://www.krakend.io/docs/enterprise/async/kafka/#rebalance_timeout "Click to copy link to rebalance_timeout") string Maximum allowed time for each worker to join the group once a rebalance has begun. This is basically a limit on the amount of time needed for all tasks to flush any pending data and commit offsets. If the timeout is exceeded, then the worker will be removed from the group, which will cause offset commit failures. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"60s"` ##### [`session_timeout`](https://www.krakend.io/docs/enterprise/async/kafka/#session_timeout "Click to copy link to session_timeout") string Timeout used to detect consumer failures when using Kafka’s group management facility. The consumer sends periodic heartbeats to indicate its liveness to the broker. If no heartbeats are received by the broker before the expiration of this session timeout, then the broker will remove this consumer from the group and initiate a rebalance. Note that the value must be in the allowable range as configured in the broker configuration by `group.min.session.timeout.ms` and `group.max.session.timeout.ms` Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"10s"` Schema: [https://www.krakend.io/schema/v2.13/messaging/kafka/group.json](https://www.krakend.io/schema/v2.13/messaging/kafka/group.json "JSON schema definition") ### ACK Behaviour in Kafka Kafka has no per-message ACK: acknowledging a Kafka message **commits the client’s partition offset**. KrakenD ACKs every message read, even when the response of the pipeline results in a non-successful status code. --- # [Introduction to Request and Response Manipulation](https://www.krakend.io/docs/request-response-manipulation/) One of KrakenD’s standout features is its powerful capability to manipulate requests and responses, allowing you to shape data flows to meet your application’s needs. With KrakenD, you can transform incoming requests and outgoing responses on the fly, ensuring your APIs are efficient and perfectly aligned with your business logic. Whether you need to filter sensitive information, restructure payloads, enrich responses, or enforce specific rules, KrakenD provides a robust toolkit to get the job done. Before you start digging into manipulation, identify which part of the request or response needs modification and define the transformation logic. Then, choose the right components from the range of declarative configurations to suit different manipulation tasks. There are several components at your disposal that we could group as follows: - **Request and response transformation**: Functionality to rewrite paths, filter parameters, or inject new data based on specific logic, adjust responses before they reach the client, add or remove fields, aggregate data, or change formats to match client expectations. - **Protocol mapping and performance**: Translate and reformat payload structures, ensuring automatic encoding conversion from APIs with differing formats. Employ cache or gzipped responses to increase delivery speed. - **Request and response validation**: Although not a manipulation itself, it allows you to abort requests or responses that do not have compliant data. ## Data transformation and aggregation KrakenD dynamically reshapes, filters, and transforms requests and responses, ensuring compatibility between clients and backends. This simplifies payloads, improves performance, and customizes data delivery to fit client needs. The components available to transform data are: - [API Composition and Aggregation](/docs/endpoints/response-manipulation/): Combine multiple API responses into a unified payload. - [Response Manipulation](/docs/backends/data-manipulation/): The basic operators allow you to group, filter, map, or capture responses. - [Static Manipulation](/docs/backends/martian/): Modify requests and responses with static data. For instance, hardcode authentication on an API, or add new headers to a request. - [Manipulation of collections](/docs/backends/flatmap/): Handle and manipulate arrays sequentially in payloads. In addition, the Enterprise edition allows you to set complex logic: - [Response Manipulation with a Query Language](/docs/enterprise/endpoints/jmespath/): Dynamically manipulate complex payloads with advanced query expressions. - [Response Manipulation with Functions](/docs/enterprise/endpoints/content-replacer/): Replace and mask content using pre-defined functions, like regular expressions. - [Response Manipulation with Templates](/docs/enterprise/backends/response-body-generator/): Write a template defining the response delivered to the client - [Request Manipulation with Templates](/docs/enterprise/backends/body-generator/): Write a template defining the request sent to the backend - [Global Response Header modification](/docs/enterprise/service-settings/response-headers-modifier/): Globally alter the headers returned to clients. - [Request Enrichment with GeoIP](/docs/enterprise/endpoints/geoip/): Enrich requests with geolocation data for tailored functionality. - [Workflows](/docs/enterprise/endpoints/workflows/): Sometimes you cannot do all the transformations in a single shot, and a Workflow can solve that. ## Protocol mapping and performance KrakenD transparently converts data from one format to another and even transforms protocols. It ensures smooth integration with various systems and optimizes payload sizes for better efficiency. Some functionalities covering this are: - [Interpretation of backend responses](/docs/backends/supported-encodings/): Tell the gateway what type of response it will read, and it will be able to manipulate its data automatically. - [Re-encoding of responses](/docs/endpoints/content-types/): Choose the format of responses returned to clients. It does not matter their origin, whether JSON, XML, SOAP, etc. - [Response Caching](/docs/backends/caching/): Improve performance by caching frequently used responses. - [Non-rest connectivity](/docs/enterprise/non-rest-connectivity/): Connect to Lambda functions, GraphQL, Pub/Sub, and others. On Enterprise : - [SOAP](/docs/enterprise/backends/soap/): Craft the body and XML content you will send to a SOAP service and treat it back as XML or JSON automatically. - [Gzip Compression](/docs/enterprise/service-settings/gzip/): Optimize payload size with Gzip compression - [Static server](/docs/enterprise/endpoints/serve-static-content/): Use the gateway as a static web server ## Request and response validation In addition to transforming data and protocols, you can also validate requests and responses against JSON Schema definitions or enforce size restrictions to maintain data integrity. These features ensure data compliance, protect against oversized payloads and enhance system reliability. - [JSON Schema Request Validation](/docs/endpoints/json-schema/): Validate that the payload set by the client conforms to a schema. - [Conditional requests and responses](/docs/endpoints/common-expression-language-cel/) : Allow the request to go in or out based on business rules - [Static responses on errors](/docs/endpoints/static-proxy/): Add static content to complement the final response on incomplete and degraded responses. Plus on the Enterprise : - [JSON Schema Response Validation](/docs/enterprise/endpoints/response-schema-validator/): Validate backend responses according to a schema before delivering them to the client. - [Setting Maximum Request Size](/docs/enterprise/endpoints/maximum-request-size/): Control the maximum size of the payload received ## Why manipulate requests and responses? Modern APIs must often adapt to varying client requirements, integrate with legacy systems, or comply with stringent data security and performance standards. In addition, most of the time, implementors of these APIs do not have control over the origin of the data. Request and response manipulation allows you to: - **Optimize payloads**: Remove unnecessary data or transform structures to enhance performance. Sending fewer bytes through the network increases the user experience and conversion rates. - **Enforce data standards**: Standardize payload formats, validate fields, or inject default values. - **Enhance security**: Mask or strip sensitive information before delivering it to clients or backends. - **Support dynamic use cases**: Apply custom transformations tailored to different user roles or device types. --- # [API Composition and aggregation](https://www.krakend.io/docs/endpoints/response-manipulation/) KrakenD allows you to perform several manipulations of the responses out of the box by adding them to the configuration file. You can also add your own or 3rd parties middleware to extend this behavior. KrakenD performance tests measure the operations in `nanoseconds`, and you can find the benchmark for every response manipulation in the [benchmarks section](https://github.com/luraproject/lura/blob/master/docs/BENCHMARKS.md#response-manipulation) The following manipulations are available by default: ## Aggregation and merging When you have more than one `backend` connected to an `endpoint` that **is not** using the `no-op` encoding, the gateway **aggregates and merges** the responses from all backends automatically in the final response. For instance, imagine you have three different API services exposing the resources `/a`,`/b`, and `/c`, and you want to disclose them all together in the KrakenD endpoint `/abc`. This is what you would get: ![](/images/documentation/krakend-merge.png "REST to Graphql") The merge operation chooses user experience and responsiveness first. It makes its *best effort* to get all the necessary parts from the involved backends and return the composed object as soon as possible. KrakenD marks the result of the merging operation with the `X-KrakenD-Completed` header, being `true` if all backends succeeded or `false` if some failed. When none succeeded, the gateway returns a `500` status code to the user. The configuration for the image above could be like this: ```json { "endpoints": [ { "endpoint": "/abc", "timeout": "800ms", "method": "GET", "backend": [ { "url_pattern": "/a", "encoding": "json", "host": [ "http://service-a.company.com" ] }, { "url_pattern": "/b", "encoding": "xml", "host": [ "http://service-b.company.com" ] }, { "url_pattern": "/c", "encoding": "json", "host": [ "http://service-c.company.com" ] } ] } ] } ``` ### Merging timeouts Keep in mind that to avoid any degraded user experience, KrakenD won’t be stuck forever until all the backends decide to respond. In a gateway **failing fast is better than succeeding slowly**, and KrakenD will make sure this happens as it will **apply the timeout policy**. It will protect your users during high load peaks, network errors, or other problems that stress your backends. The `timeout` value can be introduced inside each endpoint or globally, placing `timeout` at the root of the configuration file. The most specific definition always overwrites the generic one. #### What happens when the timeout is triggered, or some backend fails? If KrakenD waits for the backends to respond and the timeout is reached, the response will be incomplete and missing any data it couldn’t fetch before the timeout happened. On the other hand, all the parts that the gateway could effectively retrieve before the timeout occurred will appear in the response. If the response has missing parts, the cache header won’t exist, as we don’t want clients to cache incomplete responses. At all times, the `X-Krakend-Completed` header contains a boolean telling you if all backends returned their content (`x-krakend-completed: true`) or it’s a partial response (`x-krakend-completed: false`). ### Merge example Imagine an endpoint with the following configuration: ```json { "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: ```json { "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 ```json { "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: ```json { "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 an endpoint, you can choose to show only a subset of the fields coming from the response of your backends. You might want to use this functionality for several reasons, but we strongly encourage you to use it to save users’ bandwidth and decrease load and render times. There are two different strategies you can use to filter content: - [**Deny list**](/docs/backends/data-manipulation/#deny) - [**Allow list**](/docs/backends/data-manipulation/#allow) See [filtering documentation](/docs/backends/data-manipulation/#filtering) ## Grouping You can group (or encapsulate or wrap) your backend responses inside different objects. In other words, 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 places the response inside. Encapsulating backend responses inside each group is interesting when different backend responses can have colliding key names (e.g: all responses contain an `id` with different values). When you consume aggregated content, use the `group` strategy. See [grouping documentation](/docs/backends/data-manipulation/#grouping) ## Mapping (renaming) KrakenD can also manipulate the name of the fields of the generated responses, so your composing 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. See [mapping documentation](/docs/backends/data-manipulation/#mapping) ## Target (or capturing) It is frequent in many API implementations that the vital data is always encapsulated inside a generic field like `data`, `response`, or `content`, along with other fields showing the status code and metadata. Sometimes we neither want to let the client handle this nor drag this first-level container through all the configurations. When setting a `target` in your backend, these generic containers (the target) disappear, and all content is extracted to the root as if it never existed. As this capturing takes place before other options like `allow` or `mapping`, you don’t need to use nesting. See [target documentation](/docs/backends/data-manipulation/#target) ## Collection -or Array- manipulation KrakenD expects all backends to return objects in the response. There are times when the whole response of the backend comes inside an array, and other times when you need to do operations over fields that are arrays themselves. In any case, manipulations over arrays work differently than the objects. See [collections documentation](/docs/backends/data-manipulation/#collections) ## Query language manipulation The most powerful option to manipulate content without scripts or plugins is via the JMESpath component, which allows you to apply changes using a JSON query language. For example: *return the name of all the students older than 18*: ```js students[?age > `18` ].name ``` See [Advanced query language manipulation Enterprise](/docs/enterprise/endpoints/jmespath/) ## Content replacement with functions Another Enterprise feature is the `modifier/response-body`, which allows you to search objects in the responses and apply replacement functions, like regular expression, trimming, lowercase, etc. For instance: ```json { "url_pattern": "/card/{user}", "extra_config": { "modifier/response-body": { "modifiers": [ { "regexp": { "@comment": "Ridiculous card masking. Take 4 digits and remove the rest. Credit card is inside a data object.", "field": "data.credit_card", "find": "(^\\d{4})(.*)", "replace": "${1}-XXXX" } } ] } } } ``` See [Content Replacer Enterprise](/docs/enterprise/endpoints/content-replacer/) --- # [Data Manipulation](https://www.krakend.io/docs/backends/data-manipulation/) This page describes the most basic options to manipulate the content you receive from the `backend` before delivering it to the endpoint to [aggregate data from all backends](/docs/endpoints/response-manipulation/#aggregation-and-merging). Before you begin… See what kind of response you are getting from your backend in the first place. If you get the response inside an object `{}`, you can apply the manipulations right away. But if you get an array `[]`, see how to manipulate **collections** below. ## Filtering When you offer a KrakenD endpoint, you can decide whether to return all the fields from the backend (default behavior) or specify which ones are allowed through an allow or deny list. You might want to use this functionality for many different reasons. Still, we strongly encourage you to consider **using it frequently to save the user’s bandwidth**, provide the client what is needed, and decrease the load and render times. You can use 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 with the outdated term `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.: ```json { "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`. Also worth mentioning, the operator works with **objects only** and not with arrays. When working with collections `[]`, see the special case [manipulating arrays](/docs/backends/flatmap/). For instance, `{ "a": [ { "a1": 1 } ] }` does not work as `a.a1` because `a1` is inside an array, but `{ "a": { "a1": 1 } }` would work. Working with arrays Deny list and allow list filters expect objects. When working with collections, see the [flatmap approach](/docs/backends/flatmap/) #### Deny list example We will use the [JSONPlaceholder](https://jsonplaceholder.typicode.com/) fake API to see live the output of the backend. We want to set up a KrakenD endpoint that returns the **posts for a specific user**, but we’ve seen that the [backend response](https://jsonplaceholder.typicode.com/posts/1) contains too much data since our use case do not need the `body` and `userId` fields and we want a lighter and faster response. The KrakenD endpoint to accept URLs like`/posts/1` is defined as follows: ```json { "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: ```json { "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit" } ``` [Compared with the backend response](https://jsonplaceholder.typicode.com/posts/1), you’ll see that the fields `body` and `userId` are no longer there. ### 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. ```json { "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: ```json { "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit" } ``` Just exactly as we did with the deny list. ### Allow list or deny list? When filtering, you need to choose between the deny list and the allow list. The two operations cannot coexist as their behaviors are contradictory. Nothing stops you from playing for a while and seeing what happens if you mix them, but make sure it’s only experimentation! Strictly from a performance point of view, the deny list is slightly fastest than the allow list. ## 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. Encapsulating backend responses inside each own group is especially interesting when different backend responses can have colliding key names (e.g., all responses contain an `id` with different values). When grouping different backend responses **don’t share the same group name**, as the slowest backend would overwrite the response with the same group. Group names are supposed to be unique for each backend in the same endpoint but this is not enforced. ### 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`. ```json { "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: ```json { "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`: ```json { "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: ```json { "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`: ```json { "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: ```json { "updated":"2010-01-07T19:58:42.949Z", "totalItems":800, "startIndex":1, "itemsPerPage":1, "items":[] } ``` We need this KrakenD configuration: ```json { "endpoint": "/foo", "method": "GET", "backend": [ { "url_pattern": "/bar", "target": "data" } ] } ``` ## Moving, extracting, or flattening There will be times when you want to move one item to another place, or flatten its structure. In these cases you need to use the [flatmap component](/docs/backends/flatmap/). For instance, you have a nested response like the following: ```json { "shipping_id": "f15f8c62-8c63-46de-a7f6-a08f131848c5", "zone": { "state": "NY", "zip": "10001" } } ``` And you would like to **extract out its fields**, like this: ```json { "shipping_id": "f15f8c62-8c63-46de-a7f6-a08f131848c5", "shipping_state": "NY", "shipping_zip": "10001" } ``` You would need a configuration like this: ```json { "backend": [{ "url_pattern": "/shipping", "extra_config": { "proxy": { "flatmap_filter": [ { "type": "move", "args": ["zone.state","shipping_state"] }, { "type": "move", "args": ["zone.zip","shipping_zip"] }, { "type": "del","args": ["zone"] } ] } } }] } ``` See how the [flatmap filter](/docs/backends/flatmap/) works for more options. ## Collections Working with collections (or arrays) is a special manipulation case. There are 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`) ### When the Backend response is inside an array KrakenD expects all backends to return an object as the response as the default behavior. For instance, a JSON response containing an object comes encapsulated in curly braces `{}`. E.g.: ```json { "a": true, "b": false } ``` When your API does not return an object but a collection (`[]` or array) you need to declare it explicitly with `"is_collection": true` so that KrakenD can convert it to an object for further manipulation. An example of a JSON collection response is: ```json [ {"a": true }, {"b": false} ] ``` In such cases, add inside the `backend` key the property `"is_collection": true` so KrakenD can convert this collection to an object. Automatic detection of arrays The use of `is_collection` can be avoided when the backend uses as `encoding` the value `safejson`. See [supported encodings](/docs/backends/supported-encodings/) By default, KrakenD adds the key `collection` in the response, e.g.: ```json { "collection": [ {"a": true }, {"b": false} ] } ``` You can rename the default key name `collection` to something else using the `mapping` attribute (docs above, the example below), but you can also **return directly the array** to the end-user, without any wrapping, if the endpoint has `"output_encoding": "json-collection"`. The following is a real example based on a [collection response](http://jsonplaceholder.typicode.com/posts), copy and paste to test in your environment: ```json { "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: ```json { "myposts": [ { }, { } ] } ``` ### When you need to manipulate arrays All the data manipulation operations (such as the allow list, deny list, etc.) expect to find objects in the response `{}`. When there are arrays instead, KrakenD needs a special configuration that internally flattens this structure: See [Manipulating arrays - flatmap](/docs/backends/flatmap/) ## More advanced manipulations If you need more sophisticated manipulation options, there are different approaches you can use: - Through a [Query Language](/docs/enterprise/endpoints/jmespath/) Enterprise - Through a [template](/docs/enterprise/backends/response-body-generator/) Enterprise - Through [Response modifier plugins](/docs/extending/plugin-modifiers/) - Very performant, requires compilation - Through [Lua scripting](/docs/endpoints/lua/) - Less performant, does not require compilation --- # [Replace content with regular expressions and other functions (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/content-replacer/) The `modifier/response-body` allows you to change the content of any desired fields using several modifiers, from regular expression replacement to simpler modifiers like trimming characters or converting the case. It works by declaring an array of `modifiers` that KrakenD executes sequentially. You can add multiple modifiers to get the desired output. The modifiers declare an explicit `field` where you want to apply the modifications, and the value of these fields **must be a string** in all cases. If you try to replace content on integers, objects, arrays, etc. the modifier won’t apply any change. Modifiers can change fields located at the root of the response but also in nested objects. When working with nested objects, the syntax does not allow you to traverse arrays in the path (e.g.: `"data": [{"user":"Mary"}]` is not a response a modifier can alter). Still, you can traverse objects (e.g.: `"data": {"user":"Larry"}`) with any level of depth you need, using the **dot notation**. In the latter example, you could have a `field: data.user` and modify the `Larry` value, but you wouldn’t be able to modify the value of `Mary` in the first example. The modifiers work both in the `endpoint` and `backend` sections, allowing you to change content before or after the merge operation. ## Configuration The configuration is straightforward and requires you to declare the different `modifiers` you want to apply. The `modifiers` array declares **one modifier per object**, and each modifier requires a different set of fields that you can check below. You will need only one modifier in most cases, but you can add as many as needed. Operations execute sequentially; the one declared at the top of the configuration is the first to apply, and then it moves down. Remember that modifiers skip non-string values. Here’s a simple example with a single operation to convert to uppercase and lowercase two specific fields: ```json { "url_pattern": "/user/{user}", "extra_config": { "modifier/response-body": { "modifiers": [ { "upper": { "@comment": "We want all surnames uppercased, like SMITH", "field": "surname" } }, { "lower": { "@comment": "We want the address in lower case", "field": "address" } } ] } } } ``` See the different modifiers you can use below. ### Regular expression modifier The most powerful modifier is the regular expression modifier `regexp` that allows you to replace strings using any regular expression you need. It needs the field name where you want to apply the regular expression, the pattern you want to find, and its replacement. Here’s an example: ```json { "url_pattern": "/card/{user}", "extra_config": { "modifier/response-body": { "modifiers": [ { "regexp": { "@comment": "Ridiculous card masking. Take 4 digits and remove the rest. Credit card is inside a data object.", "field": "data.credit_card", "find": "(^\\d{4})(.*)", "replace": "${1}-XXXX" } } ] } } } ``` Notice that our `field` uses the dot `.` notation, because we want to modify a nested element, so we have passed `data.credit_card`. Then, the `find` contains the regular expression, and the `replace` is the new output we want to produce. The `$1` in the replace references the first capturing group (the 1st parenthesis on find) we have set in the regular expression. This is what the response returns now: ```json { "data": { "credit_card": "1234-XXXX" } } ``` Versus the original response from the backend: ```json { "data": { "credit_card": "1234-56789-1234-5678" } } ``` These are the properties of the modifier: ##### Configuration of the regexp modifier \* required fields ##### [`modifiers`](https://www.krakend.io/docs/enterprise/endpoints/content-replacer/#modifiers "Click to copy link to modifiers") array of objects A list of modifiers you would like to apply to specific fields. The modifiers are evaluated and applied in sequential order. Defaults to `[]` Each item of modifiers accepts the following properties: ##### [`regexp`](#modifiers-regexp "Click to copy link to regexp") object A list of regular expressions you would like to apply to specific fields. The expressions are evaluated and applied in sequential order. Example: `{"field":"data.credit_card","find":"(^\\d{4})(.*)","replace":"${1}-XXXX"}` ##### [`field`](#regexp-field "Click to copy link to field") * string The field you want to modify. The field supports dot notation to access nested objects (it does not work with nested arrays). Example: `"data.credit_card"` ##### [`find`](#regexp-find "Click to copy link to find") * string The find expression or literal you want to use. The syntax of the regular expressions accepted is the same general syntax used by Perl, Python, and other languages. More precisely, it is the syntax accepted by RE2 and described at [https://golang.org/s/re2syntax](https://golang.org/s/re2syntax) ##### [`replace`](#regexp-replace "Click to copy link to replace") * string The literal string or expression you want to use as a replacement. Use `$` signs to replace captured groups. Example: `"${1}-XXXX"` Schema: [https://www.krakend.io/schema/v2.13/modifier/response-body.json](https://www.krakend.io/schema/v2.13/modifier/response-body.json "JSON schema definition") ### Literal modifier Replaces in the desired `field` a text by another using a literal match (case sensitive). Here’s an example: ```json { "url_pattern": "/price", "extra_config": { "modifier/response-body": { "modifiers": [ { "literal": { "@comment": "Use the currency name instead of symbol", "field": "price", "find": "€", "replace": "EUR" } } ] } } } ``` ##### Configuration of the literal modifier \* required fields ##### [`modifiers`](https://www.krakend.io/docs/enterprise/endpoints/content-replacer/#modifiers "Click to copy link to modifiers") array of objects A list of modifiers you would like to apply to specific fields. The modifiers are evaluated and applied in sequential order. Defaults to `[]` Each item of modifiers accepts the following properties: ##### [`literal`](#modifiers-literal "Click to copy link to literal") object A literal replacement of strings (case sensitive). Example: `{"field":"data.example","find":"findme","replace":"replaceme"}` ##### [`field`](#literal-field "Click to copy link to field") * string The field you want to modify. The field supports dot notation to access nested objects (it does not work with nested arrays). Example: `"data.credit_card"` ##### [`find`](#literal-find "Click to copy link to find") * string The exact string you want to replace. ##### [`replace`](#literal-replace "Click to copy link to replace") * string The replacement string you want to use for all matching results. Schema: [https://www.krakend.io/schema/v2.13/modifier/response-body.json](https://www.krakend.io/schema/v2.13/modifier/response-body.json "JSON schema definition") ### Uppercase modifier Replaces a field value with all Unicode letters mapped to their upper case. ```json { "url_pattern": "/user", "extra_config": { "modifier/response-body": { "modifiers": [ { "upper": { "field": "surname" } } ] } } } ``` ##### Configuration of the upper modifier \* required fields ##### [`modifiers`](https://www.krakend.io/docs/enterprise/endpoints/content-replacer/#modifiers "Click to copy link to modifiers") array of objects A list of modifiers you would like to apply to specific fields. The modifiers are evaluated and applied in sequential order. Defaults to `[]` Each item of modifiers accepts the following properties: ##### [`upper`](#modifiers-upper "Click to copy link to upper") object Converts a given string to uppercase. Example: `{"field":"surname"}` ##### [`field`](#upper-field "Click to copy link to field") * string The field you want to modify. The field supports dot notation to access nested objects (it does not work with nested arrays). Example: `"user.surname"` Schema: [https://www.krakend.io/schema/v2.13/modifier/response-body.json](https://www.krakend.io/schema/v2.13/modifier/response-body.json "JSON schema definition") ### Lowercase modifier Replaces a field value with all Unicode letters mapped to their lower case. ```json { "url_pattern": "/user", "extra_config": { "modifier/response-body": { "modifiers": [ { "lower": { "field": "surname" } } ] } } } ``` ##### Configuration of the lower modifier \* required fields ##### [`modifiers`](https://www.krakend.io/docs/enterprise/endpoints/content-replacer/#modifiers "Click to copy link to modifiers") array of objects A list of modifiers you would like to apply to specific fields. The modifiers are evaluated and applied in sequential order. Defaults to `[]` Each item of modifiers accepts the following properties: ##### [`lower`](#modifiers-lower "Click to copy link to lower") object Converts a given string to lowercase. Example: `{"field":"surname"}` ##### [`field`](#lower-field "Click to copy link to field") * string The field you want to modify. The field supports dot notation to access nested objects (it does not work with nested arrays). Example: `"user.surname"` Schema: [https://www.krakend.io/schema/v2.13/modifier/response-body.json](https://www.krakend.io/schema/v2.13/modifier/response-body.json "JSON schema definition") ### Trim modifier Removes from a field all characters included in the `find` string. ```json { "url_pattern": "/hello-world", "extra_config": { "modifier/response-body": { "modifiers": [ { "trim": { "field": "message", "find": "¡!", "@comments": "Removes exclamation from message, converting '¡¡¡Hello world!!!' to 'Hello world'" } } ] } } } ``` ##### Configuration of the trim modifier \* required fields ##### [`modifiers`](https://www.krakend.io/docs/enterprise/endpoints/content-replacer/#modifiers "Click to copy link to modifiers") array of objects A list of modifiers you would like to apply to specific fields. The modifiers are evaluated and applied in sequential order. Defaults to `[]` Each item of modifiers accepts the following properties: ##### [`trim`](#modifiers-trim "Click to copy link to trim") object Removes a specific set of characters from the beginning and the end of the string. Example: `{"field":"description","find":"\n"}` ##### [`field`](#trim-field "Click to copy link to field") * string The field you want to modify. The field supports dot notation to access nested objects (it does not work with nested arrays). Example: `"description"` ##### [`find`](#trim-find "Click to copy link to find") * string The sequence of characters you want to trim in the response. Examples: `"\r\n"` , `"\n"` , `"\t"` , `" "` Schema: [https://www.krakend.io/schema/v2.13/modifier/response-body.json](https://www.krakend.io/schema/v2.13/modifier/response-body.json "JSON schema definition") ## Migration from old plugin Prior to KrakenD v2.9 part of this functionality was offered through a plugin. Now you can use the native component that offers more functionality and performance without needing to load any plugins. Here are the changes you would need to do to an existing configuration: ```diff { "extra_config": { - "plugin/req-resp-modifier": { - "name": [ - "content-replacer" - ], - "content-replacer": { - "data.credit_card": { + "modifier/response-body": { + "modifiers": [ + { "@comment": "Ridiculous card masking. Take 4 digits and remove the rest. Credit card is inside a data object.", + "field": "data.credit_card", "find": "(^\\d{4})(.*)", - "replace": "${1}-XXXX", - "regexp": true + "replace": "${1}-XXXX" }, - "message": { + { + "field": "message", "@comment": "Replace '6 items left' with '6' on the message field", "find": " items left", "replace": "" } - } + ] } } } ``` The summmary of changes required are: - Rename the namespace `plugin/req-resp-modifier` to `modifier/response-body` - Remove the `name` property - Rename `content-replacer` to `modifiers` and instead of an object convert it into an array - For each key you had, create an object using one of the available modifiers - If this was the last `plugin/xxx` entry in your configuration, remove the configuration block `plugin` at the root of the configuration If you have doubts, please get in touch with support. --- # [Response manipulation with query language (JMESPath) (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/jmespath/) If you’d like to apply **advanced manipulations** before you return the API data, the `modifier/jmespath` allows you to apply JSON query language expressions. Fine-tune the final content delivered to the user, and even add the possibility to add small logic chunks. The JMESPath query language allows you to select, slice, filter, map, project, flatten, sort, and all sorts of operations. To get familiar with the language, we recommend reading the [JMESPath Tutorial](https://jmespath.org/tutorial.html) and the [JMESPath examples](https://jmespath.org/examples.html). There is also an [Interactive JMESPath playground](https://play.jmespath.org/). Let’s better illustrate the query language with an example. Your backend returns the following content: ```json { "students": [ {"name": "Bill", "age": 23 }, {"name": "Mary", "age": 16 }, {"name": "Jessica", "age": 19 } ] } ``` But you only need the names of those students who are over 18. You could apply an expression like this: ```js students[?age > `18` ].name ``` And the final output would be: ```json ["Bill", "Jessica"] ``` ## How JMESPath works After KrakenD has fetched all the data from all the involved backends in an endpoint, the merge operation takes place. Once KrakenD merges all the data from the backends in a final object, it uses it as input of the JMESPath query expression. The output of this component is then returned to the user. Combine with other manipulation options You can use the JMESPath component in combination with other manipulation options. For instance, you can apply [Lua `pre` and Lua `post` functions](/docs/enterprise/endpoints/lua/) before this component starts. ## JMESPath configuration The configuration to set in KrakenD is straightforward as it only needs to receive the expression to execute in the endpoint or the backend. ```json { "endpoint": "/advanced-manipulation", "extra_config": { "modifier/jmespath": { "expr": "students[?age > `18` ].name" } } } ``` ##### Fields of JMESPath: Response manipulation with query language \* required fields ##### [`expr`](https://www.krakend.io/docs/enterprise/endpoints/jmespath/#expr "Click to copy link to expr") * string The JMESPath expression you want to apply to this endpoint. Schema: [https://www.krakend.io/schema/v2.13/modifier/jmespath.json](https://www.krakend.io/schema/v2.13/modifier/jmespath.json "JSON schema definition") ### Output wrappers As it happens with the rest of the content in KrakenD, depending on the type of content that the JMESPath expression returns, KrakenD will wrap it or not inside a key: - `collection`: When the backend returns an array, KrakenD adds the `collection`. You can remove this `collection` key in the final response by setting the `output_encoding` in the endpoint to `json-collection` ([see content types](/docs/endpoints/content-types/)) - `content`: when the JMESPath expression returns just a **string**, a **boolean**, or a **number**, then KrakenD adds a `content` wrapper. You can remove it automatically when setting the `output_encoding` to `string`. Reusing the same example above, the following configurations have these effects: ```json { "endpoint": "/advanced-manipulation", "extra_config": { "modifier/jmespath": { "expr": "students[?age > `18` ].name" } } } ``` Returns: ```json { "collection": ["Bill", "Jessica"] } ``` While if we add the `output_encoding` expecting a JSON collection: ```json { "endpoint": "/advanced-manipulation", "output_encoding": "json-collection", "extra_config": { "modifier/jmespath": { "expr": "students[?age > `18` ].name" } } } ``` Then we get the collection directly in the output: ```json ["Bill", "Jessica"] ``` The same happens with the `"output_encoding": "string"` when the query returns a single value. --- # [Response manipulation with templates (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/response-body-generator/) The **response body generator** lets you write a whole new payload [using a template](/docs/enterprise/configuration/templates/) that has access to all the response data. In addition to the payload from the backend, you can incorporate other information such as headers, status codes, query strings, or URL parameters. You can add validations, data transformation, and other valuable operations as you work with a template. ## Modify the body with templates The `modifier/response-body-generator` lets you change the content from a `backend` response or the aggregated result after merging in an `endpoint`. You can manipulate data with any encoding you choose, including `no-op`. You can access the status codes and the headers in the template when using `no-op` (and only in this case). The data you return is defined by a [template](/docs/enterprise/configuration/templates/) that you write in an external file or embed inline in the configuration as a base64 string. For example, the following `response.json.tmpl` represents a body in JSON format you would return from your backend and takes data from additional places: ```go-text-template { "data": {{ toJson .resp_body }}, "status_code": {{ toJson .resp_status }}, "headers": {{ toJson .resp_headers }}, } ``` The `.resp_body` is what the user would have gotten if they had contacted the backend directly. Instead, in the example above, we are putting it in a new key, `data`, and adding the status code and headers in the response (the endpoint must be `no-op` in this case). The `toJson` function converts the response field structure into a JSON object. The example template renders into a JSON format, but you could use any other form. The configuration option `content_type` lets the consumer know (or additional KrakenD components) how to treat this content. In this case, it should be `application/json` to match our written content. ## Configuration for the response body generator Manipulation skipped on errors When you use this component in and endpoint with aggregation, if one of the backends fail, the manipulation is skipped. If you’d like to apply manipulations even when there are backends failing, consider creating an intermediate endpoint that does the aggregation, and another one to apply the template. In the case of backends, if you use `no-op`, you can check the status codes and condition the responses. The response body generator modifier has the following options available: ##### Fields of Template-based body generator \* required fields Minimum configuration needs **one of**: `path` , or `template` ##### [`content_type`](https://www.krakend.io/docs/enterprise/backends/response-body-generator/#content_type "Click to copy link to content_type") string The `Content-Type` you are generating in the template, so it can be recognized by whoever is using it. Examples: `"application/json"` , `"application/xml"` , `"text/xml"` Defaults to `"application/json"` ##### [`debug`](https://www.krakend.io/docs/enterprise/backends/response-body-generator/#debug "Click to copy link to debug") boolean When `true`, shows useful information in the logs with `DEBUG` level about the input received and the body generated. Do not enable in production. Debug logs are multiline and designed fore developer readibility, not machine processing. Defaults to `false` ##### [`path`](https://www.krakend.io/docs/enterprise/backends/response-body-generator/#path "Click to copy link to path") string The path to the Go template file you want to use to craft the body. Example: `"./path/to.tmpl"` ##### [`template`](https://www.krakend.io/docs/enterprise/backends/response-body-generator/#template "Click to copy link to template") string An inline base64 encoded Go template with the body you want to generate. This option is useful if you want to have the template embedded in the configuration instead of an external file. ##### [`variables`](https://www.krakend.io/docs/enterprise/backends/response-body-generator/#variables "Click to copy link to variables") object An object traversable with dots with any content you want to be available to the template. Schema: [https://www.krakend.io/schema/v2.13/modifier/body-generator.json](https://www.krakend.io/schema/v2.13/modifier/body-generator.json "JSON schema definition") ## Template variables When you write the template’s content, you do it in a [template](/docs/enterprise/configuration/templates/). The template engine parses the content and replaces variables with the format `{{ .variable }}`, but you can use all the power of templates and introduce conditionals, loops, and other checks. The following variables are available in the template you will use to construct a body: Response data: - `.resp_headers` - `.resp_body` - `.resp_status` Request data: - `.req_params` - `.req_headers` - `.req_querystring` - `.req_path` - `.req_body` - `.req_method` See their usage below. ### `.resp_headers` (`no-op` required) The variable `{{ .resp_headers }}` contains all the response headers returned by the `backend` when you use `no-op`, in its **canonical MIME form**. The variable will be empty if you use another encoding in the `output_encoding`. The canonicalization converts the first letter and any letter following a hyphen to upper case; the rest are converted to lowercase. For example, the canonical key for `accept-encoding` is `Accept-Encoding`. Given the nature of headers and the specificities of templates, you should know a few things to work efficiently with them. Headers can be according to its RFC specification **multi-value**. It means that a single header key can coexist with several values, or said otherwise, you can pass the same header several times in a request or response. This is why the variable `.resp_headers` is a map with all the header keys, but it uses an array for their values, even if there is only one. For instance, the backend sends the header `Cache-Control: "public, max-age=60"`. To use a familiar language with most readers, if it were Javascript notation, our variable would be `resp_headers["Cache-Control"][0] = "public, max-age=60"`. In a template, that would be like `{{ .resp_headers.Cache-Control.0}}`, only that **this syntax is wrong**. The word `Cache-Control` contains a minus sign `-` that is an operator. Accessing a key in a map with special characters needs the `index` function. In all, here’s an example that returns all the headers in the response and the specific header `Cache-Control` (which is an array): ```go-text-template { "all_headers": {{ toJson .resp_headers }}, "cache-control": {{ toJson (index .resp_headers "Cache-Control") }} } ``` ### `.resp_status` (`no-op` required) The response’s status code is an integer returned by the `{{ .resp_status }}` variable when you use `no-op` encoding. The response status allows us to do **conditional responses**. Sometimes, we want to return a **custom template** when the status code is unexpected. Here’s an example of a template that checks that the status code is a `200`, and if it is not, returns a different JSON object: ```go-text-template {{- if eq 200 .resp_status -}} {{- toJson .resp_body -}} {{- else -}} { "system": "is down" } {{- end -}} ``` ### `resp_body` The response body contains the data from the response. It works with any `encoding`. When the encoding is `no-op`, KrakenD will attempt to read the body (despite it’s been told to do no-operation) and parse it following the `Content-Type` indications of the backend. If the backend does not return a `Content-Type` header or an unsupported type, you won’t be able to access its data. The configuration option `content_type` is what you return to the end-user, and it does not need to match what KrakenD gets from the backend header `Content-Type`, so they are entirely different things. The `.req_body` is initially empty unless the following requirements are met: The following `Content-Type` headers are the ones KrakenD can parse to extract its data for working with it: - `application/json` (default) - `application/xml` - `text/xml` - `text/plain` Here is an example of a template that takes parts of a response body to build a new one: ```go-text-template { "message": "{{.resp_body.some_string}}", "message2": {{toJson .resp_body.some_string}}, "number": {{.resp_body.some_integer}}, "array": {{ toJson .resp_body.some_array}} } ``` Notice that since you are writing a JSON response by hand, you must **take care of quotes**, or use the `toJson` function to do it automatically for you. Here are a few hints: - If you directly access a variable that is not set, like `{{ .res_body.unexisting }}`, the template engine will print ``, and this can break the format if unquoted. - Instead, if you access a variable that is not set through `toJSON`, the value is `null`, a valid value for a JSON object. Another problem you might face is that you see an error like this in the logs: ```log KRAKEND ERROR: [ENDPOINT: /test] invalid character 'm' looking for beginning of value ``` The `m` stands for the word`map[...]`, which indicates that you are printing the native Go data structure in the template instead of its JSON value. ### Request variables `.req_...` In addition to the backend response, you can access parameters in the request passed by the user and inject them into the response, or even check them for conditional output. The variables `{{ .req_params }}`, `{{ .req_headers }}`, `{{ .req_querystring }}`, `{{ .req_path }}`, `{{ .req_method }}`, and `{{ .req_body }}` contain information passed in the user request. You can return to the end-user data that was contained in the original request or even make checks inside the template for conditional responses. The usage of these variables is [documented in the request generator](/docs/enterprise/backends/body-generator/#req_params), which uses the same template engine and contains numerous examples of how to access data with special characters and other useful information. ## Response body transformation example Let’s show how this works with a testable model. We want to design a JSON response from scratch for an endpoint and embed a few fields from a call to the GitHub API. The response we want to get is: ```json { "hi": "there!", "a":"a", "b":"b", "c":"c", "github": { "login":"alombarte", "blog":"https://www.krakend.io" } } ``` We want the first field hard coded; then we want a, b, and c, which is a loop, and then a few properties from a Github call, but not all. Here is the configuration (you can test it, it is fully functional): ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "endpoints": [ { "endpoint": "/test", "backend": [ { "url_pattern": "/users/alombarte", "host": ["https://api.github.com"], "extra_config": { "modifier/response-body-generator": { "path": "custom_template.tmpl", "debug": true } } } ] } ] } ``` As you can see above, there is a path `custom_template.tmpl` that we will also save next to our `krakend.json` file. It has this content: ```go-text-template {{/* pick: Fields to pick from the response body Docs: http://masterminds.github.io/sprig/dicts.html */}} {{- $returned_data := pick .resp_body "login" "blog" -}} {{/* Print the response */}} { "hi": "there!", {{- range (list "a" "b" "c" ) -}} "{{.}}": "{{.}}", {{- end -}} "github": {{ toJson $returned_data }} } ``` This is mostly understanding [templates](/docs/configuration/templates/), but let’s dissect it: - We want our colleagues to know what this does, so we add comments `{{/* .... */}}` - We set a variable `$returned_data` that uses the `pick` function, which creates a new dictionary with the selected fields. The `pick` function extracts the fields from the `.resp_body`, which contains all the fields from Github. - Then, we print the response and write the opening and closing curly braces because we will write JSON content. - The `hi there` key and value are hardcoded in the template - Then we have an iteration (`range`) over a list `["a","b","c"]` - Inside the range, we use `{{.}}`, which contains the element we are iterating (a,b, and c) - Finally, we write the `github` key, and as value, we use `toJson`, which converts the response data we selected to JSON You can test this configuration and play with it. ## Debugging the template While working with the response body generator modifier, you might find it useful to set the `debug` flag to true. This flag (that you should not use in production) outputs the following information in the console (when the [debug level](/docs/logging/) is `DEBUG`): - All the variables available in the template - The final generated body, after compiling the template and injecting the variables - The content type to send to the backend server Use the flag for faster development! But remove it in production. It is designed for developer reading of the logs (multiline content) and not for machine processing of the lines. When something is not working, **read carefully the log**, as in case the templates cannot be rendered or found (the relative path could be different than you expect), you will see lines showing the problem: ```log KRAKEND DEBUG: [BACKEND: /foo][body-generator] open ./body.json.tmpl: no such file or directory ``` ## Embedding templates in base64 You can embed the template in the configuration as a base64 instead of referencing it as an external file. There are several ways you can do this. **Written inline in the template** using flexible configuration: ```go-text-template "modifier/response-body-generator": { "template": "{{ `{ "id": "{{ .req_params.Id }}", "message": "User said {{ .req_body.text }}" }` | b64enc }}", "content_type": "application/json", "debug": true } ``` As you can see, the backtick delimiters write the template as it is, and at the end, it pipes it to the `b64enc` function. **Loaded as a partial template** with base64 encoding and flexible configuration: ```go-text-template "modifier/response-body-generator": { "template": "{{ include "body.json.tmpl" | b64enc }}", "content_type": "application/json", "debug": true } ``` Copy and paste the value from a **terminal:** Base64 encode of a template  ``` $base64 -w 0 body.json.tmpl ewogICJpZCI6ICJ7eyAucmVxX3BhcmFtcy5JZCB9fSIsCiAgIm1lc3NhZ2UiOiAie3sgLnJlcV9ib2R5Lm1lc3NhZ2UgfX0iCn0= ``` Notice that we are adding `-w 0` because we don’t want new lines that would break the configuration. --- # [Serving Static Content (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/serve-static-content/) The static filesystem component lets you return content saved in the disk to end users and updates as the filesystem changes. The component is very lightweight and straightforward, using only the standard lib. **Notice the two possible usages of this component** When used globally at the service level (`server/static-filesystem`), the component registers itself as a static web server for a set of defined paths. Whenever users request a recognized starting path (the `prefix`), the component tries to return the requested resource (CSS, JS, images, or JSON files, to name a few examples). Conversely, when the component is used in a backend (`backend/static-filesystem`), the content is treated as a regular backend that gets the content from the disk instead of a remote server, and you can use it to mock data. From a content management perspective, you have to place the static content in the filesystem where KrakenD runs, and it has to be readable by the `krakend` user. ## Static content web server When the component is used as a server (`server/static-filesystem`), it works similarly to a regular static web server; it loads content from the disk and presents it to the users. It uses the same port where the gateway runs. The component executes before the gateway’s router layer enters the scene. The implications of this statement are that if a path contains the defined `prefix`, it is processed by the web server and won’t hit any KrakenD endpoint later on unless you add that endpoint in its configuration’s `skip` section. Whether a matching URL prefix can retrieve the requested file in the filesystem or not, the request finishes the journey here anyway. It won’t be passed to the router layer to serve endpoint content, neither will it make use of any [Catchall functionality](/docs/enterprise/endpoints/catch-all/). ### Configuration as a static service The configuration requires the following entry at the `extra_config` of the root level: ```json { "version": 3, "extra_config": { "server/static-filesystem": { "prefix": "/media/", "path": "./../../assets", "skip": [ "/media/ignore/this/directory", "/media/file.json" ], } } } ``` ##### Fields of Static Filesystem \* required fields ##### [`directory_listing`](https://www.krakend.io/docs/enterprise/endpoints/serve-static-content/#directory_listing "Click to copy link to directory_listing") boolean Whether to allow directory listings or not Defaults to `false` ##### [`path`](https://www.krakend.io/docs/enterprise/endpoints/serve-static-content/#path "Click to copy link to path") * string The folder in the filesystem containing the static files. Relative to the working dir where KrakenD config is (e.g.: `./assets`) or absolute (e.g.: `/var/www/assets`). Example: `"./static/"` ##### [`prefix`](https://www.krakend.io/docs/enterprise/endpoints/serve-static-content/#prefix "Click to copy link to prefix") * string This is the beginning (prefix) of all URLs that are resolved using this plugin. All matching URLs won’t be passed to the router, meaning that they are not considered endpoints. Make sure you are not overwriting valid endpoints. When the `prefix` is `/`, then **all traffic is served as static** and you must declare a prefix under `skip` (e.g.: `/api`) to match endpoints. Example: `"/media/assets"` ##### [`skip`](https://www.krakend.io/docs/enterprise/endpoints/serve-static-content/#skip "Click to copy link to skip") array of strings An array with all the prefix URLs that despite they could match with the `prefix`, you don’t want to treat them as static content and pass them to the router. Example: `["/media/ignore/this/directory","/media/file.json"]` Schema: [https://www.krakend.io/schema/v2.13/server/static-filesystem.json](https://www.krakend.io/schema/v2.13/server/static-filesystem.json "JSON schema definition") ### Protecting your content when using the web server Because this configuration works like a web server, the user could request the directory path directly, with no associated sub-resource, or try to get hidden files. #### Directory listing The **directory listing is disabled by default**. Nevertheless you can add an `index.html` file to be presented to the user. When no `index.html` exists, the end-user receives a `403 Forbidden`. You can still enable directory listing by setting `directory_listing` to `true`. The HTML output when browsing directories without an `index.html` is not customizable and looks like this: ```html
file1.json
file2.css
directory/
``` #### Symbolic links and hidden files KrakenD serves everything in the directory you pass. It means that it will follow symlinks pointing out of the directory tree and access to hidden files and directories, meaning that directories or files starting with a period (such as `.git` or `.htpasswd`) are served. Make sure to **remove those files** if you don’t want to serve them. ## Static content as a backend When the component is used inside a backend (`backend/static-filesystem`), fetching the static content does not happen until the matching endpoint is called. In this scenario, the returned static content is to be consumed by KrakenD. The endpoint’s configuration decides if the content ends up returning to the end-user “as is” (using `no-op`) or if the content is part of a larger response and needs to be parsed in conjunction with other backends’ response (e.g: `json` encoding). As the component is inside a regular backend, you have more options to use other features. ### Configuration as static backend This is the most useful way to use the component, as it gives more flexibility and you could even add authorization, rate-limiting and many other features. To use it, include in the `extra_config` the namespace `"backend/static-filesystem"`. Your `backend` section needs the following entry: ```json { "endpoints": [ { "endpoint": "/media/*", "backend":[ { "url_pattern": "/", "host": ["http://ignored-host-field"], "extra_config": { "backend/static-filesystem": { "path": "./media-files/" } } } ] } ] } ``` The settings are: ##### Fields of Static Filesystem \* required fields ##### [`directory_listing`](https://www.krakend.io/docs/enterprise/endpoints/serve-static-content/#directory_listing "Click to copy link to directory_listing") boolean Whether to allow directory listings or not Defaults to `false` ##### [`path`](https://www.krakend.io/docs/enterprise/endpoints/serve-static-content/#path "Click to copy link to path") * string The folder in the filesystem containing the static files. Relative to the working dir where KrakenD config is (e.g.: `./assets`) or absolute (e.g.: `/var/www/assets`). Example: `"./static/"` Schema: [https://www.krakend.io/schema/v2.13/backend/static-filesystem.json](https://www.krakend.io/schema/v2.13/backend/static-filesystem.json "JSON schema definition") ## Caching content The static content is already on the disk, but you might want to add in-memory caching to reduce the accesses to it. When using the component `backend/static-filesystem`, with the combination of [Martian and the Cache component](/docs/enterprise/backends/caching/#overriding-the-expiration-time), you can save in-memory the content in the disk (avoid storing large files that would exhaust the server’s memory). For the `server/static-filesystem`, if you need to cache its contents, consider adding a CDN before KrakenD. ## Examples ### Example without manipulation The following example shows an endpoint that could reply to a request like `/logs/2023-09-09/errors` and return a log file in the filesystem without manipulation (“as is”). ```json { "endpoint": "/logs/{date}/{file}", "output_encoding": "no-op", "backend": [ { "url_pattern": "/{date}-{file}.log", "host": ["http://this-host-is-not-used-but-field-is-mandatory"], "encoding": "no-op", "extra_config": { "backend/static-filesystem": { "path": "/var/log/application/" } } } ] } ``` Notice that the `url_pattern` constructs the filename using data from the user request and the `path` in this example. ### Example with data manipulation In the following example, we can see a KrakenD endpoint fetching and aggregating data from two different endpoints. The first backend entry is an API that returns content in XML format, and the second is a raw JSON file, `static-service.json`, stored in the disk (which could be the mock of our next API service). KrakenD will parse both backends and return both calls aggregated in the endpoint. With this example, we have built a temporary service until it’s actually coded. ```json { "endpoint": "/user/{id}", "backend": [ { "url_pattern": "/user/data/{id}", "encoding": "xml", "host": ["http://some-service"] }, { "url_pattern": "/static-service.json", "encoding": "json", "host": ["http://this-host-is-not-used-but-field-is-mandatory"], "extra_config": { "backend/static-filesystem": { "path": "./" } } } ] } ``` ## Upgrading from the old static plugin (before v2.4) If you used the static plugin before EE v2.4, eliminate now all entries referring to plugins. This means: - If you don’t use additional plugins, you can get rid of the `plugin` entry in the root level. - You can delete all `plugin/http-server` objects if they only use the static-filesystem - You can delete any `static-filesystem` object in the configuration - You can delete all `plugin/http-client` with an insider `static-filesystem` entry. This can be summarized with a diff as: ```diff "version": 3, - "plugin": { - "pattern":".so", - "folder": "/opt/krakend/plugins/" - }, "extra_config": { - "plugin/http-server": { - "name": ["static-filesystem"], - "static-filesystem": { + "server/static-filesystem": { ``` And when using the http-client plugin, as: ```diff "backend": [ { "extra_config": { - "plugin/http-client": { - "name": "static-filesystem" + "backend/static-filesystem": { ``` For instance, if you had this configuration: ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "extra_config": { "plugin/http-server": { "name": ["static-filesystem", "another-plugin-maybe" ], "static-filesystem": { "prefix": "/media/", "path": "./../../assets", "skip": [ "/media/ignore/this/directory", "/media/file.json" ], } } } } ``` Since EE 2.4, it has become: ```json { "version": 3, "extra_config": { "server/static-filesystem": { "prefix": "/media/", "path": "./../../assets", "skip": [ "/media/ignore/this/directory", "/media/file.json" ], } } } ``` --- # [Caching Strategies](https://www.krakend.io/docs/backends/caching/) Caching allows you to **store backend responses in memory** to reduce the number of calls a user sends to the origin, **reducing the network traffic and alleviating the pressure on your services**. KrakenD works similarly to the default rules of a CDN to cache responses, as it adheres mostly to the [RFC-7234](https://datatracker.ietf.org/doc/html/rfc7234) (HTTP/1.1 Caching) in its implementation, and all the internals follow the decisions based on the RFC. The caching component is a capability of the default KrakenD HTTP client connecting to your upstream services, and can store content in-memory so the next request that is within a valid expiration window can be returned right away without using the network, alleviating pressure and improving times. KrakenD does not cache the final content delivered to the end-user (the endpoint output), but the response of the backend (close, but not the same). Its approach is to store individual backend responses rather than aggregated content. In an endpoint with aggregation, you will need to add the cache component to all the backends that need caching individually. If you have manipulations of content after retrieving the content, these are not cached either. Caching increases memory consumption Caching can significantly increase the load and memory consumption as the returned data is saved in memory **until its expiration period**. The size of the cache depends 100% on your backends and configuration. You will need to dimension your instance accordingly, and monitor its consumption! ## What is cached and for how long? You can only cache your backend’s `GET` methods. If you connect to a backend using `POST`, `DELETE`, `PUT`, or any other **unsafe method**, the cache is skipped, and nothing stored. The method that counts for caching or not is the one used to connect to the backend, and not the one used in the endpoint, which might be different. For instance, you could still have an endpoint offering a `POST` method to the end-user, but if the backend uses a `GET` you could cache its response. When KrakenD receives the response from the backend, checks its headers. **The `Cache-Control` header sets for how long this content is stored** in the cache. See the different headers and values that affect caching below. The cache is stored in a key that contains the final URL sent to the backend plus the combination of any existing `Vary` headers. When responses are returned to users, the memory is filled according to the header directives. Stale content is replaced by fresh content when needed automatically. ## Cache configuration To enable the caching of the backend services you only need to add in the `backend` section of your `krakend.json` the following: ```json { "backend": [{ "url_pattern": "/url-to-cache", "host": ["http://host-to-cache.example.com"], "extra_config": { "qos/http-cache": {} } }] } ``` When you don’t set any additional parameters, you are creating an individual cache bucket for this backend, with uncapped memory. A safer option that limits the amount of memory you can set to a backend would be: ```json { "backend": [{ "url_pattern": "/url-to-cache", "host": ["http://host-to-cache.example.com"], "extra_config": { "qos/http-cache": { "@comment": "Allow up to 100 cache entries or ~128MB (bytes not set exactly)", "shared": false, "max_items": 100, "max_size": 128000000 } } }] } ``` The configuration options are as follows: ##### Cache options \* required fields ##### [`qos/http-cache`](https://www.krakend.io/docs/backends/caching/#qos%2fhttp-cache "Click to copy link to qos/http-cache") object Enable in-memory caching for backend responses for as long as its `Cache-Control` header permits. Only safe methods are cached (GET or HEAD). The final backend URL and the `Vary` headers are used to create the caching key. \* Required **one of**: ( `max_size` + `max_items` ) , or ( `shared` ) , or ( ) ##### [`max_items`](#qos%2fhttp-cache-max_items "Click to copy link to max_items") integer To be released on open source on v2.10. The maximum number of items the LRU cache will store **for this cache bucket** before starting to do evictions. When `max_items` is declared, you must declare `max_size` as well. ##### [`max_size`](#qos%2fhttp-cache-max_size "Click to copy link to max_size") integer To be released on open source on v2.10. The maximum number of bytes you allow the LRU cache to store **for this cache bucket** before starting to do evictions. This is not the total cache you allow to the system, but the number of bytes you reserve to this backend (or its `shared` neighbours). will store before starting to do evictions. When `max_size` is declared, you must declare `max_items` as well. Examples: `104857600` , `1048576` ##### [`shared`](#qos%2fhttp-cache-shared "Click to copy link to shared") boolean Allows different backend definitions with this flag set to true to reuse the store between them when the request is the same. Otherwise, each backend uses a private cache context that is not accessible by other endpoints. The cache definition is unique for every backend URL + `Vary` header combination. Schema: [https://www.krakend.io/schema/v2.13/backend\_extra\_config.json](https://www.krakend.io/schema/v2.13/backend_extra_config.json "JSON schema definition") Notice that `max_size` and `max_items` must coexist. Either you declare none, or you declare both. ## Cache types, cache size and LRU The caching component allows you to declare how the cache buckets work and their relationship with the hardware. There are two important things to have in mind: - **Capped or Uncapped memory** decides what you can do with the resources of the host - **Individual or Shared cache** sets if you want to reuse content in different endpoints ### Capped vs Uncapped memory **When the memory is capped** (you set `max_items` and `max_size`) you allow every cache bucket to grow up to a defined point. You stablish limits both to the number of entries and the amount of bytes, and enable the LRU algorithm (*Least Recently Used*). The two parameters are required simultaneously (otherwise it falls back to uncapped memory). In this model, when the maximum capacity is exhausted, new cacheable content replaces the old content that has been least recently used (LRU). **This is the safest option**. **When the memory is uncapped** (you don’t set the attributes `max_items` and `max_size`), the content does not have any restriction to store content and can use and exhaust all the memory of the system. This option requires you, the developer, to plan how the cache is going to be used and make numbers. Surpassing the memory limit might set the system unstable or even crash it. ### Individual vs Shared cache **When the cache is individual** (`shared` property is `false` or missing), every backend definition uses its own cache bucket, and the contents are restricted to the associated backend. It means that other backends or endpoints can’t fetch their contents. **When the cache is shared**, instead of creating the cache bucket isolated in the backend, you use a globally available bucket to all endpoints where other backends that need the same cache key can fetch the contents without needing to ask it again. This idea is represented in the following example: ![Example of shared and individual cache](/images/documentation/diagrams/cache-options.mmd.svg) In the diagram above there are four `backend` definitions that could be anywhere across different endpoints. Backends `B` and `C` query their upstream services and store the content in a cache bucket that is constrained to their private access. On the other hand, backends `A` and `D` use a shared cache bucket, so if one of them saves a result the other can fetch the same cache key without needing to go to the origin. Shared or individual cache? Systems with high pressure and concurrency perform better with an individual cache. If the content you are caching is unlikely used often by another endpoint, leave it individual. On the other hand, if several backends are doing the same query repeatedly, you might want to share the cache bucket to improve the performance of your backend systems. ## Cache TTL, size, expiration, purge When you enable the caching module, your backends control the expiration time of the cache by setting the `Cache-Control` header. If your backends do not set the header or it is set to zero, KrakenD won’t store any content in its internal cache. The response’s content size directly impacts KrakenD memory consumption, as KrakenD does not set any hard limit. Therefore, you must be aware of the response sizes, caching times, and the hit rate of the calls. Finally, KrakenD does not offer any interface to purge the cache. The cache will cleanse itself as defined by the cache-control header, and only a restart of the service would wipe the store entirely. Nevertheless, you can make a few tweaks, as described below. ## Headers affecting the cache As we said, the only possible methods to cache are `GET` and `HEAD`, but this is only true as long as there isn’t a `Range` header in the response (multipart downloads). The cache takes into account the `Vary` header too. When present, it won’t return cached content unless all `Vary` headers match the cached ones. If a recently generated response is already saved in the cache, it will be retrieved without requiring a connection to the server. However, if the saved response is outdated or stale, any validators included in the new request will be used to give the server an opportunity to respond with a `NotModified` status. If the server provides this status, then the cached response will be returned. When storing the content in cache, it takes into account the headers `Date`, `Etag` and `Last-Modified`, and KrakenD will send to the `if-none-match` and `if-modified-since` accordingly. If a response includes both an `Expires` header and a `max-age` directive, the `max-age` directive overrides the `Expires` header, even if the `Expires` header is more restrictive. The `Cache-Control` header honors the time settings and the properties `no-store`, `only-if-cached` , `no-cache`. ## Is my content cached? KrakenD **does not provide an explicit mechanism for the developer to keep track of hits or misses** or actively manage its entries. For users with advanced needs managing caching who do not want automatic management of entries, we recommend using an advanced caching system such as Varnish Cache. One endpoint might result from several cached components, so the final content might be a sum of caches. Nevertheless, a simple rule exists to identify whether a response is cached: **look at the response time in the access logs**. When the content is generated fresh, you’ll see in the access log that a response takes a few milliseconds or even seconds, depending on your backend load and performance. But when the content is cached, you’ll see times that are at least one order of magnitude smaller (like a few microseconds or even milliseconds). You’ll have no doubt when comparing a fresh call to a cached one. In addition, there are other ways to check if backend responses are served from the cache, like: - Checking the traces to notice missing backend calls - Comparing the latency of the requests in your metrics - Check your backend logs Configurations not eligible for caching The components [Client Credentials](/docs/authorization/client-credentials/), [Lambda functions](/docs/backends/lambda/), [AMQP Consumers or Producers](/docs/backends/amqp-consumer/), [Publish/Subscribe](/docs/backends/pubsub/), and [HTTP Client plugins](/docs/extending/http-client-plugins/) **don’t support direct caching** because they use a **custom HTTP client** with extended functionality that does not use the one with caching capabilities. Generally speaking, connections with upstream services that need authentication or custom HTTP clients are not eligible to cache their responses. ## Custom directives (Stale cache, freshness) You can allow your API consumers to pass **custom directives (instructions)** in their `Cache-Control` header when they make the request. KrakenD can honor the directives `max-stale`, `max-age`, `stale-if-error`, and `min-fresh` present in the request when you add the `Cache-Control` in the `input_headers` endpoint configuration: ```json { "endpoint": "/cached-or-not", "input_headers": ["Cache-Control"] } ``` If you don’t allow the input headers to pass, KrakenD ignores the consumer’s request and returns the cached content according to the backend response. But if you do, if clients are willing to accept **stale cache** and pass the directive `max-stale` (or similarly `stale-if-error`) in the request, then a response that has exceeded its expiration time by no more than the specified number of seconds in the directive receives a stale cache. ### Overriding the expiration time As you have seen, the caching module does not accept any parameters to control the cache expiration because it relies on the input headers it finds when the response returns. But as KrakenD can transform data in many ways, you can modify the `Cache-Control` header right before the cache module picks it. The [Martian module](/docs/backends/martian/) is the component that can transform the headers from the backend as long as you don’t use the `no-op` encoding (as it does not allow manipulation). Let’s illustrate how you can do this in the following example. ```json { "version": 3, "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "endpoints": [ { "endpoint": "/cached", "backend": [ { "host": ["http://worldtimeapi.org/"], "url_pattern": "/api/timezone/Europe/Madrid", "extra_config": { "qos/http-cache": { "@comment": "This API returns a cache-control: max-age=0 so KrakenD won't cache this unless changed" }, "modifier/martian": { "header.Modifier": { "scope": ["response"], "name": "Cache-Control", "value": "max-age=60, public", "@comment": "We will change the max-age policy before KrakenD checks the content for caching. Now content is cached 60 seconds." } } } } ] } ] } ``` Notice in the configuration above how KrakenD consumed a backend without an actionable cache-control header, but it set a cache of one minute anyway. This technique might be handy when you have little control of the cache headers in the response. --- # [Array manipulation](https://www.krakend.io/docs/backends/flatmap/) The flatmap middleware allows you to **manipulate collections** (or arrays, or lists; you name it) or to **flatten objects** from the response. While the [basic manipulation operations](/docs/backends/data-manipulation/) allow you to work directly with objects, the collections require you to use this **flatmap component**. The flatmap also will enable you to **extract or move nested objects** to have a customized object structure. Looking for a Query Language manipulation? If you are an Enterprise user, you might want to use [Response manipulation with query language](/docs/enterprise/endpoints/jmespath/) instead When working with lists, KrakenD needs to flatten and expand array structures to objects to operate with them and vice versa. This process is automatically done by the flatmap component, letting you concentrate only on the type of operation you want to execute. ## When to manipulate arrays You can manipulate collections at two different stages: - When the response of a backend is received (inside its `backend` section) - After merging all the backend responses (inside the `endpoint` section) You can do simultaneous combinations to output the desired result. For instance, declare an endpoint with three backends that apply transformations independently and a final change within the endpoint after merging the three. The flatmap at the endpoint level requires multiple backends You can use the flatmap both at the `endpoint` and `backend` level, but when used in the endpoint you must have more than one backend. If you have a single backend then move the flatmap operation to the backend level. ## When to manipulate objects with flatmap The flatmap can be used on objects when the [basic data manipulation](/docs/backends/data-manipulation/) options fall short. For instance, when you need to extract and rename nested objects to the root, append, and other more sophisticated operations. ## Types of manipulations There are different types of operations you can do: - `move`: To **move**, **rename**, **embed** or **extract** items from one place to another (equivalent concepts to and [`allow`](/docs/backends/data-manipulation/#allow)) - `del`: To **delete** specific items - `append`: To **append** items from one list to the other Basic manipulation is faster Prefer basic [data manipulation](/docs/backends/data-manipulation/) operations such as [`mapping`](/docs/backends/data-manipulation/#mapping), [`target`](/docs/backends/data-manipulation/#target), [`deny`](/docs/backends/data-manipulation/#deny) or [`allow`](/docs/backends/data-manipulation/#allow) over flatmap whenever you work with objects as their computational cost is lower. Reserve the flatmap component for collections or operations that basic manipulation can’t do (such as flattening objects) ## 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`. Flatmap at the endpoint level requires +1 backend The flatmap does not load at the `endpoint` level unless there is more than one backend configured, as its purpose is to manipulate responses after the merge operation. Therefore, use it in the `backend` if you only have one. The component structure with three operations would be as follows: ```json { "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"] } ] } } } ``` Then the `flatmap_filter` is an **array** with the **list of operations to execute sequentially** (top-down). Each flatmap step takes the output of its previous execution, and every operation is defined with an object containing two properties: ##### Fields of Flatmap operation \* required fields ##### [`args`](https://www.krakend.io/docs/backends/flatmap/#args "Click to copy link to args") * array of strings The arguments passed to the operation. ##### [`type`](https://www.krakend.io/docs/backends/flatmap/#type "Click to copy link to type") * The types of operations are defined as follows. Possible values are: `"move"` , `"del"` , `"append"` Schema: [https://www.krakend.io/schema/v2.13/proxy/flatmap.json](https://www.krakend.io/schema/v2.13/proxy/flatmap.json "JSON schema definition") ### Operations and arguments The types of operations are defined as follows: - **Move**: To move or rename a collection to another. It needs two arguments. - `"type": "move"` - `"args": ["target_in_collection", "destination_in_collection"]` - **Delete**: To remove all matching patterns within a collection. It needs one or more arguments. - `"type": "del"` - `"args": ["target_in_collection_to_delete", "another_collection_to_delete", "..."]` - **Append**: To append a collection after another and return only the latter. It needs two arguments. - `"type": "append"` - `"args": ["collection_to_append", "returned_collection"]` The format of the arguments (`args`) to proceed with the operation is very simple. In short, **object nesting** is represented with **dots**, while the index of an array is represented with a **number**. Or all matching items with **wildcards**. So: - The dot operator `.` indicates a new array nesting level - The wildcard `*` matches any key (property name, collection key name, or index) - A `number` identifies the Nth-1 member of a collection, being `0` its first item. Operations always apply to \** the last item\** in the arguments. So, for instance, the deletion of `a.b.c` deletes `c` but leaves `a.b` in the response. ### Notation by example We will use an elementary JSON structure as an example of data representation. See below: ```json { "a": [ { "b1": [ { "c": 1, "d": "foo" }, { "c": 2, "d": "bar" } ], "b2": true }, { "b1": [ { "c": 3, "d": "vaz" } ] } ] } ``` #### Observations Notice from this example that… - `a` and `b1` contain arrays (`[...]`) with objects inside. - `b2`, `c` and `d` are not arrays - Since `a` is an array (`"a": []`), we need to use the flatmap component. If it were an object (`"a": {}`), we would use [deny or allow](/docs/backends/data-manipulation/) #### Representing some values Now that we are familiar with the structure let’s represent some values: | Notation | Value | |--------------|---------------------------------------------------------------------------------------------------------------------| | `a` | The content of *a*: `[{"b1": [{"c": 1,"d": "foo"},{"c": 2,"d": "bar"}],"b2": true}, {"b1": [{"c": 3,"d": "vaz"}]}]` | | `a.1` | Second object of *a* key: `{"b1": [ { "c": 3, "d": "vaz" } ]}` (first objects starts at 0) | | `a.0.b1.0.d` | `foo` | | `a.1.b1.0.d` | `vaz` | | `a.*.b1.*.d` | 3 matches of `d` in this path: `foo`, `bar`, `vaz` | | `a.*.*.*.d` | 3 matches of `d` in this path: `foo`, `bar`, `vaz` | #### Practical examples regarding operations Some individual operations **on the example structure above**: | Target | Destination | Correct? | Comments | |----------------|------------------------|----------|------------------------------------------------------------------------| | `"a.*.b1.*.c"` | `"a.*.b1.*.d"` | Yes | Rename `c` to `d` | | `"a.*.b1.*.c"` | `"a.*.c"` | No | Missing level | | `"a.b1.c"` | `"c"` | No | Missing array after `a` | | `"a.0.b1.0.c"` | `"c"` | Yes | Extract only `c` from the first and first items | | `"a.*.b1.c"` | `"c"` | No | Incorrect target, `b1` has an array surrounding `c` | | `"a.*.b1.c"` | `"a.*.b1.*.d.*.e"` | No | Incorrect target, `b1` has an array surrounding `c` | | `"a.*.b1.*.c"` | `"a.*.b1.*.c.d.e.f.g"` | Yes | Add additional levels | | `"a.*.b1.*.c"` | `"a.*.x.*.c"` | No | Incorrect, renaming to an element `x` that is not in the last position | | `"a.*.b1.*.c"` | `"a.*.x.*.c.d.e.f.g"` | No | Incorrect, renaming to an element `x` that is not in the last position | | `"a.*.b1.*.c"` | `"a.*.b1.*.d.*.e"` | No | Incorrect, destination path has more wildcards than source path | ## Configuration examples The following examples demonstrate how to modify a collection or objects using flatmap. ### Example: Extract objects to another level We have a backend that provides the following response as Input for flatmap: **Input**: ```json { "shipping_id": "f15f8c62-8c63-46de-a7f6-a08f131848c5", "zone": { "state": "NY", "zip": "10001" } } ``` And we want to **extract fields** from `zone`, rename them, and **place them in the root**, like this: **Output**: ```json { "shipping_id": "f15f8c62-8c63-46de-a7f6-a08f131848c5", "shipping_state": "NY", "shipping_zip": "10001" } ``` **Configuration**: ```json { "backend": [{ "url_pattern": "/shipping", "extra_config": { "proxy": { "flatmap_filter": [ { "type": "move", "args": ["zone.state","shipping_state"] }, { "type": "move", "args": ["zone.zip","shipping_zip"] }, { "type": "del","args": ["zone"] } ] } } }] } ``` As you can see, we did three operations: 1. Move the state to the root with a new name 2. Move the zip to the root with a new name 3. Delete `zone` as it became a null object after emptying it. ### Example: Moving around data in arrays In this example we have a couple of arrays that we want to manipulate. **Input**: ```json { "kindergarten": [ { "name": "TEST Kinder" }, { "name": "Lil' Elephants" }, { "name": "Bright Rainbows" } ], "schools": [ { "title": "Brookside Elementary" }, { "title": "Oak Tree School" } ] } ``` And we want an output where both arrays are merged, using consistent naming. And we also want to get rid of the first TEST element. As follows: **Output**: ```json { "schools":[ {"name":"Lil' Elephants"}, {"name":"Bright Rainbows"}, {"name":"Brookside Elementary"}, {"name":"Oak Tree School"} ] } ``` Then we need the following **configuration**: ```json { "endpoint": "/education-clean", "backend": [{ "url_pattern": "/education", "extra_config": { "proxy": { "flatmap_filter": [ { "type": "del", "args": ["kindergarten.0"] }, { "type": "move", "args": ["schools.*.title", "schools.*.name"] }, { "type": "append", "args": ["kindergarten", "schools"] } ] } } }] } ``` **What did we do here?** There is a sequence of three operations: - Delete the first element (index `0`) of `kindergarten`. - Rename all `title` attributes of `schools` to `name`. - Append all the `kindergarten` content to `schools`. For more examples, [see this test file](https://github.com/krakend/flatmap/blob/master/tree/tree_example_test.go). ## Mixing flatmap with other manipulation operations When the flatmap filter is enabled, the operations `group` and `target` keep their functionality, but `allow`, `deny`, and `mapping` are ignored. --- # [Response header transformation](https://www.krakend.io/docs/enterprise/service-settings/response-headers-modifier/) The response headers modifier allows you to `add`, `replace`, or `delete` headers declaratively before responses are returned to the end-user for **all declared endpoints simultaneously**. The main benefit of the `modifier/response-headers` is that the execution happens **at the end of the response flow**, and you can **transform headers** from all types of endpoints or encodings or change headers set by KrakenD itself (like the `X-KrakenD-Complete` and similar) ![response-headers-modifier.mmd diagram](/images/documentation/diagrams/response-headers-modifier.mmd.svg) With this component, you can transform the returning headers no matter how they were generated. ## Configuration of response headers transformation The configuration of the modifier is quite simple, and is declared at the **service level**, acting over all endpoints’ activity. If you want to transform headers in a single endpoint, see the different ways below. Headers are case insensitive, but normalized on the output All header declarations are **case-insensitive**, as per the RFC specification of headers. You can write them in the configuration using uppercase, lowercase, or mixed. Nevertheless, when the header is returned on the response, it is normalized in the **canonical format of the MIME header**. For instance `mY-HEAdEr` becomes `My-Header`. Here is a configuration example that performs the three types of transformations: - Adds an `X-Hello: World` header to all responses - Replaces any existing value of `Cache-Control` to set a `no-store` value - Deletes the `Server` header set by the backend ```json { "version": 3, "extra_config": { "modifier/response-headers": { "add": { "X-Hello": [ "World" ] }, "replace": { "Cache-Control": [ "no-store" ], "Vary": [ "foo", "bar", "foobar" ] }, "delete": [ "Server" ], "rename": { "X-Krakend-Completed": "X-Completed" } } } } ``` The operations are always executed in this fixed order: 1. `delete` 2. `replace` 3. `add` 4. `rename` ##### Fields of Response headers modifier \* required fields ##### [`add`](https://www.krakend.io/docs/enterprise/service-settings/response-headers-modifier/#add "Click to copy link to add") object The headers you want to add. Every key under `add` is the header name, and the values are declared in an array with all those you want to set. If the header didn’t exist previously, it is created with the values you passed. If the header existed, then the new values are appended. Example: `{"X-Hello":["World"]}` Properties of `add` can use a name matching the pattern `(.+)`, and their content is an `array` ##### [`delete`](https://www.krakend.io/docs/enterprise/service-settings/response-headers-modifier/#delete "Click to copy link to delete") array of strings The list of headers you want to delete. All headers listed will be missing in the response. Example: `["X-Krakend","X-Krakend-Completed"]` ##### [`rename`](https://www.krakend.io/docs/enterprise/service-settings/response-headers-modifier/#rename "Click to copy link to rename") object The headers you want to rename. The key used under `rename` is the original header name, and the value the new header name. This operation is destructive, meaning that if you rename to a header name that already existed it will be replaced with the new header and value. Example: `{"Header-A":"Header-A-New-Name"}` Properties of `rename` can use a name matching the pattern `(.+)`, and their content is an `string` ##### [`replace`](https://www.krakend.io/docs/enterprise/service-settings/response-headers-modifier/#replace "Click to copy link to replace") object The headers you want to replace. The key used under `replace` is the header name, and the value an array with all the header values you want to set. The replacement overwrites any other value that could exist in this header. Example: `{"Cache-Control":["no-store"],"Vary":["foo","bar","foobar"]}` Properties of `replace` can use a name matching the pattern `(.+)`, and their content is an `array` Schema: [https://www.krakend.io/schema/v2.13/modifier/response-headers.json](https://www.krakend.io/schema/v2.13/modifier/response-headers.json "JSON schema definition") ## Example: Remove `X-KrakenD` headers If for any reason you want to remove the KrakenD headers from the reponses, you only need to set the following configuration: ```json { "version": 3, "extra_config": { "modifier/response-headers": { "delete": [ "X-Krakend", "X-Krakend-Completed" ] } } } ``` ## Alternatives to transform headers There are several ways to set response headers without the `modifier/response-headers` that allow you work on a per-endpoint scenario, such as: - Set them in the backend by using the [`no-op` encoding](/docs/enterprise/endpoints/no-op/) on the endpoints you like. - Setting them statically with a [Martian modifier](/docs/enterprise/backends/martian/) (and `no-op`) - Script them with [Lua](/docs/enterprise/endpoints/lua/) - In a [Go plugin](/docs/enterprise/extending/writing-plugins/) --- # [Body to header and query string extractor (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/request-body-extractor/) The **Request Body Extractor reads one or more fields from an incoming request body and maps their values to request headers or query strings** before KrakenD forwards the request upstream. You can place this transformation at three points in the processing pipeline: globally at the service level, early at the endpoint handling level before authentication runs, or inside the proxy pipeline where async agents and workflows also execute. This enables the promotion of authentication credentials, tenant identifiers, or routing parameters that clients embed in a JSON body to the standard positions where KrakenD components expect to find them. Header injection security notice Extracting body fields and injecting them as request headers gives clients indirect control over header values. Limit which endpoints use this feature, define narrow `expression` values, and use `on_conflict: skip` when you do not want clients to override headers that are already present, especially security-sensitive headers like `Authorization`. ## Namespace and Pipeline Placement The Request Body Extractor has two namespaces. Their placement in the `extra_config` configuration determines when extraction occurs relative to authentication and other pipeline stages: ![Possible placements of the Body Extractor in KrakenD pipes](/images/documentation/diagrams/components-sequence-body-extractor.mmd.svg) | Namespace | Placement | Description | |-------------------------------------------------|------------|-------------------------------------------------------------| | **(1)** `modifier/request-body-extractor` | `service` | `service` level. Applies to every incoming request | | **(2)** `modifier/request-body-extractor/early` | `endpoint` | Endpoint level. Runs **before** endpoint-related components | | **(3)** `modifier/request-body-extractor` | `endpoint` | Proxy pipeline. Runs **after** endpoint handling | When you place `modifier/request-body-extractor` at the **service level**, every request passes through the extractor regardless of which endpoint it targets. This is the right choice when all endpoints depend on the same body-derived parameter, for example, a global tenant identifier extracted into a query string. At the **endpoint level**, the same namespace runs inside the proxy pipeline. This stage executes after the endpoint handling, so authentication has already occurred by the time extraction runs. Use this placement when the extracted value feeds into backend calls, sequential proxies, async agents, or workflow composition. The **early variant** (`modifier/request-body-extractor/early`) runs at the endpoint handling level, before any other endpoint-related component, including `auth/api-keys`, `auth/validator`, and rate limiting. It is only available at the endpoint level and cannot participate in async agent pipelines. Use this namespace when the extracted value must be visible to an authentication or authorization component. ## Configuring Extraction Operations Both namespaces share the same configuration structure: an `operations` array where each element defines one extraction rule. You can declare multiple operations to populate several headers or query strings from a single request body. ##### Fields of Request Body Extractor \* required fields ##### [`operations`](https://www.krakend.io/docs/enterprise/endpoints/request-body-extractor/#operations "Click to copy link to operations") * array of objects A list of extraction operations to apply. Each operation extracts a value from the request body and writes it to a header or query string parameter. Operations are evaluated in sequential order. Each item of operations accepts the following properties: ##### [`expression`](#operations-expression "Click to copy link to expression") * string The expression that selects the value from the request body. When using the `dotnotation` strategy, use period-separated field names (e.g., `user.id`). When using `jmespath`, provide a valid JMESPath expression. Examples: `"user.id"` , `"auth.role_key"` , `"data.items[0].name"` ##### [`on_conflict`](#operations-on_conflict "Click to copy link to on_conflict") * Controls behaviour when the destination header or query string parameter already has a value. `skip` preserves the existing value and discards the extracted one. `replace` overwrites the existing value. `append` adds the extracted value alongside the existing one. Possible values are: `"skip"` , `"replace"` , `"append"` ##### [`strategy`](#operations-strategy "Click to copy link to strategy") * The strategy used to navigate the request body and extract the value. Use `dotnotation` for straightforward field access using period-separated field names (e.g., `user.id`). Use `jmespath` to evaluate a JMESPath expression for more complex selections involving arrays, filtering, or projections. Possible values are: `"dotnotation"` , `"jmespath"` ##### [`target`](#operations-target "Click to copy link to target") * string The name of the destination header or query string parameter where the extracted value is written. Examples: `"Authorization"` , `"uid"` , `"X-Tenant-Id"` ##### [`type`](#operations-type "Click to copy link to type") * The destination where the extracted value is written. Use `header` to inject the value as a request header, or `query_string` to inject it as a query string parameter. Possible values are: `"header"` , `"query_string"` Schema: [https://www.krakend.io/schema/v2.13/modifier/request-body-extractor.json](https://www.krakend.io/schema/v2.13/modifier/request-body-extractor.json "JSON schema definition") ### Extraction Strategies The `dotnotation` strategy navigates nested JSON objects using period-separated field names. For example, the expression `user.id` retrieves the `id` field nested inside a `user` object. Use `dotnotation` for straightforward field access without filtering or array traversal. The `jmespath` strategy evaluates a [JMESPath expression](https://jmespath.org/tutorial.html) against the parsed request body. Use `jmespath` when the value is inside an array, requires conditional selection, aggregations, or involves more complex projections that dot notation cannot express. ### Conflict Resolution The `on_conflict` field controls behaviour when the destination header or query string parameter already holds a value at the time extraction runs: - `skip` preserves the existing value and discards the extracted one. Use this to avoid overwriting headers set directly by the client. - `replace` overwrites the existing value with the value extracted from the body. - `append` adds the extracted value alongside the existing one. This is relevant for headers that accept multiple values. ## Example: how to extract a body parameter as a header When you place `modifier/request-body-extractor` at the endpoint level (without the `/early` suffix), the extraction runs inside the proxy pipeline. This is the correct placement when the extracted value feeds downstream components such as sequential proxies, async agents, or backend-level transformations: ```json { "endpoint": "/secure/regular/", "method": "POST", "output_encoding": "no-op", "input_headers": ["Authorization"], "extra_config": { "modifier/request-body-extractor": { "operations": [ { "type": "header", "strategy": "dotnotation", "expression": "auth.role_key", "target": "Authorization", "on_conflict": "skip" } ] } }, "backend": [ { "url_pattern": "/customer", "method": "GET", "host": ["http://backend:8080"] } ] } ``` Because the proxy pipeline runs after HTTP-level authentication, any component configured in the endpoint’s `extra_config` that operates at the endpoint handling level, such as `auth/api-keys`, does **not** see the header value injected here. If the extracted value must influence endpoint-level authentication, use `modifier/request-body-extractor/early` instead, as shown in the previous example. ## Example: how to extract a body parameter for all requests The following configuration extracts `user.id` from the request body globally and injects it as the query string parameter `uid` on every endpoint. If `uid` already exists in the request, it replaces it: ```json { "version": 3, "extra_config": { "modifier/request-body-extractor": { "operations": [ { "type": "query_string", "strategy": "dotnotation", "expression": "user.id", "target": "uid", "on_conflict": "replace" } ] } }, "endpoints": [] } ``` Every endpoint in the gateway receives the `uid` query string parameter derived from the body. For the parameter to reach the backend, each endpoint must also declare `uid` in its `input_query_strings` list. ## Example: how to extract a body parameter before authentication In this scenario, a client sends its API key inside the JSON body under `auth.role_key` instead of in an `Authorization` header. The `modifier/request-body-extractor/early` namespace extracts the field and writes it to `Authorization` before the API key validator runs: ```json { "endpoint": "/secure/early/", "method": "POST", "output_encoding": "no-op", "input_headers": ["Authorization"], "extra_config": { "modifier/request-body-extractor/early": { "operations": [ { "type": "header", "strategy": "dotnotation", "expression": "auth.role_key", "target": "Authorization", "on_conflict": "skip" } ] }, "auth/api-keys": { "roles": ["role1"] } }, "backend": [ { "url_pattern": "/sales", "method": "GET", "host": ["http://backend:8080"] } ] } ``` The `on_conflict: skip` setting ensures that a client providing the `Authorization` header directly takes precedence over the body field. When the header is absent, KrakenD populates it from `auth.role_key`, and the API key validator proceeds with that value. Because `modifier/request-body-extractor/early` executes at the endpoint handling level, the `auth/api-keys` component sees the injected header without any additional wiring. --- # [Request manipulation with templates (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/body-generator/) The body generator modifier allows you to **craft the body you send** to a backend through templates and enables injecting other values from the user request, such as the original body, headers, query strings, or URL parameters. The body generator does not necessarily need that the endpoint sends data, as it works even when there is no input body from the user. It lets you specify the body content you want to send to the final service, and you can reuse parts of the request (such as headers) to form a new body. The most common **uses cases** are: - Body manipulation or transformation - Add headers, token claims, or query strings to the body - Convert a backend POST into a GET endpoint while still sending data - Enrich the user’s body with additional data - Build your gateway without having yet a client (set mock data in the POST) ## Configuration for the body generator The body generator modifier has the following options available: ##### Fields of Template-based body generator \* required fields Minimum configuration needs **one of**: `path` , or `template` ##### [`content_type`](https://www.krakend.io/docs/enterprise/backends/body-generator/#content_type "Click to copy link to content_type") string The `Content-Type` you are generating in the template, so it can be recognized by whoever is using it. Examples: `"application/json"` , `"application/xml"` , `"text/xml"` Defaults to `"application/json"` ##### [`debug`](https://www.krakend.io/docs/enterprise/backends/body-generator/#debug "Click to copy link to debug") boolean When `true`, shows useful information in the logs with `DEBUG` level about the input received and the body generated. Do not enable in production. Debug logs are multiline and designed fore developer readibility, not machine processing. Defaults to `false` ##### [`path`](https://www.krakend.io/docs/enterprise/backends/body-generator/#path "Click to copy link to path") string The path to the Go template file you want to use to craft the body. Example: `"./path/to.tmpl"` ##### [`template`](https://www.krakend.io/docs/enterprise/backends/body-generator/#template "Click to copy link to template") string An inline base64 encoded Go template with the body you want to generate. This option is useful if you want to have the template embedded in the configuration instead of an external file. ##### [`variables`](https://www.krakend.io/docs/enterprise/backends/body-generator/#variables "Click to copy link to variables") object An object traversable with dots with any content you want to be available to the template. Schema: [https://www.krakend.io/schema/v2.13/modifier/body-generator.json](https://www.krakend.io/schema/v2.13/modifier/body-generator.json "JSON schema definition") ## How body generation works? To use the body generator modifier, write a template (inline as base64 or clear text in an external file). For example, the following `sample.json.tmpl` represents a body in JSON format you would send to your backend: ```go-text-template { "update":{ "user_id": "{{ .req_params.User }}", "email": "{{ .req_body.email }}" } } ``` When a request to the backend arrives, this template replaces the `{{ .req_params.User }}` with the `{user}` value of the URL (e.g., `/foo/{user}`), and the `{{ .req_body.email }}` with an `email` field passed in the user request body. The final render is the content of the body you will send to the backend. The example uses a JSON format, but you can write the template using any other format you need, and the configuration option `content_type` lets the backend know the correct type. In this case, it should be `application/json` to match our written content. ## Template definition When you write the content of the body, you do it in a [Go text template](https://pkg.go.dev/text/template) (similar to Helm, Kubernetes, and other systems). The template engine parses the content and replaces variables with the format `{{ .something }}`, but you can use all the power of templates and introduce conditionals, loops, and other checks. The following variables are available in the template you will use to construct a body: ### `.req_body` It contains the data sent by the user in the body request. You can reuse the user’s body sent in several formats to compose the final body you will send to the backend server. The `.req_body` is initially empty unless the following requirements are met: - The template has at least a `.req_body` declaration - The `Content-Type` is declared in the `input_headers` of the endpoint. The content type is necessary to determine **how to parse the request** body and make it available to the template. This is not the `content_type` configuration option you will send to the backend server, although it could match. The following content types are the only ones that will work when submitting data to KrakenD (otherwise, there will be an error): - `application/json` - `application/xml` - `text/xml` - `application/x-www-form-urlencoded` - `multipart/form-data` - `text/plain` For instance, a user request like: Term  ``` $curl -XPOST -d '{"foo": "bar"}' -H'Content-Type: application/json' http://localhost:8080/hello ``` Allows you to use `.req_body.foo` in a template, which will translate into `bar`. At the same time, you could decide to write a template in XML format using these values and switch to a `content_type` different when reaching the backend server. ### `.req_params` It contains all the parameters you have declared in the `endpoint` as `{placeholders}` or in the `url_pattern` ([dynamic routing](/docs/enterprise/endpoints/dynamic-routing/)). To access the parameters, use the **first letter capitalized**. For instance, an endpoint defined like this: ```json { "endpoint": "/foo/{bar}" } ``` Allows you to use in a template `.req_params.Bar` and contains the value of the request in `{bar}`. ### `.req_method` It contains a string with the method used to access the backend service, for instance `GET`, `PUT`, etc. ### Sprig functions In addition to the variables, the template system accepts [Sprig Functions](/docs/configuration/flexible-config/#sprig-functions). ### Accessing req\_params with dynamic routing When accessing content from [dynamic routing](/docs/enterprise/endpoints/dynamic-routing/), you need to access variables in a special way. Because the `url_pattern` contains variables with dots inside, these are special characters, and they must be accessed using an `index` For instance, let’s say you have an endpoint like this: ```json { "endpoint": "/notify/{id_user}", "method": "POST", "input_query_strings": ["channel"], "backend": [ { "url_pattern": "/__debug/{input_query_strings.channel}", "host": ["http://localhost:8080"], "extra_config": { "modifier/request-body-generator": { "path": "body_querystring.json.tmpl", "debug": true } } } ] } ``` And expects a request from the user such as `curl -XPOST http://localhost:8080/notify/25\?channel\=mobile`. The configuration contains the `endpoint` parameter `{id_user}` that can be accessed in the template as `{{ .req_params.Id_user }}`, but it also contains a parameter `channel` in the backend `url_pattern` that is defined as `{input_query_strings.channel}`. It has a dot inside. As the `.` is a special character on go templates, you must access it using an `index` without forgetting that we must uppercase the first letter. In all, we could then have our template as follows: ```go-text-template { "params_where": "{{ .req_params }}", "id_user": "{{ .req_params.Id_user }}", "channel": "{{ index .req_params "Input_query_strings.channel" }}" } ``` Similarly, we could access the `sub` claim of a JWT token passed in the `"url_pattern": "/foo/{JWT.sub}"` like this: ```go-text-template { "params_where": "{{ .req_params }}", "id_user": "{{ .req_params.Id_user }}", "channel": "{{ index .req_params "JWT.sub" }}" } ``` ### `.req_headers` It contains all the headers allowed in the endpoint, not the ones sent by the user. It means that the endpoint must declare in `input_headers` each header you want to access. For instance: ```json { "endpoint": "/foo/{bar}", "input_headers": ["X-Header"] } ``` Allows you to use in a template `{{ index .req_headers "X-Header"}}`. Notice that we are NOT accessing this variable as `.req_headers.X-Header` in this case because it contains the special char `-`, and that is a minus sign on the template. Same as we saw with the dot. When `input_headers` is set to `["*"]`, all headers sent by the client are in the variable, although this practice might lead to potential security threads and is discouraged. Instead, add only those that you will use. Another important comment on headers is that by definition, a header might contain **multiple values**, therefore when accessing the `.input_headers.Foo` variable you are getting an array. In most of the cases you will want the first position, so you can have a syntax in the template as follows: ```go-text-template { "content-type": "{{index (index .req_headers "Content-Type") 0}}" } ``` The index inside the parenthesis accesses the `Content-Type` key (because it has the special char `-`), and the index outside the parenthesis accesses the position `0` of the array. ### `.req_querystring` It contains all the query strings allowed to pass in the endpoint. As with headers, the endpoint must declare the list in `input_query_strings`. For instance ```json { "endpoint": "/foo/{bar}", "input_query_strings": ["query","limit"] } ``` Allows you to use in a template `.req_querystring.query` or `.req_querystring.limit`. When `input_query_strings` is set to `["*"]`, then all query strings sent by the client are in the variable, although this practice might lead to potential security threads and is discouraged. ### `.req_path` The path that KrakenD will use to connect the backend server. It matches the `url_pattern` of the configuration. ### `.variables` It contains an object traversable with dots with any content you add in the backend configuration under the `variables` property. The content inside variables accepts any structure you need, including sub-objects and arrays. For instance, if you have this configuration: ```json { "modifier/request-body-generator": { "path": "file.tmpl", "debug": true, "variables": { "hello": "world" } } } ``` Then a template containing `{{ .variables.hello }}` would print `world` ## Body generator modifier example Let’s show how this works with a testable model. Suppose you have a POST endpoint on KrakenD where a user sends a `text`, and we want to POST this content modified, along with additional parts of the input to build the body. The user request would be: Post request  ``` $curl -XPOST -d '{"text": "hello"}' -H'Content-Type: application/json' http://localhost:8080/bodygenerator/10 ``` But we want to receive in our backend something like: Post request to backend  ``` $curl -XPOST -d '{"message": "User said hello", "id": 10}' -H'Content-Type: application/json' http://backend/url ``` Where the `text` is renamed to `message` and we also include the id passed in the URL. The reproducible configuration would look like this: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "host": ["http://localhost:8080"], "debug_endpoint": true, "endpoints": [ { "endpoint": "/bodygenerator/{id}", "method": "GET", "input_headers": [ "Content-Type" ], "backend": [ { "url_pattern": "/__debug/test/{id}", "method": "POST", "encoding": "json", "extra_config": { "modifier/request-body-generator": { "path": "./body.json.tmpl", "content_type": "application/json", "debug": true } }, "host": [ "http://localhost:8080" ] } ] } ] } ``` And the configuration refers to a file `body.json.tmpl`, which would contain the following content: ```go-text-template { "id": "{{ .req_params.Id }}", "message": "User said {{ .req_body.text }}" } ``` You would have the expected replacement when running KrakenD and calling the endpoint. These are the critical takeaways from this configuration: - The `path` defines where the external template is, using a relative dir `./` based on KrakenD’s [working directory](/docs/enterprise/configuration/working-directory/), but it can also be an absolute path. - You can use `template` instead, but then you need to convert the template above into a base64 string. Do not use both. - The variable `req_params` (and not the rest) is accessing parameters with the first letter in uppercase (`Id`). - All injected variables start with a dot `.` and use one of the variables defined in the variables section. - All template blocks are delimited by `{{ }}`. ## Debugging the POST template While working with the body generator modifier, you might find it useful to set the `debug` flag to true. This flag (that you should not use in production) outputs the following information in the console (when the [debug level](/docs/logging/) is `DEBUG`): - All the variables available in the template - The final generated body, after compiling the template and injecting the variables - The content type to send to the backend server Use the flag for faster development! But remove it in production. It is designed for developer reading of the logs (multiline content), and not for machine processing of the lines. It is also important to look at the rest of the log lines, as in case the templates cannot be rendered or found (the relative path could be different than you expect) you will see lines showing the problem: ```log KRAKEND DEBUG: [BACKEND: /foo][body-generator] open ./body.json.tmpl: no such file or directory ``` ## Embedding templates in base64 You can embed the template in the configuration as a base64 instead of referencing it as an external file. There are several ways you can do this. **Written inline in the template** using flexible configuration: ```go-text-template "modifier/request-body-generator": { "template": "{{ `{ "id": "{{ .req_params.Id }}", "message": "User said {{ .req_body.text }}" }` | b64enc }}", "content_type": "application/json", "debug": true } ``` As you can see, the backtick delimiters write the template as it is, and at the end, it pipes it to the `b64enc` function. **Loaded as a partial template** with base64 encoding and flexible configuration: ```go-text-template "modifier/request-body-generator": { "template": "{{ include "body.json.tmpl" | b64enc }}", "content_type": "application/json", "debug": true } ``` Copy and paste the value from a **terminal:** Base64 encode of a template  ``` $base64 -w 0 body.json.tmpl ewogICJpZCI6ICJ7eyAucmVxX3BhcmFtcy5JZCB9fSIsCiAgIm1lc3NhZ2UiOiAie3sgLnJlcV9ib2R5Lm1lc3NhZ2UgfX0iCn0= ``` Notice that we are adding `-w 0` because we don’t want new lines that would break the configuration. --- # [GeoIP integration: Location-based services (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/geoip/) The GeoIP integration allows you load Maxmind’s [GeoIP2 City](https://www.maxmind.com/en/geoip2-city) database (both commercial and free versions) and enrich all KrakenD calls to your backends with geo data. To add GeoIP information to your backends, copy the Maxmind’s database file into the filesystem (it has to be a city DB) and specify its location in the configuration. Downloading the database requires signing up to MaxMind’s website. [Download the commercial or free GeoIP2 City](https://www.maxmind.com/en/geoip2-city) ## Configuration for GeoIP Add the following `extra_config` in the root to your configuration file: ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "extra_config": { "plugin/http-server": { "name": ["geoip", "another-plugin-maybe" ], "geoip": { "citydb_path": "/etc/krakend/GeoIP2-City.mmdb" } } } } ``` ##### Fields of GeoIP \* required fields ##### [`citydb_path`](https://www.krakend.io/docs/enterprise/endpoints/geoip/#citydb_path "Click to copy link to citydb_path") * string The path in the filesystem containing the database in GeoIP2 Binary (`.mmdb`) format. Relative to the working dir or absolute path. Example: `"path/to/GeoIP2-City.mmdb"` Schema: [https://www.krakend.io/schema/v2.13/plugin/geoip.json](https://www.krakend.io/schema/v2.13/plugin/geoip.json "JSON schema definition") ### Data passed to backends When the GeoIP plugin is enabled, all requests to the backend are enriched with a header `X-GeoIP`. The following structure is available. Keys that couldn’t retrieve its associated data show an empty value. You can use these fields in the [GeoIP policies](/docs/enterprise/security-policies/). - `City` - `GeoNameID` integer - `Names` map\[string]string - `Continent` - `Code` string - `GeoNameID` integer - `Names` map\[string]string - `Country` - `GeoNameID` integer - `IsInEuropeanUnion` bool - `IsoCode` string - `Names` map\[string]string - `Location` - `AccuracyRadius` integer16 - `Latitude` float64 - `Longitude` float64 - `MetroCode` integer - `TimeZone`string - `Postal` - `Code` string - `RegisteredCountry` - `GeoNameID` integer - `IsInEuropeanUnion` bool - `IsoCode` string - `Names` map\[string]string - `RepresentedCountry` - `GeoNameID` integer - `IsInEuropeanUnion` bool - `IsoCode` string - `Names` map\[string]string - `Type`string - `Subdivisions` (array): - `GeoNameID` integer - `IsoCode` string - `Names` map\[string]string - `Traits` - `IsAnonymousProxy` bool - `IsSatelliteProvider` bool --- # [Static modification of requests and responses with Martian](https://www.krakend.io/docs/backends/martian/) The Martian component allows you to **modify requests and responses with static data** through a simple DSL definition in the configuration file. Martian works perfectly in combination with other components, such as [CEL verifications](/docs/endpoints/common-expression-language-cel/) or [Caching](/docs/backends/caching/), as it acts before other components start processing. As it acts at HTTP level, it can change requests and responses even using the `no-op` encoding. Use Martian when you want to make modifications before passing the content to the backends (`request`) or when returning from them (`response`). ## When to use Martian Martian is a static component You can inject data in requests and responses using the Martian component as long as it’s static data, **hardcoded in the configuration**. It does not allow you to place `{variables}` inside the modifiers. Use Martian whenever you need to alter the request or response based on criteria with static values. Some **examples of typical Martian scenarios** are: - Set a new cookie during gateway processing - Flag requests with query strings or headers when specific criteria is met - Add, remove, or change specific headers - Do basic authentication between KrakenD and the backend - Add query strings before making the backend request (e.g., set an API key) ## Martian configuration Add martian modifiers in your configuration under the `extra_config` of any `backend` using the namespace `modifier/martian`. Inside the configuration, you must write a single component using the notation `package.Type` using the available ones described in this page. If you need **multiple modifiers** use a group type. You cannot write `@comment`-like attributes directly under the `modifier/martian` namespace, but you can add them inside each modifier. There are three main **types** of packages you can use in Martian: - **Modifiers**: Change the state of a request or a response. For instance, you want to add a custom header or a query string in the request before sending it to a backend. - **Filters**: Add a condition to execute a contained modifier - **Groups**: Bundle multiple operations to execute in the order specified in the group Your configuration has to look as follows: ```json { "backend": [ { "url_pattern": "/foo/{var}", "extra_config": { "modifier/martian": { "the package type key goes here" { "scope": ["request", "response"] } } } } ] } ``` Each package has its configuration, but a commonality is that they all have a `scope` key indicating when to apply the modifier. It can be an array containing `request`, `response`, or both. It depends on the component. ## Martian Modifiers All packages with keys like `package.Modifier` or `package.Header` change the state of a request or a response. For instance, you want to add a custom header in the request before sending it to a backend. See the list of available modifiers below. ### Body modifier The `body.Modifier` changes or sets the body of a request or response. The body must be **uncompressed and Base64 encoded**. Additionally, it will modify the following headers to ensure proper transport: `Content-Type`, `Content-Length`, `Content-Encoding`. ##### Fields of Body Modifier \* required fields ##### [`body`](https://www.krakend.io/docs/backends/martian/#body "Click to copy link to body") * string The body you want to set, formatted in base64. ##### [`contentType`](https://www.krakend.io/docs/backends/martian/#contentType "Click to copy link to contentType") string The content-type representing the body you are setting Examples: `"application/x-www-form-urlencoded"` , `"text/plain"` ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") The following modifier sets the body of the request and the response to `{"msg":"you rock!"}`. Notice that the `body` field is `base64` encoded (e.g., `echo "content" | base64 -w0`). ```json { "endpoint": "/test/body.Modifier", "backend": [ { "url_pattern": "/__debug/body.Modifier", "extra_config": { "modifier/martian": { "body.Modifier": { "scope": [ "request", "response" ], "@comment": "Send a {'msg':'you rock!'}", "body": "eyJtc2ciOiJ5b3Ugcm9jayEifQ==", "contentType": "application/json" } } } } ] } ``` #### Facilitating base64 content The [Flexible Configuration](/docs/configuration/flexible-config/) has a `b64enc` function that will allow you to have an easier-to-read configuration. For instance (notice the backticks as delimiters): ```go-text-template "body": "{{- `{"msg":"you rock!"}` | b64enc -}}" ``` Or from an external file: ```go-text-template "body": "{{- include "external_file.txt" | b64enc -}}" ``` ### Cookie Modifier The `cookie.Modifier` adds a cookie to a request or a response. If you set cookies in a response, the cookies are only set to the client when you use `no-op` encoding. ##### Fields of Cookie Modifier \* required fields ##### [`domain`](https://www.krakend.io/docs/backends/martian/#domain "Click to copy link to domain") string Domain of the Cookie you want to set Example: `"example.com"` ##### [`expires`](https://www.krakend.io/docs/backends/martian/#expires "Click to copy link to expires") string Date in RFC 3339 format and is absolute, not relative to the current time. Example: `"2025-04-12T23:20:50.52Z"` ##### [`httpOnly`](https://www.krakend.io/docs/backends/martian/#httpOnly "Click to copy link to httpOnly") boolean Create the Cookie with the httpOnly flag. When `true`, mitigates the risk of client side script accessing the protected cookie (if the browser supports it), mitigating the Most Common XSS Defaults to `false` ##### [`maxAge`](https://www.krakend.io/docs/backends/martian/#maxAge "Click to copy link to maxAge") integer For how long this Cookie is valid, in seconds. `0` means that the attribute is not set. `maxAge<0` means delete cookie now Defaults to `0` ##### [`name`](https://www.krakend.io/docs/backends/martian/#name "Click to copy link to name") * string Name of the Cookie you want to set ##### [`path`](https://www.krakend.io/docs/backends/martian/#path "Click to copy link to path") string Path of the Cookie you want to set Example: `"/path/to"` ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` ##### [`secure`](https://www.krakend.io/docs/backends/martian/#secure "Click to copy link to secure") boolean Cookie secure flag. When `true`, the user agent will include the cookie in the request when using https only Defaults to `false` ##### [`value`](https://www.krakend.io/docs/backends/martian/#value "Click to copy link to value") * string Value of the Cookie you want to set Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") ```json { "endpoint": "/test/cookie.Modifier", "input_headers": [ "X-Some" ], "output_encoding": "no-op", "backend": [ { "url_pattern": "/__echo/cookie.Modifier", "encoding": "no-op", "extra_config": { "modifier/martian": { "cookie.Modifier": { "scope": [ "request", "response" ], "name": "AcceptCookies", "value": "yes", "path": "/some/path", "domain": "example.com", "expires": "2025-04-12T23:20:50.52Z", "secure": true, "httpOnly": false, "maxAge": 86400 } } } } ] } ``` ### URL Modifier The `url.Modifier` allows you to change the URL despite what is set in the `host` and `url_pattern` combination. For instance, the following example calls a host and pattern that does not exist `https://does-not-exist/neither` but it ends up calling `http://localhost:8080/__echo/hello?flag=true`. It might be useful when used in combination with a Filter. Except for `scope`, all the fields are optional. Set the ones you need. ##### Fields of URL Modifier \* required fields ##### [`host`](https://www.krakend.io/docs/backends/martian/#host "Click to copy link to host") string The hostname part of the URL including the port Examples: `"example.com"` , `"localhost:8080"` ##### [`path`](https://www.krakend.io/docs/backends/martian/#path "Click to copy link to path") string The path part of the URL Example: `"/path/to"` ##### [`query`](https://www.krakend.io/docs/backends/martian/#query "Click to copy link to query") string Sets the query string parameters you want to pass, overwriting anything passed in the request. Notice that if you set a `query`, if the user passes other query string parameters listed under `input_query_strings`, they will be lost, and only the values passed in the modifier will be sent. For such uses, see the `querystring.Modifier` Examples: `"param=1"` , `"key1=val&key2=val"` ##### [`scheme`](https://www.krakend.io/docs/backends/martian/#scheme "Click to copy link to scheme") string The scheme to apply Examples: `"http"` , `"https"` ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") ```json { "endpoint": "/test/url.Modifier", "backend": [ { "host": ["https://does-not-exist"], "url_pattern": "/neither", "extra_config": { "modifier/martian": { "url.Modifier": { "scope": ["request"], "scheme": "http", "host": "localhost:8080", "path": "/__echo/hello", "query": "flag=true" } } } } ] } ``` Notice that the example above changes the URL used to query the backend, but the `Host` header remains `does-not-exist`. ### Query String modifier The `querystring.Modifier` adds a new query string or modifies existing ones in the request. ##### Fields of Querystring Modifier \* required fields ##### [`name`](https://www.krakend.io/docs/backends/martian/#name "Click to copy link to name") * string Name of the query string you want to set Examples: `"page"` , `"limit"` ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request"]` ##### [`value`](https://www.krakend.io/docs/backends/martian/#value "Click to copy link to value") * string The value of the query string you want to set Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") The example below sets an `?amount=75` independently of the value the user passed. Any other input query strings declared under `input_query_strings` are preserved and reach the backend as passed. ```json { "endpoint": "/test/querystring.Modifier", "input_query_strings": ["currency","amount"], "backend": [ { "host": ["http://localhost:8080"], "url_pattern": "/__echo/querystring.Modifier", "allow": ["req_uri"], "extra_config": { "modifier/martian": { "querystring.Modifier": { "scope": ["request"], "name": "amount", "value": "75" } } } } ] } ``` Example of querystring.Modifier output  ``` $curl -i http://localhost:8080/test/querystring.Modifier\?currency\=EUR\&amount\=55 HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 X-Krakend: Version 2.3.3 X-Krakend-Completed: true Date: Sat, 03 Jun 2023 20:57:43 GMT Content-Length: 70 {"req_uri":"/__echo/querystring.Modifier?amount=75\u0026currency=EUR"} ``` ### Copy a header Although not widely used, the `header.Copy` lets you duplicate a header using another name. If you want to return headers to the client, remember to use `no-op` encoding. Notice also that even though the modifier supports request and response, rarely the same headers are used in both directions. ##### Fields of Header Copy \* required fields ##### [`from`](https://www.krakend.io/docs/backends/martian/#from "Click to copy link to from") * string The origin header you want to copy. When the header is provided by the user it must be included in the `input_headers` list. ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` ##### [`to`](https://www.krakend.io/docs/backends/martian/#to "Click to copy link to to") * string The destination header you want to create. If this header is returned to the end-user you must use `no-op` in the `output_encoding` of the endpoint. Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") ```json { "endpoint": "/test/header.Copy", "input_headers": ["X-Some"], "output_encoding": "no-op", "backend": [ { "host": ["http://localhost:8080"], "url_pattern": "/__echo/header.Copy", "extra_config": { "modifier/martian": { "header.Copy": { "scope": ["request","response"], "from": "User-Agent", "to": "X-Browser" } } } } ] } ``` ### Stash modifier The `stash.Modifier` creates a new header (or replaces an existing one with a matching name) containing the value of the original URL and all its query string parameters. ##### Fields of Stash Modifier \* required fields ##### [`headerName`](https://www.krakend.io/docs/backends/martian/#headerName "Click to copy link to headerName") * string The header you want to create. If this header is returned to the end-user you must use `no-op` in the `output_encoding` of the endpoint. ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") The example below adds a header `X-Stash: http://localhost:8080/__echo/stash.Modifier?amount=1` both in the request and the response when the user calls `http://localhost:8080/test/stash.Modifier?amount=1` ```json { "endpoint": "/test/stash.Modifier", "input_headers": ["X-Some"], "output_encoding": "no-op", "backend": [ { "host": ["http://localhost:8080"], "url_pattern": "/__echo/stash.Modifier", "extra_config": { "modifier/martian": { "stash.Modifier": { "scope": ["request","response"], "headerName": "X-Stash" } } } } ] } ``` ### Header modifier The `header.Modifier` adds a new header or changes the value of an existing one. To change headers sent by the client, remember to add `input_headers` in the endpoint. Also, if the client needs to see the headers in the `response`, you must set the `output_encoding` to `no-op`. ##### Fields of Header Modifier \* required fields ##### [`name`](https://www.krakend.io/docs/backends/martian/#name "Click to copy link to name") * string Name of the header you want to set ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` ##### [`value`](https://www.krakend.io/docs/backends/martian/#value "Click to copy link to value") * string Value of the header you want to set Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") For instance, the following configuration changes the `User-Agent` (set internally by KrakenD) to `Late-Night-Commander v2.3` both in the request and the response. ```json { "endpoint": "/test/header.Modifier", "output_encoding": "no-op", "backend": [ { "host": ["http://localhost:8080"], "url_pattern": "/__echo/header.Modifier", "extra_config": { "modifier/martian": { "header.Modifier": { "scope": ["request","response"], "name": "User-Agent", "value": "Late-Night-Commander v2.3" } } } } ] } ``` #### Connecting to Basic Auth (user/pass) backends An application of this modifier is when you need KrakenD to provide a fixed user and password to connect to the backend, and the client does not need to know about it. The basic authentication requires you to provide a header with the form `Authorization: Basic `. The credentials are the concatenation of the username and password using a colon `:` in base64. For instance, if your username is `user` and your password `pa55w0rd`, you should generate the base64 as follows: Term  ``` $echo -n "user:pa55w0rd" | base64 dXNlcjpwYTU1dzByZA== ``` When using `echo`, make sure to add the `-n` option to avoid the final line break from being encoded. You can test if the connection succeeds now with: Term  ``` $curl -i https://yourapi --header 'Authorization: Basic dXNlcjpwYTU1dzByZA==' ``` If the connection works, it means that your credentials are correct, and you can add the resulting base64 string `dXNlcjpwYTU1dzByZA==` to the Martian modifier right before connecting to your `backend`: ```json { "url_pattern": "/protected", "extra_config": { "modifier/martian": { "header.Modifier": { "scope": ["request"], "name": "Authorization", "value": "Basic dXNlcjpwYTU1dzByZA==" } } } } ``` With the configuration above, whenever a request is made to the backend, the `Authorization` header is added automatically. ### Header ID The `header.Id` is a modifier that sets a header with a **unique identifier (UUID)** for the request. The default name of the header is `X-Krakend-Id` but it can be changed by passing a “header” parameter as per the example below which names the header `X-Request-Id`. If for whatever reason, the header already exists, the header is not altered. ##### Fields of Header Id \* required fields ##### [`header`](https://www.krakend.io/docs/backends/martian/#header "Click to copy link to header") string The header name you want to use to save the ID. In the case the header is already set, the header is unmodified. Defaults to `"X-Krakend-Id"` ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") ```json { "version": 3, "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "host": ["http://localhost:8080"], "echo_endpoint": true, "endpoints": [ { "endpoint": "/test", "backend": [ { "url_pattern": "/__echo/header.Id", "extra_config": { "modifier/martian": { "header.Id": { "scope": [ "request" ], "header": "X-Request-Id" } } } } ] } ] } ``` ### Append a header The `header.Append` adds a new header in the request or the response, or appends a new value to an existing one. There are some headers that accept only one value, so you won’t be able to set multiple entries in one header, like `Accept-Encoding`, `User-Agent`, `X-Forwarded-For`, or `X-Forwarded-Host`. ##### Fields of Append a header \* required fields ##### [`name`](https://www.krakend.io/docs/backends/martian/#name "Click to copy link to name") * string Name of the header you want to append a value. Add the same name under the `input_headers` list to append more values to an existing header passed by the client. In addition, to see the header in the response, you must use `no-op`. ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` ##### [`value`](https://www.krakend.io/docs/backends/martian/#value "Click to copy link to value") * string The value you want to add or append. Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") ```json { "endpoint": "/test/header.Append", "input_headers": ["X-Some"], "output_encoding": "no-op", "backend": [ { "url_pattern": "/__echo/header.Append", "encoding": "no-op", "extra_config": { "modifier/martian": { "header.Append": { "scope": [ "request", "response" ], "name": "X-Some", "value": "I am" } } } } ] } ``` ### Header Blacklist The `header.Blacklist` removes the listed headers under `names` in the request and response of the backend. ##### Fields of Blacklist headers \* required fields ##### [`names`](https://www.krakend.io/docs/backends/martian/#names "Click to copy link to names") * array of strings List of all the headers you want to supress from the request or the response. If you want to see the headers in the client, you must use the `output_encoding: no-op`, and if you want the client headers to propagate to the backend, you need to use `input_headers` too. ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") The following example removes several headers from the request and the response. ```json { "endpoint": "/test/header.Blacklist", "output_encoding": "no-op", "input_headers": ["X-Some"], "backend": [ { "host": ["http://localhost:8080"], "url_pattern": "/__echo/header.Blacklist", "extra_config": { "modifier/martian": { "header.Blacklist": { "scope": ["request","response"], "names": ["X-Some", "User-Agent", "X-Forwarded-Host", "X-Forwarded-For"] } } } } ] } ``` ### Port modifier The `port.Modifier` alters the `request` URL and `Host` header to use the provided port. It accepts three different settings, but only one is accepted. ##### Fields of Port Modifier \* required fields Minimum configuration needs **one of**: `scope` + `port` , or `scope` + `defaultForScheme` , or `scope` + `remove` ##### [`defaultForScheme`](https://www.krakend.io/docs/backends/martian/#defaultForScheme "Click to copy link to defaultForScheme") boolean Uses the default port of the schema. `80` for `http://` or `443` for `https://`. Other schemas are ignored. ##### [`port`](https://www.krakend.io/docs/backends/martian/#port "Click to copy link to port") integer Defines which port will be used. ##### [`remove`](https://www.krakend.io/docs/backends/martian/#remove "Click to copy link to remove") boolean Removes the port from the host string when `true`. ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") Scopes in which this modifier acts Possible values are: `["request"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") The example below connects to a backend to port 1234, but it’s switched back to 8080 by Martian. ```json { "endpoint": "/test/port.Modifier", "output_encoding": "no-op", "input_headers": ["X-Some"], "backend": [ { "host": ["http://localhost:1234"], "url_pattern": "/__echo/port.Modifier", "extra_config": { "modifier/martian": { "port.Modifier": { "scope": ["request"], "port": 8080 } } } } ] } ``` ## Martian Filters All packages with keys like `package.Filter` are modifiers, but **add a condition to execute them**. They allow you to do a check before modifying anything. All filters have in their settings a key `modifier` which executes the declared one when the condition is met, and **optionally** an `else` key to execute another modifier when the condition is not met. Not all filters support an `else`. ### Cookie Filter The `cookie.Filter` executes the contained `modifier` when a cookie with a `name` is found. Optionally it can check also if it has a specific `value`. When the condition(s) fail(s), it executes the modifier in the `else` clause when set. ##### Fields of Cookie Filter \* required fields ##### [`else`](https://www.krakend.io/docs/backends/martian/#else "Click to copy link to else") object The modifier you want to execute when the condition does not match ##### [`modifier`](https://www.krakend.io/docs/backends/martian/#modifier "Click to copy link to modifier") * object The modifier you want to execute when the cookie name (and value if provided) matches ##### [`name`](https://www.krakend.io/docs/backends/martian/#name "Click to copy link to name") * string The name of the Cookie you want to check. Notice that the `input_headers` must contain `Cookie` in the list when you want to check cookies sent by the client. ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request"]` ##### [`value`](https://www.krakend.io/docs/backends/martian/#value "Click to copy link to value") If besides the cookie name, you set this value, it ensures the cookie has a literal match. Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") The example below inspects the Cookies in the request and looks for the one named `marketingCookies`. As there is a `value` set, too, it will make sure that it’s set to `yes`. Then it executes a `header.Modifier` that sets a new header `Accepts-Marketing-Cookies` to true or false depending on the value. Test the cookie.Filter endpoint  ``` $curl -H 'Cookie: marketingCookies=no;' http://localhost:8080/test/cookie.Filter {"req_headers":{"Accepts-Marketing-Cookies":["false"]}} ``` ```json { "endpoint": "/test/cookie.Filter", "input_headers": ["Cookie"], "backend": [ { "url_pattern": "/__echo/cookie.Filter", "allow": ["req_headers.Accepts-Marketing-Cookies"], "extra_config": { "modifier/martian": { "cookie.Filter": { "scope": [ "request" ], "name": "marketingCookies", "value": "yes", "modifier": { "header.Modifier": { "scope": [ "request" ], "name": "Accepts-Marketing-Cookies", "value": "true" } }, "else": { "header.Modifier": { "scope": [ "request" ], "name": "Accepts-Marketing-Cookies", "value": "false" } } } } } } ] } ``` ### URL filter The `url.Filter` executes its contained modifier if the `request` URL matches all of the provided parameters. Missing parameters are ignored. ##### Fields of URL Filter \* required fields ##### [`else`](https://www.krakend.io/docs/backends/martian/#else "Click to copy link to else") object The modifier you want to execute when the condition does not match ##### [`host`](https://www.krakend.io/docs/backends/martian/#host "Click to copy link to host") string The literal hostname that must match, including the port Example: `"localhost:8080"` ##### [`modifier`](https://www.krakend.io/docs/backends/martian/#modifier "Click to copy link to modifier") * object The modifier you want to execute when the condition matches ##### [`path`](https://www.krakend.io/docs/backends/martian/#path "Click to copy link to path") string The `/path` of the URL, without query strings. Example: `"/path/to"` ##### [`query`](https://www.krakend.io/docs/backends/martian/#query "Click to copy link to query") string The query strings you want to check. Use `key1=value1&key2=value2` to check that the request has exactly these keys and values (order is irrelevant, but content not). Suppose the request has more query strings than declared here because the `input_query_strings` allowed them to pass. In that case, the evaluation will be `false`, and the `else` modifier will be executed. Example: `"/path/to"` ##### [`scheme`](https://www.krakend.io/docs/backends/martian/#scheme "Click to copy link to scheme") string The literal scheme it must match Examples: `"http"` , `"https"` ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") Since the `host` and the `url_pattern` of the backend are set in the configuration, the `scheme`, `host`, and `path` parameters might provide little value. Yet, they make sense when you are copy/pasting the same modifiers across all endpoints or when you use multiple environments, and you want to mark those hosts somehow. The following example allows the user to pass a `?legacy=1` query string parameter. Then it adds a new header, `X-Legacy`, with the evaluation result. ```json { "endpoint": "/test/url.Filter", "input_query_strings": ["legacy"], "backend": [ { "url_pattern": "/__echo/url.Filter", "allow": ["req_headers"], "extra_config": { "modifier/martian": { "url.Filter": { "scope": [ "request", "response" ], "query": "legacy=1", "modifier": { "header.Modifier": { "scope": [ "request" ], "name": "X-Legacy", "value": "true" } }, "else": { "header.Modifier": { "scope": [ "request" ], "name": "X-Legacy", "value": "false" } } } } } } ] } ``` ### Regex filter The `url.RegexFilter` evaluates a regular expression ([RE2 syntax](https://golang.org/s/re2syntax)) and executes the `modifier` desired when it matches, and the modifier declared under `else` when it does not. The URL evaluation does not take into account query strings. ##### Fields of URL RegexFilter \* required fields ##### [`else`](https://www.krakend.io/docs/backends/martian/#else "Click to copy link to else") object The modifier you want to execute when the condition does not match ##### [`modifier`](https://www.krakend.io/docs/backends/martian/#modifier "Click to copy link to modifier") * object The modifier you want to execute when the condition matches ##### [`regex`](https://www.krakend.io/docs/backends/martian/#regex "Click to copy link to regex") * string The regular expression you want to check against the URL ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") In the example below, we check that the URL matches with the regexp `.*localhost.*` and set the header `Is-Localhost` accordingly. ```json { "endpoint": "/test/url.RegexFilter", "output_encoding": "no-op", "backend": [ { "url_pattern": "/__echo/url.RegexFilter", "encoding": "no-op", "extra_config": { "modifier/martian": { "url.RegexFilter": { "scope": [ "request" ], "regex": ".*localhost.*", "modifier": { "header.Modifier": { "scope": [ "request" ], "name": "Is-Localhost", "value": "true" } }, "else": { "header.Modifier": { "scope": [ "request" ], "name": "Is-Localhost", "value": "false" } } } } } } ] } ``` ### QueryString filter The `querystring.Filter` executes the `modifier` if the `request` contains a query string parameter that matches the defined name and value in the filter. You must set the `name` declared in the filter in the `input_query_strings`. ##### Fields of QueryString Filter \* required fields ##### [`else`](https://www.krakend.io/docs/backends/martian/#else "Click to copy link to else") object The modifier you want to execute when the condition does not match ##### [`modifier`](https://www.krakend.io/docs/backends/martian/#modifier "Click to copy link to modifier") * object The modifier you want to execute when the condition matches ##### [`name`](https://www.krakend.io/docs/backends/martian/#name "Click to copy link to name") * string Name of the query string you want to check Examples: `"page"` , `"limit"` ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` ##### [`value`](https://www.krakend.io/docs/backends/martian/#value "Click to copy link to value") string Value of the query string you want to check Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") ```json { "endpoint": "/test/querystring.Filter", "input_query_strings": [ "param" ], "backend": [ { "url_pattern": "/__echo/querystring.Filter", "allow": ["req_headers"], "extra_config": { "modifier/martian": { "querystring.Filter": { "scope": [ "request" ], "name": "param", "value": "true", "modifier": { "header.Modifier": { "scope": [ "request" ], "name": "X-Passed-Param", "value": "true" } } } } } } ] } ``` ### Header Filter The `header.Filter` executes its contained `modifier` if the `request` or `response` contain a header that matches the defined name and value. The `value` is optional, and only the header’s existence evaluates when undefined. ##### Fields of Header Filter \* required fields ##### [`else`](https://www.krakend.io/docs/backends/martian/#else "Click to copy link to else") object The modifier you want to execute when the condition does not match ##### [`modifier`](https://www.krakend.io/docs/backends/martian/#modifier "Click to copy link to modifier") * object The modifier you want to execute when the condition matches ##### [`name`](https://www.krakend.io/docs/backends/martian/#name "Click to copy link to name") * string Name of the header you want to check. You must add under `input_headers` the `name` included in the filter. Examples: `"X-Some"` , `"Content-Type"` ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` ##### [`value`](https://www.krakend.io/docs/backends/martian/#value "Click to copy link to value") string Value of the header you want to check Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") Example configuration that adds the query string parameter `?legacy=1` when there is a header `X-Tenant: v1`. ```json { "endpoint": "/test/header.Filter", "input_headers": [ "X-Tenant" ], "backend": [ { "url_pattern": "/__echo/header.Filter", "allow": ["req_uri"], "extra_config": { "modifier/martian": { "header.Filter": { "scope": [ "request" ], "name": "X-Tenant", "value": "v1", "modifier": { "querystring.Modifier": { "scope": [ "request" ], "name": "legacy", "value": "1" } } } } } } ] } ``` The endpoint above produces the following output. Example of header filter  ``` $curl -H 'X-Tenant: v1' http://localhost:8080/test/header.Filter {"req_uri":"/__echo/header.Filter?legacy=1"} ``` ### Header Regexp filter The `header.RegexFilter` checks that a regular expression ([RE2 syntax](https://golang.org/s/re2syntax)) passes on the target header and, if it does, executes the `modifier`. ##### Fields of Header RegexFilter \* required fields ##### [`header`](https://www.krakend.io/docs/backends/martian/#header "Click to copy link to header") * string Name of the header you want to check. You must add under `input_headers` the `name` included in the filter. Examples: `"X-Some"` , `"Content-Type"` ##### [`modifier`](https://www.krakend.io/docs/backends/martian/#modifier "Click to copy link to modifier") * object The modifier you want to execute when the condition matches ##### [`regex`](https://www.krakend.io/docs/backends/martian/#regex "Click to copy link to regex") * string The regular expression you want to check against the header value Examples: `".*localhost.*"` , `"^foo-[a-z]+$"` ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") The example below checks a header `X-App-Version` and if it contains the terminations `-alpha`, `-beta`, or `-preview`, adds to the backend request a query string `?testing=1`. ```json { "endpoint": "/test/header.RegexFilter", "input_headers": [ "X-App-Version" ], "backend": [ { "url_pattern": "/__echo/header.RegexFilter", "allow": ["req_uri"], "extra_config": { "modifier/martian": { "header.RegexFilter": { "scope": [ "request" ], "header": "X-App-Version", "regex": ".*-(alpha|beta|preview)$", "modifier": { "querystring.Modifier": { "scope": [ "request" ], "name": "testing", "value": "1" } } } } } } ] } ``` Example of output  ``` $curl -H 'X-App-Version: v1.2.3-alpha' http://localhost:8080/test/header.RegexFilter {"req_uri":"/__echo/header.RegexFilter?testing=1"} ``` ### Port filter The `port.Filter` executes its `modifier` only when the port matches the one used in the request. It does not support `else`. ##### Fields of Port Filter \* required fields ##### [`else`](https://www.krakend.io/docs/backends/martian/#else "Click to copy link to else") object The modifier you want to execute when the condition does not match ##### [`modifier`](https://www.krakend.io/docs/backends/martian/#modifier "Click to copy link to modifier") * object The modifier you want to execute when the condition matches ##### [`port`](https://www.krakend.io/docs/backends/martian/#port "Click to copy link to port") * integer The port number you want to check ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") The following example defines a backend using port `1234`, but the modifier changes it back to `8080` when this happens. ```json { "endpoint": "/test/port.Filter", "backend": [ { "host": [ "http://localhost:1234" ], "url_pattern": "/__echo/port.Filter", "extra_config": { "modifier/martian": { "port.Filter": { "scope": [ "request" ], "port": 1234, "modifier": { "port.Modifier": { "scope": [ "request" ], "port": 8080 } } } } } } ] } ``` ## Groups (Apply multiple modifiers) All the modifiers perform a single modification in the request or the response. However, the `fifo.Group` and the `priority.Group` allow you to create a list of modifiers executed sequentially or in a specific order. The group is needed when using more than one modifier and encapsulates all the following actions to perform in the `modifiers` array. ### FIFO group The `fifo.Group` holds a list of modifiers executed in first-in, first-out order. ##### Fields of FIFO group \* required fields ##### [`aggregateErrors`](https://www.krakend.io/docs/backends/martian/#aggregateErrors "Click to copy link to aggregateErrors") boolean When true, the group will continue to execute consecutive modifiers when a modifier in the group encounters an error. The Group will then return all errors returned by each modifier after all modifiers have been executed. When false, if an error is returned by a modifier, the error is returned by ModifyRequest/Response and no further modifiers are run. Defaults to `false` ##### [`modifiers`](https://www.krakend.io/docs/backends/martian/#modifiers "Click to copy link to modifiers") * array The list of modifiers you want to execute in the declared order ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") Example of usage (modify the body, and set a header): ```json { "endpoint": "/test/fifo.Group", "output_encoding": "no-op", "backend": [ { "url_pattern": "/__echo/fifo.Group", "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" } } ] } } } } ] } ``` ### Priority Group The `priority.Group` contains the modifiers you want to execute, but the order in which they are declared is unimportant. Instead, each modifier adds a `priority` attribute that defines the order in which they are run. ##### Fields of Priority group \* required fields ##### [`modifiers`](https://www.krakend.io/docs/backends/martian/#modifiers "Click to copy link to modifiers") * array The list of modifiers you want to execute, order specified in the items using `priority`. Each item of modifiers accepts the following properties: ##### [`modifier`](#modifiers-modifier "Click to copy link to modifier") * The modifier definition you want to execute ##### [`priority`](#modifiers-priority "Click to copy link to priority") * integer The assigned priority number ##### [`scope`](https://www.krakend.io/docs/backends/martian/#scope "Click to copy link to scope") * Scopes in which this modifier acts Possible values are: `["request","response"]` , `["request"]` , `["response"]` Schema: [https://www.krakend.io/schema/v2.13/modifier/martian.json](https://www.krakend.io/schema/v2.13/modifier/martian.json "JSON schema definition") Example configuration that adds the query string `first` and later `last` of foo=bar and deletes any X-Martian headers on requests: It is useful when you want to reorder them in the future, but instead of moving the whole block, you just change the priority number. ```json { "endpoint": "/test/priority.Group", "output_encoding": "no-op", "backend": [ { "url_pattern": "/__echo/priority.Group", "extra_config": { "modifier/martian": { "priority.Group": { "scope": [ "request", "response" ], "modifiers": [ { "priority": 0, "modifier" : { "querystring.Modifier": { "scope": ["request"], "name": "first", "value": "0" } } }, { "priority" : 100, "modifier" : { "querystring.Modifier": { "scope": ["request"], "name": "last", "value": "100" } } } ] } } } } ] } ``` --- # [Supported Encodings for Backend Integration](https://www.krakend.io/docs/backends/supported-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` - `fast-json` (Enterprise only) - `yaml` (Enterprise only) - `safejson` - `xml` - `rss` - `string` - `no-op` Notice that all values are in **lower case**. Unknown values for `encoding` or no value at all, is treated as `json`. Each backend declaration can set a different encoder to process the responses, and still, KrakenD can transparently work with the mixed content returning a unified encoding in the endpoint. ## 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` or `fast-json` (EE only) | | JSON inside an array/collection (`[]`) | `json` or `fast-json` with `"is_collection": true` | | JSON with variable types | `safejson` | | XML | `xml` | | RSS Feed (types Atom, RSS or JSON ) | `rss` | | YAML | `yaml` | | Not an object, but a string | `string` | | Nevermind, just proxy | `no-op` ([read how](/docs/endpoints/no-op/)) | Working with JSON arrays If you want to return to the client a JSON array instead of an object, consider using the following combinations: `output_encoding: json-collection` in your `endpoint`, and `is_collection: true` in your `backend`. See [response content types](/docs/endpoints/content-types/). When hesitating whether to use `safejson` or `json` and the `is_collection=true`, the `json` encoder is faster and more performant but less resilient: it will fail when the content doesn’t have the expected type. If you are in control of the output of the service, choose `json`, if you are not, choose `safejson`. ## Automatic content wrappers (`collection` and `content`) When the content returned by your service is wrapped inside an array instead of an object, the gateway will wrap the response inside a `collection` object. This scenario is possible with `is_collection=true` , or when the `safejson` found an array in the response. When the content returned by your service is a string, a float, integer, etc. but a type that is not an object or a collection, then the response is wrapped inside a `content` object. This scenario is possible with when the `safejson` finds a non-array or non-object type, or when you use a `string` encoding. For instance, if your backend returns a simple `Hello World!`, the response of KrakenD (`output_encoding=json`) would be `{ "content": "Hello World!" }`. Similarly, if your backend returns `[{"item": 1},{"item": 2}]` then the response of KrakenD (`output_encoding=json`) would be `{ "collection": [{"item": 1},{"item": 2}] }`. Some `output_encodings` will revert this wrapping before returning the content to the user to match the exact value provided by the backend, but this gives you the opportunity to manipulate the content and work with it at the endpoint level. The `output_encoding` of `string` will remove the `content` wrapping, while the `json-collection` while remove the `collection` wrapping. ## Example of mixed encodings The following example demonstrates how an endpoint `/abc` is feeding on three different services and urls `/a`, `/b`, and `/c` and aggregates their responses. All the information is returned in JSON (`output_encoding`) despite it is consuming heterogeneous formats: ```json { "endpoints": [ { "endpoint": "/abc", "output_encoding": "json", "backend": [ { "url_pattern": "/a", "encoding": "json", "host": [ "http://service-a.example.com" ] }, { "url_pattern": "/b", "encoding": "xml", "host": [ "http://service-b.example.com" ] }, { "url_pattern": "/c", "encoding": "rss", "host": [ "http://service-c.example.com" ] } ] } ] } ``` As you can see, having the `encoding` declaration inside every backend allows you to consume services with different content types. The endpoint `/abc` instead uses the encoding of your choice (e.g., JSON), but is feeding and merging from XML, RSS and JSON content simultaneously. --- # [Returned encodings](https://www.krakend.io/docs/endpoints/content-types/) An important concept to get familiar with is that by default, KrakenD **does not work as a reverse proxy** (unless you use the [`no-op` encoding](/docs/endpoints/no-op/)). When clients consume upstream services content through KrakenD, the response is **automatically transformed to the encoding of your choice**, independently of the encoding it had in origin, and you have the opportunity to [manipulate and aggregate data](/docs/endpoints/response-manipulation/) easily. KrakenD can send these responses back to the client **in different formats** than provided by your services (in KrakenD jargon, `backend`). We call the encoding you return to the end-user the `output_encoding`, and `encoding` the one your services return to KrakenD. The request/response flow is: ![content-type-flow.seq.mmd diagram](/images/documentation/diagrams/content-type-flow.seq.mmd.svg) - The `encoding` is how KrakenD expects to find the response data of your backends. It is declared in each [`backend` section](/docs/backends/supported-encodings/) (and you can mix types) - The `output_encoding` is how you would like to process and return all the responses to the client. It is declared in the `endpoint` section, or globally as a default for all endpoints when you add in the root level. **Example**: You can have an endpoint `/foo` that fetches content from multiple services in parallel in different formats (JSON, XML, RSS, etc.), and you define for each service the corresponding `encoding`. But you want to return the aggregated information in JSON (the `output_encoding`). You can mix encodings and return them normalized automatically. ![Output encoding diagram](/images/documentation/diagrams/content-types.mmd.svg) The diagram above illustrates a gateway returning JSON content after merging multiple sources in heterogeneous formats. ## Configuration of `output_encoding` The following `output_encoding` strategies are available to choose from for every endpoint, depending on the decoding and encoding needs you have: ### Proxy to one service - `no-op`: No operation in the response, meaning that KrakenD skips any encoding or decoding, capturing whatever content, format, and status code your backend returns. This is how most API gateway products work today, but KrakenD is not just a proxy. [See no-op documentation](/docs/endpoints/no-op/). ### Working with JSON - `json`: This is the **default encoding** when no `output_encoding` is declared or when you pass an invalid option. The endpoint always returns a JSON object to the client, no matter what the `encoding` of your backend is. - `fast-json`: Same as `json` but it’s ~140% faster on collections and ~30% on objects (average tests). Only available on the Enterprise Edition. You will notice the difference in speed of the fast-json encoding when the payloads increase in size (a small payload has an insignificant comparison to `json` encoding). - `json-collection`: Returning an array or collection is not treated equally to an object. You must use this output when the endpoint must return a JSON collection `[...]` instead of an object `{...}`. The backend response expects an object named `collection`, but this is automatically done by KrakenD when you use in the `backend` the [`is_collection` or `safejson`](/docs/backends/supported-encodings/). ### Working with non-JSON - `xml`: When the endpoint returns an XML object no matter the encoding of your backend. - `yaml`: When the endpoint returns the data in YAML format. - `string`: Treat the whole response as a simple string - `negotiate`: Allows the client to choose by parsing its `Accept` header. KrakenD accepts: - `application/json` - `application/xml` - `text/plain` (outputs in YAML) ## Output encoding examples Each endpoint declaration can define which encoder should be used, as shown in this example. By default, when the `output_encoding` is omitted, KrakenD falls back to the `output_encoding` in the root, or to JSON when none is declared. ```json { "version": 3, "output_encoding": "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 in its definition. --- # [Automatic Gzip compression on responses (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/service-settings/gzip/) KrakenD Enterprise compresses with gzip **all traffic returned to the client** when the request contains an `Accept-Encoding: gzip` header. This feature only talks about Gzipping responses KrakenD understands gzipped responses from backends and decompresses them when it needs to manipulate the response. But this functionality talks about compressing all traffic back to the client. The gzip response compression is not applied when the header is missing or when the `Accept-Encoding` header contains a `text/event-stream` value. Gzip compression is unrelated to decompression when consuming content from your services. Decompression is also done automatically, but in all KrakenD flavors, not only Enterprise. When the content is successfully compressed, the response contains a `"Content-Encoding: gzip"` header. ## Gzip configuration The gzip is always on unless you disable it in the router section as follows: ```json { "version": 3, "extra_config": { "router": { "disable_gzip": true } } } ``` ##### Fields of `"router"` \* required fields ##### [`disable_gzip`](https://www.krakend.io/docs/enterprise/service-settings/gzip/#disable_gzip "Click to copy link to disable_gzip") boolean **Enterprise only.** All the output to the end user on the Enterprise Edition uses gzip when accepted by the client. Use this flag to remove gzip compression. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/router.json](https://www.krakend.io/schema/v2.13/router.json "JSON schema definition") --- # [API Gateway Status Codes](https://www.krakend.io/docs/endpoints/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](/docs/endpoints/no-op/)) - Use custom logic to set specific status codes ## Default status codes of KrakenD endpoints The following status codes are the ones returned by the gateway. When you use `no-op` in the `output_encoding`, the user can receive any status code from your backend. These are the status codes that KrakenD can use to reply. **If you see a different one in the list, then the status code is not generated on KrakenD** : | Status Code | When | |-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `100 Continue` | Used when establishing a WebSockets connection | | `200 OK` | **At least** one backend returned a 200 or 201 status code on time. Completeness information provided by the `X-Krakend-Completed` header | | `301 Redirect` | When the router [adds a missing slash](/docs/service-settings/router-options/) to the endpoint and similar cases. | | `400 Bad Request` | Client made a malformed request, i.e. [json-schema](/docs/endpoints/json-schema/) validation failed, or problems when [signing a token](/docs/authorization/jwt-signing/) | | `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](/docs/authorization/jwt-validation/), [bot detector](/docs/throttling/botdetector/) banned it, [IP rejected](/docs/enterprise/throttling/ipfilter/), etc. | | `404 Not Found` | The requested endpoint is not configured on KrakenD | | `405 Method Not Allowed` | You have requested an endpoint that exists but not for the requested method (e.g.: you declared a GET but the request had a POST) | | `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` | | `502 Bad Gateway` | Error returned to the user when a WebSockets connection to the backend is gone after exhausting all retries | ## Returning status code from a single backend There are two things you can do: - Return the original status code of a single backend - Log error status code for your tracking If your endpoints get data from a single backend and you’d like to couple the response between the client and the server (not recommended), you can use the `no-op` encoding, or use an [alternative strategy](/docs/backends/detailed-errors/). If you use regular endpoints (e.g., `json` encoding), the status code is “calculated” by KrakenD and returns a `200` or a `500` in most cases. Nevertheless, the backend status belonging to an error gets logged. Like this: ``` WARNING [BACKEND: GET /endpoint/foo -> POST /backend/bar][Client] Status: 403 ``` ### Why does KrakenD treat errors like a `500 Internal Server Error` by default? In most cases, when there isn’t a happy path, you’ll see KrakenD returning a `500 Internal Server Error`. When KrakenD needs to combine in the final gateway response, there is no way to properly distinguish the status code from the backend and the one from the gateway itself. That’s why all errors external to KrakenD are translated into a `500 Internal Server Error`. To offer a gracefully degraded service when some backends fail, we leave the decision to the client on what to do by adding the header `X-Krakend-Completed: false` (some backends succeeded, others don’t) and also by adding the [detailed errors](/docs/backends/detailed-errors/) feature. ## Returning the status codes of the backend If you need to return the content of a backend service *as is*, then the [no-op encoding](/docs/endpoints/no-op/) will proxy the client call to the backend service without any manipulation. When the backend produces the response, it’s passed back to the client, preserving its form: body, headers, status codes, and such. An exception to this behavior is `30x` responses, which will be followed by the gateway even with `no-op` encoding. If your backend returns a `301` the client won’t follow it, but the gateway will (Enterprise [can change this](/docs/enterprise/backends/client-redirect/)) ## Returning other status codes Default status codes can be overridden per endpoint following different implementations. - [**Using no-operation**](/docs/endpoints/no-op/): When your call is not idempotent (i.e., a write operation), and you want the client to receive whatever the backend is responding. - [**Using a Lua script**](/docs/endpoints/lua/): To write in the configuration any logic, you need to evaluate and return a `custom_error`, with any status code of your choice. - As `custom_error` will end the pipe execution. If you just want to alter the status code, you can (in a no-op pipe) use the `statusCode` dynamic helper on the response. --- # [Returning the backend headers and errors](https://www.krakend.io/docs/backends/detailed-errors/) KrakenD’s default policy regarding errors and status codes is to **hide from the client any backend details**, this includes headers and errors, except when you use the [`no-op` encoding](/docs/endpoints/no-op/). The philosophy behind this is that **clients have to be decoupled** from their underlying services, as an API Gateway should do. The opposite is a reverse proxy or a simple router. ## Strategies to return headers and errors We do not recommend you to change the default behavior to have a secure and decoupled gateway. Yet, KrakenD provides flexibility so you can **override** the default policy of returning backend error details with different **strategies**. These are the different strategies you can set: - **Default strategy - Graceful degradation of the response** (when using something different than`no-op`): Supports multiple backends ([aggregation](/docs/endpoints/response-manipulation/)). HTTP status codes returned to the client are essentially `200` or `500`, regardless of the backend(s) status codes. The body is built, merged, and manipulated from the working backends. The status codes of backend errors are logged in the console. - **Headers and errors entirely handled by the backend** (default strategy for `no-op`): Shows the HTTP status codes, headers, and body as returned by the backend (**maximum one**). You cannot manipulate data (except with plugins or Lua). This option is mostly a reverse proxy, and its usage is discouraged. Use [`no-op` encoding](/docs/endpoints/no-op/) when you want this. - **Return the HTTP status code of a single backend**. Sets an empty body when there are errors (obfuscation), but preserves the HTTP status codes of the backend. Use the `return_error_code` flag in the backend ([see below](/docs/backends/detailed-errors/#return-the-http-status-code-of-a-single-backend)). - **Return the HTTP status code and error body of a single backend**. No obfuscation at all. Forwards the error body and the status code of a single backend. Use `return_error_code` and `return_error_msg` together (the latter is [declared as a `router` option](/docs/service-settings/router-options/#return_error_msg) in the service level). The Content-Type from the backend is lost. - **Return backend errors in a new key**. Supports multiple backends. Like the graceful degradation option, but it adds a new error key in the body when the backend fails -—ideal for debugging multiple backends. Use `return_error_details` (see below). - **Show the error interpretation by the gateway but not the actual error body**. Semi-obfuscation. The client sees an interpretation of the gateway like *invalid status code*, or *context deadline exceeded* but does not see the actual error delivered by the backend. The status code is always 200 or 500. Use `return_error_msg` (at the [router level](/docs/service-settings/router-options/#return_error_msg)). The flag is also compatible with `no-op`. The different combinations are exemplified below. ## Return backend errors in a new key If you prefer revealing error details to the client, you can show them in the gateway response. To achieve this, enable the `return_error_details` option in the `backend` configuration, and all errors will appear in the desired key. Place the following configuration inside the `backend` configuration: ```json { "url_pattern": "/return-my-errors", "extra_config": { "backend/http": { "return_error_details": "backend_alias" } } } ``` The `return_error_details` option sets an alias for this backend. When a backend fails, you’ll find an object named `error_` + its `backend_alias` containing the detailed errors of the backend. If there are no errors, the key won’t exist. An error example is: ```json { "error_backend_alias": { "http_status_code": 404, "http_body": "404 page not found\\n" } } ``` All status are 200 When you use `return_error_details`, all status codes returned to the client are `200`. The client must parse the response for the presence of the `error_backend_alias` or any other key you have set to determine if there’s a problem. ### Example The following configuration sets an endpoint with two backends that return its errors in two different keys: ```json { "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’s response could look like this: ```json { "error_backend_b": { "http_status_code": 404, "http_body": "404 page not found\\n" }, "foo": 42 } ``` ## Return the HTTP status code of a single backend When you have **one backend only** and use an encoding different than `no-op`, you can choose to return the original HTTP status code to the client. The gateway obfuscates the HTTP status codes of the backend by default for many reasons, including security and consistency. Still, if you prefer using the HTTP status code of the backend instead, you can enable the `return_error_code` flag. The body of the error will be empty unless you complement it with the `return_error_msg` at the `router` configuration (see below): Place the following configuration in the configuration: ```json { "version": 3, "$schema": "http://www.krakend.io/schema/krakend.json", "extra_config": { "router": { "return_error_msg": true } }, "endpoints": [ { "endpoint": "/return-status-and-error", "backend": [ { "url_pattern": "/404", "host": [ "http://somehost" ], "extra_config": { "backend/http": { "return_error_code": true } } } ] } ] } ``` Notice that the `return_error_code` and the `return_error_details` are mutually exclusive. You can use one or the other but not both. If you declare them together, the gateway will use only `return_error_details`. ## Show an interpretation but not the error body When you want to show the interpretation of the error but not the error of the backend, use the [router option `return_error_msg`](/docs/service-settings/router-options/) as follows: ```json { "version": 3, "$schema": "http://www.krakend.io/schema/krakend.json", "extra_config": { "router": { "return_error_msg": true } } } ``` --- # [Static Proxy Endpoints](https://www.krakend.io/docs/endpoints/static-proxy/) The **static proxy** aids in decorating the final response with static data. It helps deal with incomplete and degraded responses or add more content to good responses. When enabled, it injects the static `data`\** into the final response when a backend’s behavior falls within the selected strategy. The `data` is **injected** replacing any colliding keys and merging with any existing data from the backend responses. Key precedence The static proxy `data` is a final decorator and wins every fight. Any keys you add under the `data` of the static proxy, when triggered, take precedence over any existing response. If your static data uses the **same keys** you use in your backends, those colliding with the upstream will be replaced. A typical scenario for using the static proxy is **when some backend fails** and the endpoint becomes incomplete, but you prefer to provide a **stub/mock response** to enhance the functionality. When your application cannot handle well the degraded response, the static data comes in handy. Another example scenario is to **connect to an unfinished or unexisting backend**. While the backend development is not still there, the client application can have mock data that will become real data as soon as the backend starts responding. There are many other scenarios, and this is why KrakenD offers several **strategies** for deciding whether to inject static data. In any case, remember that the primary goal of this feature is to support **corner cases** related to clients not ready to deal with gracefully degraded responses. ## 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 there were no errors. The errors can come from the backend, but also from CEL evaluations, Security Policies and other components that add conditions. - `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 fails, returning an explicit error. - `incomplete`: When some backend did not reach the merge operation (timeout or another reason). This strategy does not support `no-op` as there is no evaluation of the response. Pay attention to the different strategies as they might offer **subtle differences**. The following table shows when each of the strategies will return the content inside its `data`: | Strategy | Condition to trigger | |--------------|----------------------------------------------------------| | `always` | `true` | | `success` | `err == nil` | | `complete` | `err == nil and Response != nil and Response.IsComplete` | | `errored` | `err != nil` | | `incomplete` | `Response == nil or !Response.IsComplete` | As you can see, a strategy `success` or `complete` are essentially the same, but the latter is more demanding. If you doubt between `errored` and `incomplete`, choose `errored`, because an incomplete response is surely provoked by some error. ## Handling collisions As we said above, **the static proxy is processed after all the backend merging** has occurred, meaning that if your static data has keys colliding with the existing responses, these are overwritten. The static proxy is the last execution, taking precedence over any existing response. When an endpoint aggregates data from multiple sources, if a `group` for each backend is not used, then all the responses are merged straight into the root. The static data makes the merge in the root as well, so be cautious when setting the content of `data`, to make sure you are not replacing valuable information. A piece of advice is that you add a new key under `data` to avoid undesired replacements. ## Adding static responses To add a static response, add under any `endpoint` an `extra_config` entry as follows: ```json { "endpoint": "/static", "extra_config": { "proxy": { "static": { "strategy": "errored", "data": { "YOUR STATIC JSON OBJECT": "GOES HERE" } } } } } ``` Inside the `strategy` key choose the strategy that fits your use case (one of `always`, `success`, `complete`, `errored`or `incomplete`), and inside `data` you need to add the JSON object as it’s returned. ## Static proxy example The following configuration declares two endpoints that will fail that you can test locally (copy and paste without replacements): ```json { "version": 3, "endpoints": [ { "endpoint": "/static/errored", "backend": [ { "host": [ "http://example.com" ], "url_pattern": "/foo", "group": "foo" }, { "host": [ "http://example.com" ], "url_pattern": "/bar", "group": "bar" } ], "extra_config": { "proxy": { "static": { "strategy": "errored", "data": { "errors_happened": { "foo": 42, "bar": "default bar" } } } } } }, { "endpoint": "/static/incomplete", "backend": [ { "host": [ "http://example.com" ], "url_pattern": "/foo", "group": "foo" }, { "host": [ "http://localhost:8080" ], "url_pattern": "/__health", "group": "bar" } ], "extra_config": { "proxy": { "static": { "strategy": "incomplete", "data": { "defaults": { "foo": 42, "bar": "default bar" } } } } } } ] } ``` - `/static/errored` tries to connect to two unexisting backends which will make the whole endpoint to fail at getting data, and uses the `errored` strategy in the proxy. When this happens, we return a static object that could contain some data to allow the client render something. In this case we are returning: ```json { "errors_happened": { "foo": 42, "bar": "default bar" } } ``` - `/static/incomplete` connects to a failing backend, but the other works, so we get some information. We return an object with default values the client could use to render the missing content: ```json { "defaults": { "foo": 42, "bar": "default bar" } } ``` At a practical level, the two strategies are almost the same, only that `incomplete` won’t work with `no-op`. Notice two things in the example endpoints: 1. Each backend uses a `group`, so when they work correctly, responses are inside a key “foo” or “bar”. Using this strategy, if “foo” and “bar” contain duplicate keys in the response, there is no overwrite problem. 2. The strategies do not know which backend failed or had errors, so having a different key like `defaults` or `errors_happened` allows you to have both the default data and the partial responses in the final response. And remember: the static proxy is the last piece of software evaluated, so it merges with the responses (if any) and can overwrite good data, so keeping different keys is advisable. --- # [JSON Schema Validation](https://www.krakend.io/docs/endpoints/json-schema/) KrakenD endpoints receiving a JSON object in its body can apply automatic validations using the [JSON Schema](https://json-schema.org/) vocabulary before the content passes to the backends. The json schema component allows you to define **validation rules** on the body, type definition, or even validate the fields’ values. When the validation fails, KrakenD returns to the user a status code `400` (Bad Request), and only if it succeeds, the backend receives the request. ## 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, draft-07, 2019-09 and 2020-12**. The following example **checks if the body is a json object**: ```json { "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](https://json-schema.org/) to get familiar with the specification. A full configuration for you to try on the localhost with the [debug endpoint](/docs/endpoints/debug-endpoint/) is: ```json { "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", "required": ["number", "street_name", "street_type"], "properties": { "number": { "type": "number" }, "street_name": { "type": "string" }, "street_type": { "type": "string", "enum": ["Street", "Avenue", "Boulevard"] } } } } } ] } ``` Do you want to extend this example? try [this other example](https://json-schema.org/learn/examples/address.schema.json) ### Returning the error message The default (and recommended) policy of KrakenD is to hide implementation details to the API consumers, and when a JSON schema fails, the gateway returns the `400` HTTP status code and no body. Still, you can show the **JSON schema error message** to the end user by [enabling the `return_error_msg`](/docs/service-settings/router-options/#return_error_msg) in the router options. The same example used above with the `return_error_msg` addition will output the problems when the schema does not validate: ```json { "version": 3, "port": 8080, "host": [ "http://127.0.0.1:8080" ], "extra_config": { "router": { "return_error_msg": true } }, "endpoints": [ { "endpoint": "/address", "method": "POST", "backend": [ { "url_pattern": "/__debug/" } ], "extra_config":{ "validation/json-schema": { "type": "object", "required": ["number", "street_name", "street_type"], "properties": { "number": { "type": "number" }, "street_name": { "type": "string" }, "street_type": { "type": "string", "enum": ["Street", "Avenue", "Boulevard"] } } } } } ] } ``` And when calling it incorrectly: Term  ``` $curl -i -X POST -d '{"invalid": true}' http://localhost:8080/address HTTP/1.1 400 Bad Request X-Krakend: Version 2.13.8 X-Krakend-Completed: false Date: Thu, 17 Nov 2022 08:57:53 GMT Content-Length: 96 Content-Type: text/plain; charset=utf-8 - at '': missing properties 'number', 'street_name', 'street_type' ``` --- # [Response Schema Validation for API Endpoints (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/response-schema-validator/) The response JSON schema validator adds a schema validation **before the gateway returns the response to the end-user** or before it’s merged in the endpoint with the rest of the backends. Before returning the response, you can define the minimum response fields and their characteristics through [JSON schema syntax](https://json-schema.org/) (drafts 04, 06, 07, 2019-09 and 2020-12 supported). Validation of responses, not requests This component validates that the response from the server of after content aggregation/manipulation in KrakenD has the desired attributes. To validate the user input on requests use the [`validation/json-schema`](/docs/endpoints/json-schema/) instead. You can use this plugin in conjunction with other components and perform validations, and you can insert it either in the endpoint section or the backend section. For instance, it can be part of a sequential proxy call and decide that the response does not have enough data to move forward and cancel the cascading connection. ## Configuration The configuration is straightforward, as you only need to write the desired JSON schema you expect in the response at the `endpoint` or `backend` levels (you can place it in both). For example, to validate that the backend response has a specific schema, you would do: ```json { "url_pattern": "/mybackend", "extra_config": { "validation/response-json-schema": { "schema": { "type": "object", "required": [ "user" ] }, "error": { "body": "We couldn't process you request, try again later.", "status": 401 } } } } ``` The configuration above declares a very simple JSON schema that checks the existence of a property `user` in the response. All backend manipulations (e.g.: `deny`, `allow`, `target`, etc.) take place before the validator runs, so when planning your validations make sure to take into account any previous modifications. There are other attributes you can use. This is the configuration possible: ##### Fields of Response Schema Validator \* required fields ##### [`error`](https://www.krakend.io/docs/enterprise/endpoints/response-schema-validator/#error "Click to copy link to error") object In case the validation fails, the error definition containing body and status. Example: `{"body":"We couldn't process you request, try again later.","status":401}` ##### [`body`](#error-body "Click to copy link to body") string The error message you want to show when the validation fails. Set it to an empty string `""` to show the JSON-schema validation error. Defaults to `""` ##### [`content_type`](#error-content_type "Click to copy link to content_type") string The Content-Type header you want to set back in the response when you are setting a custom `body` Example: `"application/json"` Defaults to `"text/plain"` ##### [`status`](#error-status "Click to copy link to status") integer The HTTP status code you want to set back in the response. Defaults to `500` ##### [`schema`](https://www.krakend.io/docs/enterprise/endpoints/response-schema-validator/#schema "Click to copy link to schema") * object Write your JSON schema directly in this field, with any number of fields or validations you need. Schema: [https://www.krakend.io/schema/v2.13/validation/response-json-schema.json](https://www.krakend.io/schema/v2.13/validation/response-json-schema.json "JSON schema definition") At the endpoint level the validator works exactly the same, and runs also after other manipulations. Here is the same example but with more detail on the validated JSON Schema: ```json { "endpoint": "/foo", "extra_config": { "validation/response-json-schema": { "schema": { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "user": { "type": "object", "properties": { "username": { "type": "string" }, "user_id": { "type": "number" }, "status": { "type": "string", "enum": [ "registered", "banned" ] } }, "required": [ "username", "user_id" ] } }, "required": [ "user" ] }, "error": { "body": "We couldn't process you request, try again later.", "status": 401 } } } } ``` ## Migration from old plugin Prior to v2.9 the `response-schema-validator` offered this functionality, and is now deprecated as it is supported natively. If you used the plugin you will need to execute the following change in your configuration: ```diff { "endpoint": "/foo", "extra_config": { - "plugin/req-resp-modifier": { - "name": [ - "response-schema-validator" - ], - "response-schema-validator": { - "schema": {}, - "error": { - "body": "The server encountered an internal error. Please try again later.", - "status": 500 - } + "validation/response-json-schema": { + "schema": {}, + "error": { + "body": "The server encountered an internal error. Please try again later.", + "status": 500 } } } ``` As you can see the following changes are needed: - The `plugin/req-resp-modifier` is renamed to `"validation/response-json-schema` - The `name` property goes away - The extra `response-schema-validator` level goes away - The attributes `schema` and `error` stay the same. --- # [Limiting requests' payload maximum size (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/maximum-request-size/) Consumers of the gateway might see their requests limited when using the `max_payload` property. All requests are validated when this attribute is set, checking that their size is inside the limits set. There are two possible placements of this attribute: - At the service level: limits the requests of all activity - At the endpoint level: limits the requests of specific endpoints You can set both limits simultaneously if you want. When both limits exist, the service level limit is checked first, and if it passes, then it checks the endpoint limit. This means that the endpoint `max_payload` must be equal to or smaller than the one at the service. Otherwise, the endpoint limit will never have an opportunity to work. When users exceed the maximum number of bytes, they get the following status code: ``` HTTP/1.1 413 Request Entity Too Large ``` Maximum payload of Gzipped content When you use `max_payload` in combination with `decompress_gzip` when Gzip content goes through KrakenD, it is decompressed in the first place and the maximum payload applies to the decompressed payload. ## Limit the request size of all calls When you want to limit the maximum request size of any request, add the following configuration at the service level under the `router` property: ```json { "version": 3, "extra_config": { "router": { "max_payload": 1024 } } } ``` The configuration above limits all requests to 1KB (1024 bytes) ##### Fields of `"router"` \* required fields ##### [`decompress_gzip`](https://www.krakend.io/docs/enterprise/endpoints/maximum-request-size/#decompress_gzip "Click to copy link to decompress_gzip") integer **Enterprise only.** Decompresses any Gzipped content before sending it to the backend when the `Content-Encoding` has `gzip` in the first position. You can also set this value [per endpoint](https://www.krakend.io/docs/enterprise/endpoints/maximum-request-size/). Defaults to `0` ##### [`max_payload`](https://www.krakend.io/docs/enterprise/endpoints/maximum-request-size/#max_payload "Click to copy link to max_payload") integer **Enterprise only.** Limits the maximum number of bytes a user can send to the gateway. `0` means no limit. You can also set this value [per endpoint](https://www.krakend.io/docs/enterprise/endpoints/maximum-request-size/). Defaults to `0` Schema: [https://www.krakend.io/schema/v2.13/router.json](https://www.krakend.io/schema/v2.13/router.json "JSON schema definition") ## Limit the maximum request size to an endpoint When you want to limit the maximum payload you can send to an endpoint, place the flag under the `proxy` entry of the endpoint. Remember that if you have also set the limit in the `router`, that limit will kick in first. This is what you need to add to limit an endpoint: ```json { "endpoint": "/body_size", "method":"POST", "backend": [ { "url_pattern": "/__debug/body_size" } ], "extra_config":{ "proxy":{ "max_payload": 1024 } } } ``` ##### Maximum payload configuration for proxy's extra\_config \* required fields ##### [`decompress_gzip`](https://www.krakend.io/docs/enterprise/endpoints/maximum-request-size/#decompress_gzip "Click to copy link to decompress_gzip") boolean **Enterprise only.** Decompresses any Gzipped content before sending it to the backend when the `Content-Encoding` has `gzip` in the first position. You can also set this value globally at the [service level](https://www.krakend.io/docs/enterprise/service-settings/router-options/#max_payload). Defaults to `false` ##### [`max_payload`](https://www.krakend.io/docs/enterprise/endpoints/maximum-request-size/#max_payload "Click to copy link to max_payload") integer **Enterprise only.** Limits the maximum number of bytes a user can send to the endpoint. `0` means no limit. You can also set this value globally at the [service level](https://www.krakend.io/docs/enterprise/service-settings/router-options/#max_payload). Defaults to `0` Schema: [https://www.krakend.io/schema/v2.13/endpoint\_extra\_config.json](https://www.krakend.io/schema/v2.13/endpoint_extra_config.json "JSON schema definition") --- # [Workflows (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/workflows/) The Workflow component allows you to create **complex API flows** behind an endpoint. In a regular endpoint, you connect a route to one or several backends, but what if you could **nest other endpoints** in any backend call? The workflow component allows you, amongst other things, to: - **Add more functionality to your backends without changing them**: like decorating responses of existing backends, sending additional messages to queues, and other functionality. - **Create unlimited nested calls**: The user calls an endpoint that internally calls one or more services, and at the same time, they can call other services again and again to create complex workflows - **Combine sequential and parallel flows**: While a regular endpoint offers sequential **OR** concurrent connections, with the workflow, you can connect sequentially **AND** in concurrent using the combinations that better work for you. - **Intermediate manipulations**: Sometimes, you must do multiple manipulations and create intermediate states before another backend can use the data. - **Set conditional logic**: Add multiple backends but call only those that comply with your business logic - **Reduce client API calls**: Move API calls that are done by the client to a new endpoint and save all the traffic and computation to the client - **Continue the flow on errors**: While the flow of regular endpoints halts when errors are found, the workflow can continue operations even in those cases. In summary, a workflow can be seen as a **nested endpoint with no route exposed** to the consumers. In combination with [sequential](/docs/enterprise/endpoints/sequential-proxy/) backends, [security policies](/docs/enterprise/security-policies/), [API composition](/docs/enterprise/endpoints/response-manipulation/), and many other KrakenD features, you can manipulate data and perform complex jobs that would be impossible to achieve otherwise. ## How Workflows work When you declare an endpoint, you always add one or more backends to determine where KrakenD will connect. Workflows add the capability of adding **more internal endpoints under a backend**, so you start new processes when the backend is hit. As very few limitations apply, you can use the new internal endpoints for aggregation, conditional requests, or anything you want. The workflow can reduce the number of API calls a client application needs to make to complete a job on a microservices architecture. Looking at the bigger picture, here’s how the workflows act in a KrakenD pipe: ![Data flow](/images/documentation/diagrams/components-sequence-simple.mmd.svg) As you can see, when you fetch data from a backend (whether initiated by an end-user or an [async agent](/docs/enterprise/async/) through an endpoint), you can repeatedly initiate the flow of another endpoint (saving you from the unnecessary HTTP processing). ## Workflow declaration A `workflow` object always lives under a `backend`’s `extra_config` and, from a functional perspective, is precisely like any other `endpoint` object. The significant differences are that its first parent `endpoint` object handles the end-user HTTP request (like validating the JWT token, applying CORS, etc.), while the `workflow` kicks in in a later stage that has all the HTTP processing completed by its parent and concentrates on the data manipulation and proxy parts. It is important to notice that the `workflow` has an `endpoint` wrapped inside its configuration, but its **route is unpublished and inaccessible**. Skipping the unrelated parts, the addition of a workflow looks like this (this is a conceptual, non-valid, configuration): ```yaml endpoint: /foo/{param} backend: url_pattern: /__workflow/unused-pattern extra_config: worfklow: endpoint: /__workflow/unpublished-route/{param} backend: url_pattern: /backend-2 ``` Get into an agreement with your team As you can see, as the gateway sets no limit to the complexity and level of nesting you can do, you could implement crazy ideas. If you use complex workflows, you and your team should go into a handshake deal to set limits and boundaries to keep everyone’s sanity. From this syntax, you need to understand the following concepts: - You could repeatedly add another workflow inside any `backend`. There is no logical limit to nested `workflow` components. The limits are the latency and machine resources. - The workflow can include as many backends as you want, sequential or not. - The `endpoint` inside the `workflow` (`/__workflow/unpublished-route/{param}` above) must have any `{params}` you will use inside the workflow’s `backend`. Besides that, this endpoint is solely to identify the log activity when triggering a workflow, as its HTTP route does not exist. It is not required that it starts with `/__workflow` but that helps when you read the logs. - The `url_pattern` declared at the immediate superior level of a `workflow` (here `/__workflow/unused-pattern`), from a connection perspective, **is not used at all**. Yet, it has an important function: declaring the [dynamic routing](/docs/enterprise/endpoints/dynamic-routing/) (e.g., `{JWT.sub}`) and [sequential proxy](/docs/enterprise/endpoints/sequential-proxy/) variables (e.g., `{resp0_id}`)you will reuse in the workflow. While the `url_pattern` you choose is unusued for anything else than logs, if dynamic and sequential proxy variables do not exist in the `url_pattern`, inner levels won’t have access to these variables. We recommend you again writing `/__workflow/` or something that helps you identify it in the logs and visualize that this is not any call to a service. - If you have a `host` list outside the `workflow`, all backends inside will use it by default, so you can skip the declaration if they are all the same. - The endpoint will stop any workflow when its `timeout` is reached. If you need larger timeouts, remember to declare them decreasing (e.g., the endpoint timeout is larger than the backend/workflow timeout). - Unlike `endpoints`, workflows can continue with the rest of the backends if you use the `ignore_errors` flag. - From a Telemetry point of view, workflows get their share too! ## Workflow configuration Experimental syntax The declarative workflow configuration is considered experimental in its first release of v2.7 because future changes could be needed given its endless uses and combinations. You’ll find the following configuration familiar as it is like an `endpoint` with very few differences: ##### Fields of Workflow Object \* required fields ##### [`backend`](https://www.krakend.io/docs/enterprise/endpoints/workflows/#backend "Click to copy link to backend") * array of objects List of all the [backend objects](https://www.krakend.io/docs/backends/) called within this workflow. Each backend can initiate another workflow if needed. ##### [`concurrent_calls`](https://www.krakend.io/docs/enterprise/endpoints/workflows/#concurrent_calls "Click to copy link to concurrent_calls") integer The concurrent requests are an excellent technique to improve the response times and decrease error rates by requesting in parallel the same information multiple times. Yes, you make the same request to several backends instead of asking to just one. When the first backend returns the information, the remaining requests are canceled. Defaults to `1` ##### [`endpoint`](https://www.krakend.io/docs/enterprise/endpoints/workflows/#endpoint "Click to copy link to endpoint") * string An endpoint name for the workflow that will be used in logs. The name will be appended to the string `/__workflow/` in the logs, and although it does not receive traffic under this route, it is necessary when you want to pass URL `{params}` to the nested backends. Example: `"/workflow-1/{param1}"` ##### [`extra_config`](https://www.krakend.io/docs/enterprise/endpoints/workflows/#extra_config "Click to copy link to extra_config") object Configuration entries for additional components that are executed within this endpoint, during the request, response or merge operations. ##### [`ignore_errors`](https://www.krakend.io/docs/enterprise/endpoints/workflows/#ignore_errors "Click to copy link to ignore_errors") Allow the workflow to continue with the rest of declared actions when there are errors (like security policies, network errors, etc). The default behavior of KrakenD is to abort an execution that has errors as soon as possible. If you use conditional backends and similar approaches, you might want to allow the gateway to go through all steps. Defaults to `false` ##### [`output_encoding`](https://www.krakend.io/docs/enterprise/endpoints/workflows/#output_encoding "Click to copy link to output_encoding") The gateway can work with several content types, even allowing your clients to choose how to consume the content. See the [supported encodings](https://www.krakend.io/docs/endpoints/content-types/) Possible values are: `"json"` , `"json-collection"` , `"yaml"` , `"fast-json"` , `"xml"` , `"negotiate"` , `"string"` , `"no-op"` Defaults to `"json"` ##### [`timeout`](https://www.krakend.io/docs/enterprise/endpoints/workflows/#timeout "Click to copy link to timeout") string The duration you write in the timeout represents the **whole duration of the pipe**, so it counts the time all your backends take to respond and the processing of all the components involved in the endpoint (the request, fetching data, manipulation, etc.). By default the timeout is taken from the parent endpoint, if redefined **make sure that is smaller than the endpoint’s** Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Examples: `"2s"` , `"1500ms"` Schema: [https://www.krakend.io/schema/v2.13/workflow.json](https://www.krakend.io/schema/v2.13/workflow.json "JSON schema definition") Here is an elementary example of a workflow you can try locally: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "echo_endpoint": true, "debug_endpoint": true, "endpoints": [ { "endpoint": "/test", "extra_config": { "proxy": { "sequential": true } }, "@comment": "Because there is a sequential proxy the two first level backends are executed in order", "backend": [ { "host": ["http://localhost:8080"], "url_pattern": "/__debug/call-1", "group": "call-1" }, { "host": ["http://localhost:8080"], "url_pattern": "/__debug/call-2", "group": "call-2", "extra_config": { "workflow": { "endpoint": "/call-2", "@comment": "Call 2A and 2B are fetched in parallel because there is no sequential proxy inside the workflow", "backend": [ { "url_pattern": "/__debug/call-2A", "group": "call-2A" }, { "url_pattern": "/__debug/call-2A", "group": "call-2B" } ] } } } ] } ] } ``` The example above calls three backend servers for one endpoint call and returns a structure like this: - `call-1` - `call-2` - `call-2A` - `call-2B` Notice that because there is a `sequential` proxy flag, the calls 1 and 2 are fetched one after the other. But the calls 2A and 2B are fetched concurrently, because there is no sequential configuration inside the second backend. Another important takeaway is that `"url_pattern": "/__debug/call-2"` is never called. This is because when there is a `workflow` object inside a backend, the patterns and hosts used are those inside its inner `backend` definition. Still, `url_pattern` in the superior levels is needed to define the dynamic variables you can use inside the workflows. Let’s see a practical example. Here is a short flow: ![Workflows Example](/images/documentation/diagrams/workflows-example.mmd.svg) In the example above, a user signs up using a single endpoint on KrakenD, and the gateway calls a legacy server. Up to this point, this could be a regular endpoint, but when the successful response from the legacy service comes in, we want to start a workflow with two more concurrent calls: one inserting an event into a queue for other microservices to be notified and another triggering email sending. In this example, our old legacy application grows in functionality without actually coding on it. If developers won’t touch legacy code with a ten-foot pole, this strategy helps them add new services without changing the API contract there was with the end user. This example mixes concurrent and sequential calls (the lines do not reveal the difference between concurrent and sequential). The configuration would be: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "host": [ "http://localhost:8080" ], "debug_endpoint": true, "echo_endpoint": true, "endpoints": [ { "@comment": "signup endpoint for /user/signup/yes and /user/signup/no", "method": "POST", "endpoint": "/user/signup/{wants_notifications}", "input_headers": [ "User-Agent" ], "extra_config": { "proxy": { "@comment": "We want our first group of backends to register in order (sequentially)", "sequential": true } }, "backend": [ { "@comment": "Call to the legacy service registering the user first", "method": "POST", "url_pattern": "/__debug/user-registration", "group": "legacy-response" }, { "@comment": "Additional services next. Declare the 'message' field from the legacy response, user agent, and params", "url_pattern": "/__workflow/{resp0_legacy-response.message}/{input_headers.User-Agent}/{wants_notifications}/", "group": "additional-services", "extra_config": { "workflow": { "ignore_errors": true, "endpoint": "/workflow1/{wants_notifications}/{resp0_legacy-response.message}", "@comment": "Backends below will be executed concurrently after the legacy service has been called and the signup was ok (returned a 'message')", "backend": [ { "@comment": "publish a message to the queue", "url_pattern": "/__debug/you-could-replace-this-with-a-rabbitmq?newuser={resp0_legacy-response.message}", "group": "notification-service" }, { "@comment": "trigger a welcome email only when when user wants notifications ('yes')", "url_pattern": "/__echo/welcome/{resp0_legacy-response.message}?ua={input_headers.User-Agent}", "extra_config": { "security/policies": { "req": { "policies": [ "req_params.Wants_notifications == 'yes'" ] } } }, "allow": [ "req_uri" ], "mapping": { "User-Agent": "browser" }, "group": "send-email" } ] } } } ] } ] } ``` The response for the following configuration is, when calling `/user/signup/yes` Response when user wants notifications  ``` $curl -XPOST http://localhost:8080/user/signup/yes | jq { "additional-services": { "notification-service": { "message": "pong" }, "send-email": { "req_uri": "/__echo/welcome/pong?ua=curl/8.6.0" } }, "legacy-response": { "message": "pong" } } ``` And when calling `/user/signup/no` Response when user does not want email notifications  ``` $curl -XPOST http://localhost:8080/user/signup/yes | jq { "additional-services": { "notification-service": { "message": "pong" } }, "legacy-response": { "message": "pong" } } ``` Try this example locally and play with it to understand the flow. --- # [Security Overview](https://www.krakend.io/docs/security/) KrakenD is a software built with a **security-first philosophy**. In 2022, we became a recognized **CVE Numbering Authority (CNA)** worldwide for software distribution ([Partner page](https://www.cve.org/PartnerInformation/ListofPartners/partner/KrakenD)), and we publish CVE Records for vulnerabilities within any KrakenD software or the Lura Project (© the Linux Foundation). In addition, our company is [SOC 2 Type II Certified](/blog/soc2-certification-announcement/) and you can get more information on our [Trust Center](https://trust.krakend.io/). [Security Advisories](/categories/security/ "Security Advisories") [Security Policy](/security-policy/ "Security Policy") ## Secure by design At KrakenD, security is not just an add-on; it’s a **design principle baked into every component**. The [Zero-trust design](/docs/design/zero-trust/) is the foundational philosophy. From blocking unauthorized access to rejecting untrusted traffic by default or even not logging sensitive data, KrakenD ensures a minimal attack surface by enforcing strict controls over headers, parameters, and tokens. Our *Security Program Policy and Incident Response Plan* have the following principles: - **Secure Development and Proactive Threat Detection**: To ensure that KrakenD is secure, we conduct continuous automated code analysis, vulnerability assessments, and other security measures integrated into the CI/CD pipeline. - **Software Integrity**: To protect the codebase’s and software’s integrity by enforcing security measures that prevent unauthorized changes, reduce human error, and mitigate potential security vulnerabilities in real time. - **Rapid Incident Response**: To ensure a quick and effective response to security incidents and minimize their impact through defined protocols for containment, eradication, recovery, and post-incident analysis. - **Compliance with Industry Standards**: To ensure KrakenD’s software adheres to industry standards and security frameworks, such as OWASP best practices, and complies with regulatory requirements for enterprises. - **Enterprise-Ready Security**: To provide a robust security framework suitable for large-scale enterprise deployment, ensuring that all software produced by KrakenD is safe, scalable, and reliable for its enterprise customers. Below are the categories in which security is more obvious. Although this is not a complete list, it provides you with a place to start exploring our documentation. ## Authentication and Authorization API authentication and authorization are key to any secured API. KrakenD has mechanisms such as [JWT validation](/docs/authorization/jwt-validation/), [JWT signing](/docs/authorization/jwt-signing/), [OAuth2 Client Credentials](/docs/authorization/client-credentials/) or [API keys](/docs/enterprise/authentication/api-keys/) Enterprise to name a few examples. Authorization allows you to implement Role-based (RBAC) and attribute-based access control (ABAC) policies. In addition, if you need to invalidate legitimate tokens that are still within a valid TTL, KrakenD supports [JWT token revocation using bloom filters](/docs/authorization/revoking-tokens/) and [centralized token revocation servers](/docs/enterprise/authentication/revoke-server/), ensuring revoked tokens are immediately invalidated across all KrakenD nodes. ## Encryption and Secure Communication The gateway supports [TLS](/docs/service-settings/tls/) for traffic coming from consumers (server) and also between KrakenD and your services (client). It defaults to TLS 1.3 unless downgraded by config. For business-to-business authentication, [Mutual TLS (mTLS)](/docs/authorization/mutual-authentication/) creates a secure and exclusive channel based on trusted certificates. Governments can also get a Docker container with [FIPS 140-2 validated cryptography](/docs/enterprise/security/fips-140/) Enterprise for compliance with their regulations. ## Data protection Showing the right data or allowing limited access is key on any API. In addition to blocking users without enough privileges to consume data, you can apply [data filtering and manipulation](/docs/enterprise/backends/data-manipulation/) or even [masking of data](/docs/enterprise/endpoints/content-replacer/) Enterprise In addition, to prevent malicious or malformed requests, KrakenD allows you to [validate the payload](/docs/endpoints/json-schema/) of requests against a JSON schema before it reaches your service. But it also works the other way around: you can also [validate responses](/docs/enterprise/endpoints/response-schema-validator/) Enterprise of your services against a schema and decide whether is worth or not returning it to the end user. Finally, the [Security policy engine](/docs/enterprise/security-policies/) is designed to enforce complex business logic based on real-time evaluation of requests, responses, and tokens. ## Traffic Control [**API Throttling**](/docs/enterprise/throttling/) is a dragon of many heads. You might want to limit the throughput your users do against your API with one of the many rate-limiting strategies: [per-service](/docs/enterprise/service-settings/service-rate-limit/), [per-tier](/docs/enterprise/service-settings/tiered-rate-limit/) (both Enterprise ) , [per-endpoint](/docs/endpoints/rate-limit/), [per-user](/docs/endpoints/rate-limit/#client-rate-limiting-client_max_rate), or [per-proxy](/docs/backends/rate-limit/). Another key security component is the [Circuit Breaker](/docs/backends/circuit-breaker/), which automatically blocks calls to failing backends, **preventing cascading failures** and reducing the load on a suffering system. Then, depending on your environment you might want to enable [IP Filtering](/docs/enterprise/throttling/ipfilter/) or [GeoIP filtering](/docs/enterprise/endpoints/geoip/) to restrict API traffic based on IP addresses, CIDR ranges, or geography (both are Enterprise ), [Bot detection](/docs/throttling/botdetector/),or enable conditional requests with [Conditional Expression Language](/docs/endpoints/common-expression-language-cel/) (CEL) or Security Policies (also Enterprise ). ## HTTPS Security and OWASP Recommendations KrakenD follows **OWASP best practices** and security recommendations, incorporating several protections by just declaring the security component: - [Host Restriction](/docs/service-settings/security/#restrict-connections-by-host): Restrict connections by host, defining a list of backends that the API gateway can communicate with. - [Cross-Origin Resource Sharing (CORS)](/docs/service-settings/cors/) lets you control and limit which domains can access APIs, protecting against cross-origin attacks. - [HTTP Strict Transport Security (HSTS)](/docs/service-settings/security/#http-strict-transport-security-hsts) makes sure that all interactions with the gateway use HTTPS, mitigating protocol downgrade attacks. - **Public Key Pinning**: To [prevent certificate forgery](/docs/service-settings/security/#http-public-key-pinning-hpkp), HPKP allows you to “pin” a public key, ensuring clients connect to the intended service. - **Clickjacking Protection**: To activate frame-busting mechanisms by [configuring X-Frame-Options headers](/docs/service-settings/security/#clickjacking-protection). - **Cross-Site Scripting (XSS) Protection**: Mitigate XSS attacks by adding relevant security headers like [X-XSS-Protection](/docs/service-settings/security/#cross-site-scripting-xss-protection), protecting clients from malicious script injections. - **MIME Sniffing Prevention** prevents browsers from [MIME sniffing](/docs/service-settings/security/#mime-sniffing-prevention) and interpreting files as a different content type than declared by using the X-Content-Type-Options header. ## Monitoring, Auditing, and Logging Logging and Monitoring, like OpenTelemetry, Prometheus, New Relic, Datadog, and other integrations, ensure that audit trails are available for all requests and responses, which is crucial for forensics and compliance. Another part directly related to security is the [automatic audit of configuration](/docs/configuration/audit/), a step in your build process that checks whether your configuration has security problems or it can be improved before going live. ## Tested by many KrakenD’s security is strengthened by the fact that **it is tested by thousands of servers every day** across diverse environments, geographies, and use cases. This extensive usage (approx. 2 million servers/month) means that potential vulnerabilities are identified and addressed quickly, as real-world scenarios expose the system to a wide range of security challenges. Continuous feedback from a large community of developers ensures that KrakenD remains resilient to new threats, benefits from community-driven improvements, and maintains robust security practices. This collective testing approach makes KrakenD more secure and reliable over time. ## No data storage As KrakenD operates as a stateless gateway, only processes data in transit and **does not store any information**. Since KrakenD does not retain user data, logs, or any sensitive information, it reduces the risk of data breaches or unauthorized access. This design ensures that all data flows securely through the system without lingering in any storage, making KrakenD inherently more secure and compliant with heavy data privacy regulations (banking, health, insurance, etc), as it minimizes the exposure of sensitive information in an eventual breach. --- # [Cross-Origin Resource Sharing (CORS) Configuration](https://www.krakend.io/docs/service-settings/cors/) When KrakenD endpoints are consumed from a browser, you should enable the **Cross-Origin Resource Sharing (CORS)** module, as browsers restrict cross-origin HTTP requests initiated from scripts. When the Cross-Origin Resource Sharing (CORS) configuration is enabled, KrakenD uses additional HTTP headers to tell browsers that they can **use resources from a different origin** (domain, protocol, or port). For instance, you will need this configuration if your web page is hosted at [https://www.domain.com](https://www.domain.com) and the Javascript references the KrakenD API at [https://api.domain.com](https://api.domain.com). ## 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: ```json { "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: ##### Fields of CORS \* required fields ##### [`allow_credentials`](https://www.krakend.io/docs/service-settings/cors/#allow_credentials "Click to copy link to allow_credentials") boolean When requests can include user credentials like cookies, HTTP authentication or client side SSL certificates. Defaults to `false` ##### [`allow_headers`](https://www.krakend.io/docs/service-settings/cors/#allow_headers "Click to copy link to allow_headers") array of strings An array with the headers allowed, but `Origin`is always appended to the list. Requests with headers not in this list are rejected. Example: `["Accept-Language"]` Defaults to `[]` ##### [`allow_methods`](https://www.krakend.io/docs/service-settings/cors/#allow_methods "Click to copy link to allow_methods") array An array with all the HTTP methods allowed, in uppercase. Possible values are `GET`, `HEAD`,`POST`,`PUT`,`PATCH`,`DELETE`, or `OPTIONS` Defaults to `["GET","HEAD","POST"]` ##### [`allow_origins`](https://www.krakend.io/docs/service-settings/cors/#allow_origins "Click to copy link to allow_origins") array of strings An array with all the origins allowed, the use of one `*` is permitted to allow groups of hosts. Examples of values are `https://example.com`, `https://example.*`, `https://*.example.com` `or just`\*\` (any origin). Defaults to `["*"]` ##### [`allow_private_network`](https://www.krakend.io/docs/service-settings/cors/#allow_private_network "Click to copy link to allow_private_network") boolean Indicates whether to accept cross-origin requests over a private network. Defaults to `false` ##### [`debug`](https://www.krakend.io/docs/service-settings/cors/#debug "Click to copy link to debug") boolean Show debugging information in the logger, **use it only during development**. Defaults to `false` ##### [`expose_headers`](https://www.krakend.io/docs/service-settings/cors/#expose_headers "Click to copy link to expose_headers") array of strings List of headers that are safe to expose to the API of a CORS API specification. Defaults to `["Content-Length","Content-Type"]` ##### [`max_age`](https://www.krakend.io/docs/service-settings/cors/#max_age "Click to copy link to max_age") string For how long the response can be cached. For zero values the `Access-Control-Max-Age` header is not set. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0h"` ##### [`options_passthrough`](https://www.krakend.io/docs/service-settings/cors/#options_passthrough "Click to copy link to options_passthrough") boolean Instructs preflight to let other potential next handlers to process the OPTIONS method. Turn this on when you set the [`auto_opts` flag in the router to `true`](https://www.krakend.io/docs/service-settings/router-options/#auto_options). Defaults to `false` ##### [`options_success_status`](https://www.krakend.io/docs/service-settings/cors/#options_success_status "Click to copy link to options_success_status") integer The HTTP status code that is considered a success. Example: `200` Defaults to `204` Schema: [https://www.krakend.io/schema/v2.13/security/cors.json](https://www.krakend.io/schema/v2.13/security/cors.json "JSON schema definition") Allow credentials and wildcards According to the CORS specification, you are not allowed to use wildcards and credentials at the same time. If you need to do this, [check this workaround](https://github.com/krakend/krakend-cors/issues/9) ## Debugging configuration The following configuration might help you debug your CORS configuration. Check the inline `@comments`: ```json { "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" } ] } ]} ``` ## Adding the OPTIONS method When working in a SPA, you will usually receive `OPTIONS` calls to KrakenD; although this configuration is not related to CORS, it usually goes in hand. To support `OPTIONS` in your endpoints, you only need to add the [flag `auto_options`](/docs/service-settings/router-options/#auto_options) as follows: ```json { "version": 3, "extra_config": { "router": { "auto_options": true }, "security/cors": { "@comment": "...CORS configuration inside this block..." } } } ``` ##### Fields of `"router"` \* required fields ##### [`auto_options`](https://www.krakend.io/docs/service-settings/cors/#auto_options "Click to copy link to auto_options") boolean When true, enables the autogenerated OPTIONS endpoint for all the registered paths Schema: [https://www.krakend.io/schema/v2.13/router.json](https://www.krakend.io/schema/v2.13/router.json "JSON schema definition") --- # [TLS Configuration](https://www.krakend.io/docs/service-settings/tls/) The TLS settings define the parameters that the gateway takes into account to handle incoming and outgoing HTTPS traffic. We refer to this as: - `tls`: **TLS settings**, or how the gateway handles incoming traffic as a server. - `client_tls`: **Client TLS settings**, or how the gateway connects to your upstream services ![TLS diagram](/images/documentation/diagrams/tls.mmd.svg) Independent properties The properties `tls` and `client_tls` are independent of each other. You can declare one, both, or none. ## TLS server settings 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](/docs/throttling/load-balancing/) (e.g., ELB, HAproxy) If you want to listen with TLS, add a `tls` key at the service level (configuration’s file root) with at least the public and private keys. When you add TLS, KrakenD listens **only using TLS**, and no traffic to plain HTTP is accepted (no redirection). If you want to enable mTLS see [Mutual TLS configuration](/docs/authorization/mutual-authentication/) Secure by default When you don’t set any other parameters than stated below, KrakenD defaults to very strong security. **Only TLS 1.3 is accepted** (with its three cipher suites), and attempts to negotiate other versions will fail. Nevertheless, you can change this behavior. To start KrakenD with TLS, you need to provide a certificate for both the public and the private keys: ```json { "version": 3, "tls": { "keys": [ { "public_key": "/path/to/cert.pem", "private_key": "/path/to/key.pem" } ] } } ``` All TLS options for the server go inside the `tls` object: ##### Fields of TLS/SSL \* required fields Minimum configuration needs **any of**: `keys` , or `null` object ##### [`ca_certs`](https://www.krakend.io/docs/service-settings/tls/#ca_certs "Click to copy link to ca_certs") array of strings An array with all the CA certificates you would like to load to KrakenD **when using mTLS**, in addition to the certificates present in the system’s CA. Each certificate in the list is a relative or absolute path to the PEM file. If you have a format other than PEM, you must convert the certificate to PEM using a conversion tool. See also `disable_system_ca_pool` to avoid system’s CA. Example: `["ca.pem"]` Defaults to `[]` ##### [`cipher_suites`](https://www.krakend.io/docs/service-settings/tls/#cipher_suites "Click to copy link to cipher_suites") array of integers The list of cipher suites as defined in the documentation. Defaults to `[4865,4866,4867]` ##### [`curve_preferences`](https://www.krakend.io/docs/service-settings/tls/#curve_preferences "Click to copy link to curve_preferences") array The list of all the identifiers for the curve preferences. Use `23` for CurveP256, `24` for CurveP384 or `25` for CurveP521. Defaults to `[23,24,25]` ##### [`disable_system_ca_pool`](https://www.krakend.io/docs/service-settings/tls/#disable_system_ca_pool "Click to copy link to disable_system_ca_pool") boolean Ignore any certificate in the system’s CA. The only certificates loaded will be the ones in the `ca_certs` list when true. Defaults to `false` ##### [`disabled`](https://www.krakend.io/docs/service-settings/tls/#disabled "Click to copy link to disabled") boolean A flag to disable TLS (useful while in development). Defaults to `false` ##### [`enable_mtls`](https://www.krakend.io/docs/service-settings/tls/#enable_mtls "Click to copy link to 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. Defaults to `false` ##### [`keys`](https://www.krakend.io/docs/service-settings/tls/#keys "Click to copy link to keys") array An array with all the key pairs you want the TLS to work with. You can support multiple and unrelated domains in a single process. Each item of keys accepts the following properties: ##### [`private_key`](#keys-private_key "Click to copy link to private_key") string Absolute path to the private key, or relative to the current [working directory](https://www.krakend.io/docs/configuration/working-directory/). Examples: `"/path/to/key.pem"` , `"./certs/key.pem"` Defaults to `"./certs/key.pem"` ##### [`public_key`](#keys-public_key "Click to copy link to public_key") string Absolute path to the public key, or relative to the current [working directory](https://www.krakend.io/docs/configuration/working-directory/). Examples: `"/path/to/cert.pem"` , `"./certs/cert.pem"` Defaults to `"./certs/cert.pem"` ##### [`max_version`](https://www.krakend.io/docs/service-settings/tls/#max_version "Click to copy link to max_version") Maximum TLS version supported. Possible values are: `"SSL3.0"` , `"TLS10"` , `"TLS11"` , `"TLS12"` , `"TLS13"` Defaults to `"TLS13"` ##### [`min_version`](https://www.krakend.io/docs/service-settings/tls/#min_version "Click to copy link to min_version") Minimum TLS version supported. When specifiying very old and insecure versions under TLS12 you must provide the `ciphers_list`. Possible values are: `"SSL3.0"` , `"TLS10"` , `"TLS11"` , `"TLS12"` , `"TLS13"` Defaults to `"TLS13"` Schema: [https://www.krakend.io/schema/v2.13/tls.json](https://www.krakend.io/schema/v2.13/tls.json "JSON schema definition") ### Upgrade from legacy key declaration The version v2.7 of KrakenD introduces declaring multiple TLS keys in the configuration. **Prior to KrakenD v2.7** the public and private keys were unique, and you had to declare them directly under `tls`, like this: ```json { "version": 3, "tls": { "public_key": "/path/to/cert.pem", "private_key": "/path/to/key.pem" } } ``` When using **KrakenD v2.7 and above**, make sure to place key pairs inside the `keys` array: ```json { "version": 3, "tls": { "keys": [ { "public_key": "/path/to/cert.pem", "private_key": "/path/to/key.pem" } ] } } ``` ## Client TLS settings You can also set global TLS settings when KrakenD acts as a client, meaning that the gateway takes the role of the requesting user and fetches data with the upstream services. All TLS options for the client go inside the `client_tls` object, and are similar to the server ones. When you set a `client_tls` in the configuration **the settings apply to all HTTP backends of all endpoints**. For instance: ```json { "version": 3, "client_tls": { "@comment": "Skip SSL verification when connecting to backends", "allow_insecure_connections": true } } ``` If you need to **define TLS options for an individual backend** see [HTTP Client options](/docs/enterprise/backends/http-client/) Enterprise These are the `client_tls` options: ##### Fields of TLS client settings \* required fields ##### [`allow_insecure_connections`](https://www.krakend.io/docs/service-settings/tls/#allow_insecure_connections "Click to copy link to allow_insecure_connections") boolean By default, KrakenD verifies every SSL connection. This option allows you to connect to backends considered **insecure**, for instance when you are using self-signed certificates Defaults to `false` ##### [`ca_certs`](https://www.krakend.io/docs/service-settings/tls/#ca_certs "Click to copy link to ca_certs") array An array with all the CA certificates you would like to validate the server you are connecting to. Example: `["ca.pem"]` Defaults to `[]` ##### [`cipher_suites`](https://www.krakend.io/docs/service-settings/tls/#cipher_suites "Click to copy link to cipher_suites") array The list of cipher suites as defined in the documentation. Defaults to `[4865,4866,4867]` ##### [`client_certs`](https://www.krakend.io/docs/service-settings/tls/#client_certs "Click to copy link to client_certs") array of objects The list of all client certificates available when fetching data from the upstream service. Each item of client\_certs accepts the following properties: ##### [`certificate`](#client_certs-certificate "Click to copy link to certificate") * string The path to the certificate you will use for mTLS connections. ##### [`private_key`](#client_certs-private_key "Click to copy link to private_key") * string The path to the private key you will use for mTLS connections. ##### [`curve_preferences`](https://www.krakend.io/docs/service-settings/tls/#curve_preferences "Click to copy link to curve_preferences") array The list of all the identifiers for the curve preferences. Use `23` for CurveP256, `24` for CurveP384 or `25` for CurveP521. Defaults to `[23,24,25]` ##### [`disable_system_ca_pool`](https://www.krakend.io/docs/service-settings/tls/#disable_system_ca_pool "Click to copy link to disable_system_ca_pool") boolean Ignore any certificate in the system’s CA. The only certificates loaded will be the ones in the `ca_certs` list when true. Defaults to `false` ##### [`max_version`](https://www.krakend.io/docs/service-settings/tls/#max_version "Click to copy link to max_version") Maximum TLS version supported. Possible values are: `"SSL3.0"` , `"TLS10"` , `"TLS11"` , `"TLS12"` , `"TLS13"` Defaults to `"TLS13"` ##### [`min_version`](https://www.krakend.io/docs/service-settings/tls/#min_version "Click to copy link to min_version") Minimum TLS version supported. When specifiying very old and insecure versions under TLS12 you must provide the `ciphers_list`. Possible values are: `"SSL3.0"` , `"TLS10"` , `"TLS11"` , `"TLS12"` , `"TLS13"` Defaults to `"TLS13"` Schema: [https://www.krakend.io/schema/v2.13/client\_tls.json](https://www.krakend.io/schema/v2.13/client_tls.json "JSON schema definition") ## Supporting older TLS 1.2 and below Although we do not recommend downgrading your installation, this is the configuration you will need to support **older protocol versions**. ### Support old TLS v1.2 To support TLS v1.2 and 1.3 simultaneously, you need the following configuration: ```json { "version": 3, "tls": { "keys": [ { "public_key": "/path/to/cert.pem", "private_key": "/path/to/key.pem" } ], "min_version": "TLS12" } } ``` ### Support archaic TLS versions To support versions older than 1.2, specify the list of `cipher_suites` you want to enable (see below). ## Supported cipher suites You can select which `cipher_suites` you’d like to support by passing an array of integers. The possible values of the cipher suites are listed below. The recommendation is to stay with TLS 1.3, or downgrade to TLS 1.2 when strongly needed. Below that should be out of the table. **Default suites for TLS 1.3** are: - `4865`: TLS\_AES\_128\_GCM\_SHA256 - `4866`: TLS\_AES\_256\_GCM\_SHA384 - `4867`: TLS\_CHACHA20\_POLY1305\_SHA256 **Default suites for TLS 1.2** are: - `49199`: TLS\_ECDHE\_RSA\_WITH\_AES\_128\_GCM\_SHA256 - `49200`: TLS\_ECDHE\_RSA\_WITH\_AES\_256\_GCM\_SHA384 - `52392`: TLS\_ECDHE\_RSA\_WITH\_CHACHA20\_POLY1305 - `49196`: TLS\_ECDHE\_ECDSA\_WITH\_AES\_256\_GCM\_SHA384 - `52393`: TLS\_ECDHE\_ECDSA\_WITH\_CHACHA20\_POLY1305 - `49195`: TLS\_ECDHE\_ECDSA\_WITH\_AES\_128\_GCM\_SHA256 Other `cipher_suites` are supported but **not recommended**. Their existence is to support legacy systems: - `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 - `60`: TLS\_RSA\_WITH\_AES\_128\_CBC\_SHA256 - `156`: TLS\_RSA\_WITH\_AES\_128\_GCM\_SHA256 - `157`: TLS\_RSA\_WITH\_AES\_256\_GCM\_SHA384 - `49159`: TLS\_ECDHE\_ECDSA\_WITH\_RC4\_128\_SHA - `49161`: TLS\_ECDHE\_ECDSA\_WITH\_AES\_128\_CBC\_SHA - `49162`: TLS\_ECDHE\_ECDSA\_WITH\_AES\_256\_CBC\_SHA - `49169`: TLS\_ECDHE\_RSA\_WITH\_RC4\_128\_SHA - `49170`: TLS\_ECDHE\_RSA\_WITH\_3DES\_EDE\_CBC\_SHA - `49171`: TLS\_ECDHE\_RSA\_WITH\_AES\_128\_CBC\_SHA - `49172`: TLS\_ECDHE\_RSA\_WITH\_AES\_256\_CBC\_SHA - `49187`: TLS\_ECDHE\_ECDSA\_WITH\_AES\_128\_CBC\_SHA256 - `49191`: TLS\_ECDHE\_RSA\_WITH\_AES\_128\_CBC\_SHA256 ## Generating certificates You can acquire, use external tools, or self-generate your certificates. For example, to generate a self-signed certificate from the command line, you can do the following: Generate a certificate  ``` $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" ``` ## Common TLS errors Self-signed certificates will show in the logs: ```log http: TLS handshake error from 172.17.0.1:45474: local error: tls: bad record MAC ``` Old clients trying to connect to a newer TLS version, will produce errors as follows: ```log http: TLS handshake error from 172.17.0.1:33698: tls: unsupported SSLv2 handshake received http: TLS handshake error from 172.17.0.1:33710: tls: client offered only unsupported versions ``` Incompatible curves offered by client: ``` http: TLS handshake error from 172.17.0.1:33810: tls: no cipher suite supported by both client and server http: TLS handshake error from 172.17.0.1:33814: tls: no ECDHE curve supported by both client and server ``` When the client offers a limited set of options that the server cannot accept. It will hang up the connection with an End Of File: ``` http: TLS handshake error from 172.17.0.1:33990: EOF ``` ### SSL scan results for the default settings The following output is the results of the [sslscan](https://github.com/rbsec/sslscan) command with a KrakenD configuration specifying the private and public keys in the configuration with no other additional `tls` settings: ``` Version: 2.0.15-7-gbc46606-static OpenSSL 1.1.1u-dev xx XXX xxxx Connected to ::1 Testing SSL server localhost on port 443 using SNI name localhost SSL/TLS Protocols: SSLv2 disabled SSLv3 disabled TLSv1.0 disabled TLSv1.1 disabled TLSv1.2 disabled TLSv1.3 enabled TLS Fallback SCSV: Server supports TLS Fallback SCSV TLS renegotiation: Session renegotiation not supported TLS Compression: Compression disabled Heartbleed: TLSv1.3 not vulnerable to heartbleed Supported Server Cipher(s): Preferred TLSv1.3 128 bits TLS_AES_128_GCM_SHA256 Curve P-521 DHE 521 Accepted TLSv1.3 256 bits TLS_AES_256_GCM_SHA384 Curve P-521 DHE 521 Accepted TLSv1.3 256 bits TLS_CHACHA20_POLY1305_SHA256 Curve P-521 DHE 521 Server Key Exchange Group(s): TLSv1.3 128 bits secp256r1 (NIST P-256) TLSv1.3 192 bits secp384r1 (NIST P-384) TLSv1.3 260 bits secp521r1 (NIST P-521) SSL Certificate: Signature Algorithm: sha256WithRSAEncryption RSA Key Strength: 2048 Subject: localhost Issuer: localhost Not valid before: Feb 28 11:44:05 2023 GMT Not valid after: Feb 28 11:44:05 2024 GMT ``` --- # [HTTP Security Considerations](https://www.krakend.io/docs/service-settings/security/) KrakenD has implemented several security strategies, controlled via the `security/http` component. 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: ```json { "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. ##### Fields of HTTP Security \* required fields ##### [`allowed_hosts`](https://www.krakend.io/docs/service-settings/security/#allowed_hosts "Click to copy link to allowed_hosts") array of strings When a request hits KrakenD, it will confirm if the value of the Host HTTP header is in the list. If so, it will further process the request. If the host is not in the allowed hosts list, KrakenD will simply reject the request. Defaults to `[]` ##### [`allowed_hosts_are_regex`](https://www.krakend.io/docs/service-settings/security/#allowed_hosts_are_regex "Click to copy link to allowed_hosts_are_regex") boolean Treat the allowed hosts list as regular expressions. Defaults to `false` ##### [`browser_xss_filter`](https://www.krakend.io/docs/service-settings/security/#browser_xss_filter "Click to copy link to browser_xss_filter") boolean Defaults to `false` ##### [`content_security_policy`](https://www.krakend.io/docs/service-settings/security/#content_security_policy "Click to copy link to content_security_policy") string The HTTP Content-Security-Policy (CSP) default-src directive serves as a fallback for the other CSP fetch directives. Example: `"default-src 'self';"` Defaults to `""` ##### [`content_type_nosniff`](https://www.krakend.io/docs/service-settings/security/#content_type_nosniff "Click to copy link to content_type_nosniff") boolean Enabling this feature will prevent the user’s browser from interpreting files as something else than declared by the content type in the HTTP headers. Defaults to `false` ##### [`custom_frame_options_value`](https://www.krakend.io/docs/service-settings/security/#custom_frame_options_value "Click to copy link to custom_frame_options_value") string You can add an X-Frame-Options header using custom\_frame\_options\_value with the value of DENY (default behavior) or even set your custom value. Example: `"ALLOW-FROM https://example.com"` Defaults to `""` ##### [`force_sts_header`](https://www.krakend.io/docs/service-settings/security/#force_sts_header "Click to copy link to force_sts_header") boolean Force a STS Header even if using plain HTTP. Defaults to `false` ##### [`frame_deny`](https://www.krakend.io/docs/service-settings/security/#frame_deny "Click to copy link to frame_deny") boolean Set to true to enable clickjacking protection, together with `custom_frame_options_value`. Defaults to `false` ##### [`host_proxy_headers`](https://www.krakend.io/docs/service-settings/security/#host_proxy_headers "Click to copy link to host_proxy_headers") array of strings A set of header keys that may hold a proxied hostname value for the request. Example: `["X-Forwarded-Hosts"]` ##### [`hpkp_public_key`](https://www.krakend.io/docs/service-settings/security/#hpkp_public_key "Click to copy link to hpkp_public_key") string HTTP Public Key Pinning (HPKP) is a security mechanism which allows HTTPS websites to resist impersonation by attackers using mis-issued or otherwise fraudulent certificates. (For example, sometimes attackers can compromise certificate authorities, and then can mis-issue certificates for a web origin.). Example: `"pin-sha256=\"base64==\"; max-age=expireTime [; includeSubDomains][; report-uri=\"reportURI\"]"` Defaults to `""` ##### [`is_development`](https://www.krakend.io/docs/service-settings/security/#is_development "Click to copy link to is_development") boolean 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. Defaults to `false` ##### [`referrer_policy`](https://www.krakend.io/docs/service-settings/security/#referrer_policy "Click to copy link to referrer_policy") string Allows the Referrer-Policy header with the value to be set with a custom value. Defaults to `"same-origin"` ##### [`ssl_host`](https://www.krakend.io/docs/service-settings/security/#ssl_host "Click to copy link to ssl_host") string When the SSL redirect is true, the host where the request is redirected to. Example: `"ssl.host.domain"` Defaults to `"ssl.host.domain"` ##### [`ssl_proxy_headers`](https://www.krakend.io/docs/service-settings/security/#ssl_proxy_headers "Click to copy link to ssl_proxy_headers") object Header keys with associated values that would indicate a valid https request. Useful when using Nginx, e.g: `"X-Forwarded-Proto": "https"` Example: `{"X-Forwarded-Proto":"https"}` ##### [`ssl_redirect`](https://www.krakend.io/docs/service-settings/security/#ssl_redirect "Click to copy link to ssl_redirect") boolean Redirect any request that is not using HTTPS Defaults to `true` ##### [`sts_include_subdomains`](https://www.krakend.io/docs/service-settings/security/#sts_include_subdomains "Click to copy link to sts_include_subdomains") boolean Set to true when you want the `includeSubdomains` be appended to the Strict-Transport-Security header. Defaults to `false` ##### [`sts_seconds`](https://www.krakend.io/docs/service-settings/security/#sts_seconds "Click to copy link to sts_seconds") integer Enable this policy by setting the `max-age` of the `Strict-Transport-Security` header. Setting to `0` disables HSTS. Defaults to `0` Schema: [https://www.krakend.io/schema/v2.13/security/http.json](https://www.krakend.io/schema/v2.13/security/http.json "JSON schema definition") ### Restrict connections by host Use `allowed_hosts` Define a list of hosts that KrakenD should accept requests to. When a request hits KrakenD, it will confirm if the value of the `Host` HTTP header is in the list. If so, it will further process the request. If the host is not in the allowed hosts list, KrakenD will simply reject the request. The list must contain the fully qualified domain names that are allowed, along with the origin port. When the list is empty accepts any host. ### Clickjacking protection KrakenD follow the OWASP’s recommendations by adding a frame-breaking strategy. Use `frame_deny` together with `custom_frame_options_value` You can add an `X-Frame-Options` header using `custom_frame_options_value` with the value of `DENY` (default behavior) or even set your custom value. Check the [OWASP Clickjacking cheat sheet](https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet#X-Frame-Options_Header_Types) for more details about the header and its recommended values. ### MIME-Sniffing prevention Use `content_type_nosniff` Enabling this feature will prevent the user’s browser from interpreting files as something else than declared by the content type in the HTTP headers. ### Cross-site scripting (XSS) protection Use `browser_xss_filter` This feature enables the [Cross-site scripting (XSS)](https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29) filter in the user’s browser. ### Content-Security-Policy Related to XSS protection there is the HTTP Content-Security-Policy response header, which allows you to control resources the user agent is allowed to load for a given page. Use `content_security_policy` (*string*) to set your policy. E.g.: `default-src 'self';` ## HTTPS ### HTTP Strict Transport Security (HSTS) OWASP defines the HSTS as > HTTP Strict Transport Security (HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections, and never via the insecure HTTP protocol. HSTS is an IETF standards track protocol and is specified in RFC 6797. A server implements an HSTS policy by supplying a header (Strict-Transport-Security) over an HTTPS connection (HSTS headers over HTTP are ignored). - Use `sts_seconds` (*integer*): Enable this policy by setting the max-age of the Strict-Transport-Security header. Setting to `0` disables HSTS. Use the `sts_seconds` setting. - Use `sts_include_subdomains` (*bool*): Set to true when you want the `includeSubdomains` be appended to the Strict-Transport-Security header. ### HTTP Public Key Pinning (HPKP) Use `hpkp_public_key` OWASP defines the HPKP as > HTTP Public Key Pinning (HPKP) is a security mechanism which allows HTTPS websites to resist impersonation by attackers using mis-issued or otherwise fraudulent certificates. (For example, sometimes attackers can compromise certificate authorities, and then can mis-issue certificates for a web origin.). **This feature must be used with caution because there is a risk that hosts may make themselves unavailable by pinning to a set of public key hashes that becomes invalid.** ## OAuth2 KrakenD supports the client credentials grant. Use this feature if you need to authorize the KrakenD to access your backend services. See the specific docs for [OAuth2 Client Credentials](/docs/authorization/client-credentials/) --- # [Run using the FIPS-140 Cryptographic Module](https://www.krakend.io/docs/enterprise/security/fips-140/) While KrakenD’s stateless design **does not handle customer data storage**, it still offers a binary that facilitates **FIPS 140-3 compliance** to **protect customers’ data in-transit**. If you are unfamiliar with FIPS, the [US National Institute of Standards and Technology (NIST)](https://www.nist.gov/), in collaboration with the Canadian Centre for Cyber Security, released the \[Cryptographic Module Validation Program]\[https://csrc.nist.gov/projects/cryptographic-module-validation-program] (CMVP), which validates cryptographic modules to ensure they meet security standards for Federal agencies. Still, it gained interest from other sectors and industries that started to follow the same standards. The primary benefit of FIPS is its help towards **regulatory compliance**, as the cryptography used by KrakenD meets the requirements of industries regulated by government standards. Might not satisfy regulatory requirements Simply using a FIPS 140-3 compliant and validated cryptographic module may not, on its own, satisfy all relevant regulatory requirements. The KrakenD team does not provide any guarantees or support around how usage of the provided FIPS 140-3 mode may, or may not, satisfy specific regulatory requirements. You should determine whether the use of this module meets your particular requirements. In any case, you still must integrate a FIPS-validated cryptographic module yourself to secure your data at rest and properly use TLS end-to-end. The most common industries mandated to follow strict cryptographic standards due to the sensitivity of the information they handle are: - **Government and defense**: Entities that deal with national security data. - **Healthcare**: Organizations handling protected health information (PHI) that must comply with [HIPAA requirements](https://www.hhs.gov/hipaa/for-professionals/index.html). - **Financial services**: Financial institutions that must secure customer data and comply with regulations like the Sarbanes-Oxley Act (**SOX**) or Gramm–Leach–Bliley Act (**GLBA**). - **Technology and cloud**: Providers that need to ensure data protection for their clients across different jurisdictions. ## Enabling FIPS 140-3 To make the gateway’s Cryptographic Module operate in FIPS 140-3 mode, you need to **add an environment variable** to the container or server with the following value: ``` GODEBUG=fips140=only ``` The gateway must be started when setting this variable. Setting the variable while the process is running won’t take any effect. The possible values of the fips140 setting are: - `GODEBUG=fips140=off`: no special support for FIPS 140-3 mode. This is the same as removing the environment variable. - `GODEBUG=fips140=on`: Force the Cryptographic Module to operate in FIPS 140-3 mode. - `GODEBUG=fips140=only`: Same as above, but cryptographic algorithms not approved by FIPS 140-3 return an error or panic. **Note**: Older versions of KrakenD required you to use a dedicated Docker container, but this is no longer a requirement. --- # [Security Policies Engine (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/security-policies/) The policies engine lets you write custom rules validated on runtime during **requests, responses, and token validation**. The policies allow you to implement all sorts of validations and user access control, from parameter compliance, to **RBAC** (Role-Based Access Control) and **ABAC** (Attribute-Based Access Control) strategies. ## What is a policy A policy is a test rule based on the [CEL language](/docs/enterprise/security-policies/policy-language/) and [advanced macros](/docs/enterprise/security-policies/advanced-policy-macros/) that evaluates the gateway’s input or output and determines if it can continue executing or if it must break the flow with an error. For example, a simple policy could look like this: ```js has(JWT.department) && JWT.department in ["marketing", "sales"] ``` The policy above checks if the JWT token contains a claim named `department` and its value is one of the listed ones. If it doesn’t there is a configurable error. The [JWT validator](/docs/authorization/jwt-validation/) would have previously validated that the token is valid, signed, and so on. ### What can you do with policies? Policies extend the rest of existing features (security related or not) like token validation, CORS, HTTPS headers, authentication, parameter validation, etc., with **your set of rules**. The rules are **business logic** that you will apply to your API. These rules might have access to query strings, URLs, parameters, cookies, tokens, time functions, geolocation, cryptography, and a long etcetera, and you can combine all this data. For instance, real-world examples could be: - Show a document if the user works in the legal department and has a senior role. - The user request contains a mandatory query string and params with a particular format (regexp) - The endpoint `/blackfriday` will become available and unavailable automatically based on dates and without any intervention. - The GDPR cookie isn’t set, so the tracking endpoint must be disabled. - The partner requesting this content is using a specific tenant and is in a specific billing plan to display the content ## Why are policies based on CEL? Because CEL evaluates an expression from the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) in nanoseconds to microseconds, the ideal use case for CEL is applications with performance-critical paths. For example, executing a security policy with each HTTP request to a service is a perfect use case for CEL because the security policy does not change constantly, and CEL will have a **negligible impact on the response time**. The rules are pre-compiled to the AST during startup time, so when the rule is evaluated, there is only the execution part. The interpretation is never needed as it was performed when the service started. CEL is maintained by Google and used in hundreds of mission-critical projects like Google Cloud, Openshift, Digital Ocean, or Kubernetes. Even though there are other popular policy engines, we chose CEL because of its simplicity, extensibility, and, most importantly, security and performance. ### But why not JavaScript, Lua, or WASM? JavaScript and Lua are rich languages that require **sandboxing** to execute safely. Unfortunately, sandboxing is **costly** and heavily factors into the “what will I let users evaluate?” question when the answer is anything more than *O(n)* complexity. CEL **evaluates linearly** concerning the expression size and the input evaluated when macros are disabled. The host environment provides the only functions beyond the built-ins you can invoke. Then, why not WASM? WASM is an excellent choice for specific applications. It is far superior to embedded JavaScript and Lua, but **it does not support garbage collection**, and non-primitive object types require **semi-expensive calls** across modules. In most cases, CEL will be faster and more portable for its intended use case. ## Policy placements You can set policies between the end-user and KrakenD via **endpoint policies**, or you can set policies between KrakenD and the services you want to consume via **backend policies**. Policies can work independently or together in **two directions**, both in the request and response. You decide in which direction you want to apply the policies by setting them inside the proper context: - `jwt`: During the token validation (`endpoint` only) in `auth/validator`. If you don’t have any JWT validation configuration, policies remain disabled. This context evaluates in the first place. - `req`: During the request - `resp`: Before delivering the final response ## Configuration The following example shows the three different places to apply policies with a few checks at various stages during endpoint and backend processing. The code is `@commented` to understand each evaluation block: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "debug_endpoint": true, "extra_config": { "router": { "return_error_msg": true } }, "endpoints": [ { "endpoint": "/legal/document/{docId}", "input_query_strings": [ "q" ], "extra_config": { "security/policies": { "auto_join_policies": true, "debug": false, "req": { "@comment": "Validates that the URL parameter docId begins with 'EU' and there is a ?q= parameter", "policies": [ "req_params.DocId.startsWith('EU')", "hasQuerystring('q')" ], "error": { "@comment": "If any rule fails, we will return this JSON body and content-type. Plus an HTTP status code 400", "body": "{\"message\":\"wrong request, try again\"}", "status": 400, "content_type": "application/json" } }, "resp": { "@comment": "Check that when we get the response, contains all involved backend calls", "policies": [ "resp_completed" ], "error": { "body": "The service didn't return all the requested information", "status": 500, "content_type": "text/plain" } }, "jwt": { "@comment": "The token was already validated, but user is from legal?", "policies": [ "has(JWT.user_id)", "'legal' == JWT.department" ] } }, "auth/validator": { "@comment": "Add here your auth/validator settings when using 'jwt' policies" } }, "backend": [ { "url_pattern": "/cms/contracts/docid/{docId}", "extra_config": { "security/policies": { "auto_join_policies": true, "debug": false, "req": { "@comment": "DocId is like EU123456Z. We could have done this earlier too.", "policies": [ "req_params.DocId.isAlphanumeric()" ], "error": { "body": "The document requested does not exist", "status": 400, "content_type": "text/plain" } }, "resp": { "policies": [ "!isEmpty(resp_data.message)" ], "error": { "body": "We received an empty response from the service", "status": 500 } } } } } ] } ] } ``` Notice the presence of `return_error_msg` when we want to return a custom body to the end-user. These are the options available in the component: ##### Fields of Securiy Policies \* required fields Minimum configuration needs **any of**: `req` , or `resp` , or `jwt` ##### [`auto_join_policies`](https://www.krakend.io/docs/enterprise/security-policies/#auto_join_policies "Click to copy link to auto_join_policies") boolean When true, all policies of the same type concatenate with an AND operation to evaluate a single expression. Performs faster, but its harder the debug. Defaults to `false` ##### [`debug`](https://www.krakend.io/docs/enterprise/security-policies/#debug "Click to copy link to debug") boolean When true, all the inputs and evaluation results are printed in the console. Defaults to `false` ##### [`disable_macros`](https://www.krakend.io/docs/enterprise/security-policies/#disable_macros "Click to copy link to disable_macros") boolean Advanced macros can be disabled in those policies not needing them for a faster evaluation. Defaults to `false` ##### [`jwt`](https://www.krakend.io/docs/enterprise/security-policies/#jwt "Click to copy link to jwt") object All the policies applied in the JWT context (token validation). **You must configure `auth/validator`** for the policies to run, otherwise they will be skipped. Any policy failing will generate a `401 Unauthorized` error. Works in the `endpoint` context only, and is not available under `backend`. ##### [`policies`](#jwt-policies "Click to copy link to policies") * array of strings An array with all the policies to evaluate. Each policy is represented as a string ##### [`req`](https://www.krakend.io/docs/enterprise/security-policies/#req "Click to copy link to req") object All the policies applied in the request context. ##### [`error`](#req-error "Click to copy link to error") object ##### [`body`](#error-body "Click to copy link to body") string Leave an empty string to use the validation error, or write a string with the error response body. This error is NOT returned in the response, but in the application logs, unless you enable `return_detailed_errors` in the `router` section. You can add escaped JSON, XML, etc in the string and add a Content-Type. Defaults to `""` ##### [`content_type`](#error-content_type "Click to copy link to content_type") string The Content-Type header you’d like to send with the error response. When unset, uses `text/plain` by default. Defaults to `"text/plain"` ##### [`status`](#error-status "Click to copy link to status") integer The HTTP status code you want to return when the validation fails. Defaults to `500` ##### [`policies`](#req-policies "Click to copy link to policies") * array of strings An array with all the policies to evaluate. Each policy is represented as a string ##### [`resp`](https://www.krakend.io/docs/enterprise/security-policies/#resp "Click to copy link to resp") object All the policies applied in the response context. ##### [`error`](#resp-error "Click to copy link to error") object ##### [`body`](#error-body "Click to copy link to body") string Leave an empty string to use the validation error, or write a string with the error response body. This error is NOT returned in the response, but in the application logs, unless you enable `return_detailed_errors` in the `router` section. You can add escaped JSON, XML, etc in the string and add a Content-Type. Defaults to `""` ##### [`content_type`](#error-content_type "Click to copy link to content_type") string The Content-Type header you’d like to send with the error response. When unset, uses `text/plain` by default. Defaults to `"text/plain"` ##### [`status`](#error-status "Click to copy link to status") integer The HTTP status code you want to return when the validation fails. Defaults to `500` ##### [`policies`](#resp-policies "Click to copy link to policies") * array of strings An array with all the policies to evaluate. Each policy is represented as a string Schema: [https://www.krakend.io/schema/v2.13/security/policies.json](https://www.krakend.io/schema/v2.13/security/policies.json "JSON schema definition") --- # [Security Policies language and syntax (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/security-policies/policy-language/) The policies language and syntax look nearly identical to **C++, Go, Java, and Typescript** and is based on Google’s CEL [built-in functions](/docs/enterprise/security-policies/policy-language/) and [advanced macros](/docs/enterprise/security-policies/advanced-policy-macros/). If you have existing validation using the [CEL Component](/docs/endpoints/common-expression-language-cel/), you can also port them as policies. The policy language is used in CEL Validation, Security Policies, and in the Tiered Rate Limit. ## Basic syntax ### Data types The supported data types are: - `uint` (unsigned integer) - `int` (integer) - `double` (Number, float) - `string` (string) - `bool` (boolean) - `null_type` - `bytes` (on JSON mapping a string of base64-encoded bytes) - `map` (Object. e.g., `{}`) - `list` (Array. e.g., `[]`) - `duration` (e.g., `1h1m` for 1 hour and 1 minute) - `timestamp` (e.g., `timestamp(now)` returns in RFC3339 format including timezone `2023-01-01T10:00:20.021-05:00`). - `dyn` the union of all other types. In addition, you can **cast** some types using their type name as a function, for instance: `int("23")` or `double("21.3")`. ### Logic and arithmetic operators The logic operators are: - `!` (not) - `==` (equal) - `!=` (unequal) - `&&` (and) - `||` (or) - `_?_:_` (ternary) - `<`,`<=`, `>=`, `>` (ordering) The arithmetic operators are: - `%` (mod) - `*` (multiplication) - `+` (sum and concatenation) - `-` (subtract) - `/` (divide) ### Accessing maps A `map` (also could be named object) is traversed with the dot operator `.`, or using the syntax `['field']`. For instance, the map `{"foo": {"bar": 1}}` can be accessed as `foo.bar` and `foo['bar]` equivalently. The second method is necessary when keys use characters like `-` that can be interpreted as operators. E.g., `req_headers['X-Forwarded-For']`. If you want to test if a specific field exists in a map use `has('field')`. ## Functions and macros For the complete list of supported functions and macros see: - [Built-in functions](/docs/enterprise/security-policies/built-in-functions/) - [Advanced macros](/docs/enterprise/security-policies/advanced-policy-macros/) ## Data variables The following data variables are available inside CEL and security policies. They are only available if you are in a request, response, or validating a token accordingly: - 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** (available on `endpoint` context only if the namespace `auth/validator` is correctly configured). When instead of working with security policies, you work with a [Tiered Rate Limit policy](/docs/enterprise/service-settings/tiered-rate-limit/) using the `qos/ratelimit/tiered` namespace, **the only variable you can access** is: - `value`, which contains the string value that matched the ratelimit tier. For example: A policy `value.matches('[a-zA-Z]+')` will make sure that the rate limit tier is a word of one or more letters using a regular expression. ### Variables for requests You can use the following variables inside policies: - `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. First letter capitalized All **`req_params` capitalize the first letter**. E.g., an `"endpoint": "/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](/docs/endpoints/sequential-proxy/#chaining-the-requests) 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`: A map with all the headers received in its **canonical form** (converts the first letter and any letter following a hyphen to upper case; the rest are converted to lowercase. For example, the canonical key for `accept-encoding` is `Accept-Encoding`). The value of the map **is an array**, as you can have a header declared multiple times (e.g., multiple cookies with `Set-Cookie`). For example, you can access headers like this: `req_headers['X-Forwarded-For']`. Do not forget `input_headers`! The `req_headers` variable won’t contain any headers that are not declared in the `input_headers` of the endpoint. - `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 query strings, 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`). Most of the times you might want `req_querystring.foo[0]`. Do not forget `input_query_strings`! The `req_querystring` variable won’t contain any query strings that are not declared in the `input_query_strings` of the endpoint. - `now`: An object containing the current timestamp, e.g., `timestamp(now).getDayOfWeek()` ### Variables for responses You can use the following variables inside the policies: - `resp_completed`: Boolean whether all the data has been successfully retrieved. When you use multiple backends, this information makes sense in the `endpoint` context. - `resp_metadata_status`: Returns an integer with the status code. Only available when using `no-op` encoding. - `resp_metadata_headers`: Returns a map with all the headers of the response. Only available when using `no-op` encoding. - `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`. Not available when using `no-op`. - `now`: An object containing the current timestamp A note on response metadata The response metadata is only filled for no-op pipes. In non-no-op cases, it will always be empty, and the pipe will end the execution if the status code is not 200/201. ### Variables for the JWT rejecter You can also use CEL expressions during the JWT token validation. Use the `JWT` variable to access its metadata in an `endpoint` context. The namespace `auth/validator` must be correctly configured. For instance: ```js has(JWT.user_id) && has(JWT.enabled_days) && (timestamp(now).getDayOfWeek() in JWT.enabled_days) ``` This example checks that the JWT token contains the metadata `user_id` and `enabled_days` with the macro `has()`, and then checks that today’s weekday is within one of the allowed days to see the endpoint. --- # [Advanced Macros for Security Policies (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/security-policies/advanced-policy-macros/) The advanced macros are powerful functions that allow you to declare Security Policies with simple expressions, reducing code complexity and speeding up development. Advanced macros are not available on the [CEL component](/docs/enterprise/endpoints/common-expression-language-cel/), only on Security Policies. You have the following list of advanced macros always available unless you set in the configuration the `disable_advanced_macros` flag to `true`. Adding functionality The following list is not the complete catalogue of functions, but the ones added to the [built-in functions](/docs/enterprise/security-policies/built-in-functions/). ## Math ### `between` Checks if a specific value is between two numbers of the same numeric type, including the limiting numbers. E.g.: `between(2,5,7)` returns true because `2` is between the range. ``` .between(, ) -> .between(, ) -> between(, , ) -> between(, , ) -> ``` Examples ```js between(2,2,2) // returns true between(2,5,7) // returns false 2.between(1,3) // returns true 2.between(4,5) // returns false 2.0.between(1.9,2.1) // returns true ``` ## Strings ### `isEmpty` Returns true when a specific value does not exist, or it has an empty value (e.g., a string is `""`) ``` isEmpty() -> ``` Examples ```js isEmpty(object.a) // returns true when object is {"a":"", "b":2} isEmpty(object.a) // returns true when object is {"a":null, "b":2} isEmpty(object.c) // returns true when object is {"a":1, "b":2} isEmpty(object.a) // returns false when object is {"a":"hi", "b":2} ``` ### `isAlphanumeric` Checks if a specific value is an alphanumeric string. English letters and numbers only. No other characters are allowed. ``` isAlphanumeric() -> .isAlphanumeric() -> ``` Examples ```js isAlphanumeric("") // returns true isAlphanumeric("abcDFG123") // returns true isAlphanumeric("abcD-G123") // returns false "abcDFG123".isAlphanumeric() // returns true "abcD-G123".isAlphanumeric() // returns false ``` ### `isAlphabetic` Checks if a specific value is an alphabetic string. English letters only. No other characters are allowed. ``` isAlphabetic() -> .isAlphabetic() -> ``` Examples ```js isAlphabetic("") // returns true isAlphabetic("abcDFG") // returns true isAlphabetic("abcD-G") // returns false "abcDFG".isAlphabetic() // returns true "abcD-G".isAlphabetic() // returns false ``` ### `find` Returns the first match for the provided regular expression. ``` .find() -> ``` Examples ```js "abc 123".find('[0-9]*') // returns '123' "abc 123".find('xyz') // returns '' ``` ### `charAt` Returns the character at the given position. If the position is negative or greater than the length of the string, the function will produce an error ``` .charAt() -> ``` Examples ```js 'hello'.charAt(4) // returns 'o' 'hello'.charAt(5) // returns '' 'hello'.charAt(-1) // error ``` ### `indexOf` Returns the integer index of the first occurrence of the search string. If the search string is not found, the function returns -1. The function also accepts an optional position from which to begin the substring search. If the substring is the empty string, the index where the search starts is returned (zero or custom). ``` .indexOf() -> .indexOf(, ) -> ``` Examples ```js 'hello mellow'.indexOf('') // returns 0 'hello mellow'.indexOf('ello') // returns 1 'hello mellow'.indexOf('jello') // returns -1 'hello mellow'.indexOf('', 2) // returns 2 'hello mellow'.indexOf('ello', 2) // returns 7 'hello mellow'.indexOf('ello', 20) // error ``` ### `join` Returns a new string where the elements of string list are concatenated. The function also accepts an optional separator which is placed between elements in the resulting string. ``` >.join() -> >.join() -> ``` Examples ```js ['hello', 'mellow'].join() // returns 'hellomellow' ['hello', 'mellow'].join(' ') // returns 'hello mellow' [].join() // returns '' [].join('/') // returns '' ``` ### `lastIndexOf` Returns the integer index at the start of the last occurrence of the search string. If the the search string is not found, the function returns -1. The function also accepts an optional position which represents the last index to be considered as the beginning of the substring match. If the substring is the empty string, the index where the search starts is returned (string length or custom). ``` .lastIndexOf() -> .lastIndexOf(, ) -> ``` Examples ```js 'hello mellow'.lastIndexOf('') // returns 12 'hello mellow'.lastIndexOf('ello') // returns 7 'hello mellow'.lastIndexOf('jello') // returns -1 'hello mellow'.lastIndexOf('ello', 6) // returns 1 'hello mellow'.lastIndexOf('ello', -1) // error ``` ### `lowerAscii` Returns a new string where all ASCII characters are lower-cased. This function does not perform Unicode case-mapping for characters outside the ASCII range. ``` .lowerAscii() -> ``` Examples ```js 'TacoCat'.lowerAscii() // returns 'tacocat' 'TacoCÆt Xii'.lowerAscii() // returns 'tacocÆt xii' ``` ### `replace` Returns a new string based on the target, which replaces the occurrences of a search string with a replacement string if present. The function accepts an optional limit on the number of substring replacements to be made. When the replacement limit is 0, the result is the original string. When the limit is a negative number, the function behaves the same as replace all. ``` .replace(, ) -> .replace(, , ) -> ``` Examples ```js 'hello hello'.replace('he', 'we') // returns 'wello wello' 'hello hello'.replace('he', 'we', -1) // returns 'wello wello' 'hello hello'.replace('he', 'we', 1) // returns 'wello hello' 'hello hello'.replace('he', 'we', 0) // returns 'hello hello' ``` ### `split` Returns a list of strings split from the input by the given separator. The function accepts an optional argument specifying a limit on the number of substrings produced by the split. When the split limit is 0, the result is an empty list. When the limit is 1, the result is the target string to split. When the limit is a negative number, the function behaves the same as split all. ``` .split() -> > .split(, ) -> > ``` Examples ```js 'hello hello hello'.split(' ') // returns ['hello', 'hello', 'hello'] 'hello hello hello'.split(' ', 0) // returns [] 'hello hello hello'.split(' ', 1) // returns ['hello hello hello'] 'hello hello hello'.split(' ', 2) // returns ['hello', 'hello hello'] 'hello hello hello'.split(' ', -1) // returns ['hello', 'hello', 'hello'] ``` ### `substring` Returns the substring given a numeric range corresponding to character positions. Optionally may omit the trailing range for a substring from a given character position until the end of a string. Character offsets are 0-based with an inclusive start range and exclusive end range. It is an error to specify an end range that is lower than the start range, or for either the start or end index to be negative or exceed the string length. ``` .substring() -> .substring(, ) -> ``` Examples ```js 'tacocat'.substring(4) // returns 'cat' 'tacocat'.substring(0, 4) // returns 'taco' 'tacocat'.substring(-1) // error 'tacocat'.substring(2, 1) // error ``` ### `trim` Returns a new string that removes the leading and trailing whitespace in the target string. The trim function uses the Unicode definition of whitespace, which does not include the zero-width spaces. See: [](https://en.wikipedia.org/wiki/Whitespace_character#Unicode)[https://en.wikipedia.org/wiki/Whitespace\_character#Unicode](https://en.wikipedia.org/wiki/Whitespace_character#Unicode) ``` .trim() -> ``` Examples ```js ' \ttrim\n '.trim() // returns 'trim' ``` ### `upperAscii` Returns a new string where all ASCII characters are upper-cased. This function does not perform Unicode case-mapping for characters outside the ASCII range. ``` .upperAscii() -> ``` Examples ```js 'TacoCat'.upperAscii() // returns 'TACOCAT' 'TacoCÆt Xii'.upperAscii() // returns 'TACOCÆT XII' ``` ## Bitwise The bitwise operators work at the level of individual bits. All functions expect one or two number arguments that are internally compared bit to bit. **The maximum number you can pass is a 64-bit** (ignoring the highest bit). Notice that when comparing bits with claims or values found elsewhere, you might **need to cast values to integer** first. ### `bitAnd` Returns the result of a bitwise AND binary operation comparing each pair of the corresponding bits. When bits in the same position are both 1, the bit resulting in the binary representation is 1. ``` bitAnd(, ) -> ``` Examples ```js bitAnd(6, 1) // returns 0, because 110 AND 001 becomes 000 bitAnd(4, 6) // returns 4, because 100 AND 110 becomes 100 bitAnd(5, 1) // returns 1, because 101 AND 001 becomes 001 ``` ### `bitOr` Returns the result of a bitwise OR (inclusive OR) binary operation comparing each pair of the corresponding bits. The result in each position is 0 if both bits are 0, otherwise the result is 1. ``` bitOr(, ) -> ``` Examples ```js bitOr(6, 1) // returns 7, because 110 AND 001 becomes 111 bitOr(4, 6) // returns 6, because 100 AND 110 becomes 110 bitOr(5, 1) // returns 5, because 101 AND 001 becomes 101 ``` ### `bitXor` Returns the result of a bitwise XOR (exclusive OR) binary operation comparing each pair of the corresponding bits. The result in each position is 1 if only one of the bits is 1, otherwise is 0. ``` bitXor(, ) -> ``` Examples ```js bitXor(6, 1) // returns 5, because 110 AND 001 becomes 101 bitXor(4, 6) // returns 2, because 100 AND 110 becomes 010 bitXor(5, 1) // returns 4, because 101 AND 001 becomes 100 ``` ### `bitNot` Returns the result of a bitwise NOT (or bitwise complement) that performs the logical negation on each pair of the corresponding bits. Bits that are 1 become 0 and vice-versa. You need a 64-bit calculator to understand the following examples. ``` bitNot() -> ``` Examples ```js bitNot(2264546456654) // returns 9223349772308319153 bitNot(9223349772308319153) // returns 22264546456654 ``` ## Headers and query strings ### `has` Checks if a specific header or query string has a specific key containing a non-empty value. To be used with `req_headers` and `req_querystring`. All keys and values must be accessed as strings. Extends the functionality of the [built-in `has`](/docs/enterprise/security-policies/built-in-functions/#has) function. ``` has(>>, ) -> >>.has() -> ``` Examples ```js // With a request passing a header "Content-Type: application/json" req_headers.has("Content-Type") // returns true req_headers.has("X-Unknown") // returns false has(req_headers,"Content-Type") // returns true has(req_headers,"X-Unknown") // returns false // With a request /endpoint?a[]=1&a[]=2&b=3&c=5&d= req_querystring.has("a") // returns true req_querystring.has("d") // returns false has(req_querystring, "f") // returns false ``` ### `hasHeader` Checks if a specific header exists in the request. The endpoint must declare it in the `input_headers` list. ``` hasHeader() -> ``` Examples ```js hasHeader("") // returns false hasHeader("User-Agent") // returns true hasHeader("X-Invented-Header") // returns false ``` ### `hasCookie` Checks if a specific non-empty cookie exists (the endpoint must declare the header `Cookie` in the `input_headers` list)| ``` hasCookie() -> ``` Examples ```js // With these three cookie headers sent: ["foo=bar;foobar=1", "name=value;a=b;c=1", "empty= "] hasCookie("foo") // returns true hasCookie("c") // returns true hasCookie("empty") // returns false hasCookie("unknown") // returns false ``` ### `hasQuerystring` Checks if a specific non-empty query string exists (the endpoint must declare the query string in the `input_query_strings` list) ``` hasQuerystring() -> ``` Examples ```js // With a request /endpoint?a[]=1&a[]=2&b=3&c=5&d= hasQuerystring("a") // returns true hasQuerystring("c") // returns true hasQuerystring("d") // returns false hasQuerystring("unknown") // returns false ``` ### `getHeader` Returns the value of a specific header if exists, otherwise returns an error. The endpoint must include this header in the `input_headers` list. ``` getHeader() -> ``` Examples ```js // With a request passing a header "Content-Type: application/json" getHeader("Content-Type") // returns "application/json" getHeader("unknown") // returns an error ``` ### `getCookie` Checks if a specific non-empty cookie exists. The endpoint must declare the header `Cookie` in the `input_headers` list. ``` getCookie() -> ``` Examples ```js // With these three cookie headers sent: ["foo=bar;foobar=1", "name=value;a=b;c=1", "empty= "] getCookie("foo") // returns "bar" getCookie("name") // returns "value" getCookie("emtpy") // returns "" getCookie("unknown") // returns an error ``` ### `contains` Checks if a specific object contains in a key a specific value. It is designed to work with the types of `req_headers` and `req_querystring`. All keys and values must be accessed as strings. ``` contains(>>, , ) -> >>.contains(, ) -> ``` Examples ```js req_headers.contains("Content-Type", "application/json") // returns true when this header is set req_querystring.contains("foo", "bar") // returns true when ?foo=bar object.contains("b", "bar") // returns true when object = {"a":["foo"], "b":["bar"]} contains(object, "b", "bar") // returns true when object = {"a":["foo"], "b":["bar"]} object.contains("a", "vaz") // returns false when object = {"a":["foo"], "b":["bar"]} contains(object, "a", "vaz") // returns false when object = {"a":["foo"], "b":["bar"]} ``` ### `containsCookie` Checks if a specific cookie name contains a specific value. To be used with `req_headers`. All keys and values must be accessed as strings. ``` containsCookie(>>, , ) -> >>.containsCookie(, ) -> ``` Examples ```js // With these three cookie headers sent: ["foo=bar;foobar=1", "name=value;a=b;c=1", "empty= "] req_headers.containsCookie("foo", "bar") // returns true req_headers.containsCookie("name", "value") // returns true containsCookie(req_headers, "foo", "something") // returns false containsCookie(req_headers, "unknown", "something") // returns false containsCookie(req_headers, "empty", "") // returns true ``` ### `geoIP` Returns all the information provided by the [GeoIP plugin](/docs/enterprise/endpoints/geoip/). The contents are determined by the ability of MaxMind’s database to return the information. ``` geoIP() -> ``` Examples ```js // With the GeoIP plugin enabled geoIP().Country.IsoCode in ["US","UK","AU"] // returns true if user is from the US geoIP().RepresentedCountry.IsInEuropeanUnion // returns false if user is from the US ``` ## Lists and collections ### `collate` Providing one or more paths of a list, returns a new list with the elements matching the path. ``` >.collate() -> > >.collate(>) -> > >.collate() -> > >.collate(>) -> > ``` Examples ```js // Given v: // { // "a": [ // {"b": 1}, // {"b": 2}, // {"b": 3} // ], // "c": [ // {"d": -1, "c": 10}, // {"d": -2, "c": 20}, // {"d": -3, "c": 30} // ] // } v.collate("a") // returns [{"b": 1}, {"b": 2}, {"b": 3}] v.collate("a.b") // returns [1, 2, 3] v.collate(["a.b", "c.d"]) // returns [1, 2, 3, -1, -2, -3] v.collate(["a", "c.d"]) // returns [{"b": 1 }, {"b": 2 }, {"b": 3 }, -1, -2, -3 ] ``` ### `drop` Providing one or more paths of a list, returns a new list removing all matching paths. ``` >.drop() -> > >.drop(>) -> > >.drop() -> > >.drop(>) -> > ``` Examples ```js // Given v: // { // "a": [ // {"b": 1}, // {"b": 2}, // {"b": 3} // ], // "b": [ // {"b": -1, "c": 10}, // {"b": -2, "c": 20}, // {"b": -3, "c": 30} // ] // } v.drop("a") // returns {"b": [{"b": -1, "c": 10}, {"b": -2, "c": 20}, {"b": -3, "c": 30}]} v.drop("a.b") // returns {"a": [{}, {}, {}], "b": [{"b": -1, "c": 10}, {"b": -2, "c": 20}, {"b": -3, "c": 30}]} v.drop(["a.b", "b.b"]) // returns {"a": [{}, {}, {}], "b": [{"c": 10}, {"c": 20}, {"c": 30}]} v.drop(["a", "b.b"]) // returns {"b": [{"c": 10}, {"c": 20}, {"c": 30}]} ``` ### `dropEmpty` Providing an object, removes any element considered empty ``` >.dropEmpty() -> > >.dropEmpty() -> > ``` Examples ```js // Given v: // { // "a": [ // {}, // {}, // {} // ], // "b": [ // {"b": -1, "c": 10}, // {"b": -2, "c": 20}, // {"b": -3, "c": 30} // ] // } // v.dropEmpty() // returns {"b":[{"b":-1, "c":10}, {"b":-2, "c":20}, {"b":-3, "c":30}]} ``` ### `flatten` Flattens an array returning a list of values resulting from the depth-first traversal of all nested lists. ``` ...>.flatten() -> > ``` Examples ```js [[1],[2,3],[[[4]],[5,6]]].flatten() // returns [1, 2, 3, 4, 5, 6] [[{"a":1,"b":[10, 11]}],[2,3],[[[4]],[5,6]]].flatten() // returns [{"a":1, "b":[10, 11]}, 2, 3, 4, 5, 6] ``` ### `merge` Merges two objects in a single one, taking precedence the one passed as value. ``` >.merge(>) -> > ``` Examples ```js {"a":1, "b":2}.merge({"a":10, "c":3}) // returns {"a":10, "b":2, "c":3} resp_data.merge(JWT) // returns all the JWT data and response data together ``` ### `isSorted` Returns true if the provided list of comparable elements is sorted, else returns false. ``` >.isSorted() -> , T must be a comparable type ``` Examples ```js [1, 2, 3].isSorted() // returns true ['a', 'b', 'b', 'c'].isSorted() // returns true [2.0, 1.0].isSorted() // returns false [1].isSorted() // returns true [].isSorted() // returns true ``` ### `sum` Returns the sum of the elements of the provided list. Supports CEL number (int, uint, double) and duration types. ``` >.sum() -> , T must be a numeric type or a duration ``` Examples ```js [1, 3].sum() // returns 4 [1.0, 3.0].sum() // returns 4.0 ['1m', '1s'].sum() // returns '1m1s' emptyIntList.sum() // returns 0 emptyDoubleList.sum() // returns 0.0 [].sum() // returns 0 ``` ### `min` Returns the minimum valued element of the provided list. Supports all comparable types. If the list is empty, an error is returned. ``` >.min() -> , T must be a comparable type ``` Examples ```js [1, 3].min() // returns 1 [].min() // error [1].min() // returns 1 ([0] + emptyList).min() // returns 0 ["foo", "bar"].min() // returns "bar" ``` ### `max` Returns the maximum valued element of the provided list. Supports all comparable types. If the list is empty, an error is returned. ``` >.max() -> , T must be a comparable type ``` Examples ```js [3, 0.7, 1].max() // returns 3 [1.3, 1.4].max() // returns 1.4 ["foo", "bar"].max() // returns "foo" ``` ### `indexOf` Returns the first positional index of the provided element in the list. If the element is not found, -1 is returned. Supports all equatable types. ``` >.indexOf() -> , T must be an equatable type ``` Examples ```js [1, 2, 2, 3].indexOf(2) // returns 1 ['a', 'b', 'b', 'c'].indexOf('a') // returns 0 [1.0].indexOf(1.1) // returns -1 [].indexOf('string') // returns -1 ``` ### `lastIndexOf` Returns the last positional index of the provided element in the list. If the element is not found, -1 is returned. Supports all equatable types. ``` >.lastIndexOf() -> , T must be an equatable type ``` Examples ```js [1, 2, 2, 3].lastIndexOf(2) // returns 2 ['a', 'b', 'b', 'c'].lastIndexOf('b') // returns 2 [1.0].lastIndexOf(1.1) // returns -1 [].lastIndexOf('string') // returns -1 ``` ## URL ### `url` Converts a string to a URL or results in an error if the string is not a valid URL. The URL must be an absolute URI or an absolute path. ``` url() -> ``` Examples ```js url('https://user:pass@example.com:80/path?query=val#fragment') // returns a URL url('/absolute-path') // returns a URL url('https://a:b:c/') // error url('../relative-path') // error ``` ### `isURL` Returns true if a string is a valid URL. The URL must be an absolute URI or an absolute path. ``` isURL() -> ``` Examples ```js isURL('https://user:pass@example.com:80/path?query=val#fragment') // returns true isURL('/absolute-path') // returns true isURL('https://a:b:c/') // returns false isURL('../relative-path') // returns false ``` ### `getScheme` Returns the scheme of an URL or an empty string when absent. ``` .getScheme() -> ``` Examples ```js url('/path').getScheme() // returns '' url('https://example.com/').getScheme() // returns 'https' ``` ### `getHost` Returns the host of an URL or an empty string when absent. IPv6 addresses are returned with braces, e.g. `[::1]`. ``` .getHost() -> ``` Examples ```js url('https://example.com:80/').getHost() // returns 'example.com:80' url('https://example.com/').getHost() // returns 'example.com' url('https://[::1]:80/').getHost() // returns '[::1]:80' url('https://[::1]/').getHost() // returns '[::1]' url('/path').getHost() // returns '' ``` ### `getHostname` Returns the host name of an URL or an empty string when absent. IPv6 addresses are returned without braces, e.g. `::1` ``` .getHostname() -> ``` Examples ```js url('https://example.com:80/').getHostname() // returns 'example.com' url('https://127.0.0.1:80/').getHostname() // returns '127.0.0.1' url('https://[::1]:80/').getHostname() // returns '::1' url('/path').getHostname() // returns '' ``` ### `getPort` Returns the port of an URL or an empty string when absent. ``` .getPort() -> ``` Examples ```js url('https://example.com:80/').getPort() // returns '80' url('https://example.com/').getPort() // returns '' url('/path').getPort() // returns '' ``` ### `getEscapedPath` Returns the escaped URL or an empty string when absent.. E.g., `with space` becomes `with%20space`. ``` .getEscapedPath() -> ``` Examples ```js url('https://example.com/path').getEscapedPath() // returns '/path' url('https://example.com/path with spaces/').getEscapedPath() // returns '/path%20with%20spaces/' url('https://example.com').getEscapedPath() // returns '' ``` ### `getQuery` Returns the query parameters in “matrix” form where a repeated query key is interpreted to mean that there are multiple values for that key. The keys and values are returned unescaped. If absent in the URL, returns an empty map. ``` .getQuery() -> , > ``` Examples ```js url('https://example.com/path?k1=a&k2=b&k2=c').getQuery() // returns { 'k1': ['a'], 'k2': ['b', 'c']} url('https://example.com/path?key with spaces=value with spaces').getQuery() // returns { 'key with spaces': ['value with spaces']} url('https://example.com/path?').getQuery() // returns {} url('https://example.com/path').getQuery() // returns {} ``` ## Encoding ### `toJSON` Returns the JSON representation of an object. ``` toJSON() -> .toJSON() -> ``` Examples ```js {"foo":1, "bar":[1, 2, 3]}.toJSON() // returns "{\"foo\":1,\"bar\":[1,2,3]}" toJSON({"foo":1, "bar":[1, 2, 3]}) // returns "{\"foo\":1,\"bar\":[1,2,3]}" ``` ### `fromJSON` Returns an object from a JSON representation. ``` .fromJSON() -> .fromJSON() -> fromJSON() -> fromJSON() -> ``` Examples ```js "{\"foo\":1,\"bar\":[1,2,3]}".fromJSON() // returns {"foo":1, "bar":[1, 2, 3]} "{\"a\":1,\"bar\":[1,2,3]}".fromJSON() // returns {"a":1, "bar":[1, 2, 3]} ``` ### `fromJSONStream` Returns a list of objects from a JSON stream. ``` .fromJSONStream() -> > .fromJSONStream() -> > fromJSONStream() -> > fromJSONStream() -> > ``` Examples ```js '{"foo":1}{"bar":2}'.fromJSONStream() // returns [{"foo":1}, {"bar":2}] '{"foo":1}{"bar":2}'.fromJSONStream() // returns [{"foo":1}, {"bar":2}] ``` ### `base64` Returns the base64 encoding of a string or array of bytes ``` base64() -> base64() -> .base64() -> .base64() -> ``` Examples ```js "hello world".base64() // returns "aGVsbG8gd29ybGQ=" ``` ### `base64Raw` Returns the base64 encoding of a string or array of bytes ``` base64Raw() -> base64Raw() -> .base64Raw() -> .base64Raw() -> ``` Examples ```js "hello world".base64Raw() // returns "aGVsbG8gd29ybGQ" ``` ### `hex` Returns the base64 encoding of a string or array of bytes ``` hex() -> hex() -> .hex() -> .hex() -> ``` Examples ```js "hello world".hex() // returns "68656c6c6f20776f726c64" ``` ## Cryptography ### `sha1` Returns the SHA1 of a string or array of bytes ``` sha1() -> sha1() -> .sha1() -> .sha1() -> ``` Examples ```js "hello world".sha1() // returns "Kq5sNclPz7QV2+lfQIuc6R7oRu0=" "hello world".sha1().hex() // returns "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed" ``` ### `sha256` Returns the SHA2 of a string or array of bytes ``` sha256() -> sha256() -> .sha256() -> .sha256() -> ``` Examples ```js "hello world".sha256() // returns "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=" "hello world".sha256().hex() // returns "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" ``` ### `hmac` Returns the HMAC of a string or array of bytes ``` hmac(, , ) -> hmac(, , ) -> .hmac(, ) -> .hmac(, ) -> ``` Examples ```js "hello world".hmac("sha256", b"key") // returns "C6BvH5pjAEYeQ0VFNdw8QiPkex01cHPXU26ukOwJW+E=" "hello world".hmac("sha256", b"key").hex() // returns "0ba06f1f9a6300461e43454535dc3c4223e47b1d357073d7536eae90ec095be1" ``` ### `uuid` Returns a random UUID v4 string ``` uuid() -> ``` Examples ```js uuid() // returns "582fc58b-f983-4c35-abb1-65c507c1dc0c" ``` --- # [CEL Built-in functions for Security Policies (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/security-policies/built-in-functions/) The CEL language ([language definition](https://github.com/google/cel-spec/blob/master/doc/langdef.md)) offers the following built-in functions and macros that you can use while building your [Security Policies](/docs/enterprise/security-policies/) or [conditional requests and responses with CEL](/docs/enterprise/endpoints/common-expression-language-cel/). The following functions and macros are **always available** regardless of your configuration. The signatures of the functions below follow the format `function -> return`, and include the different data `` you can use in each of them. ## Membership functions and macros Their usage is checking different aspects of maps and arrays mostly. Macros are like function calls but check parameters differently and have different runtime semantics. Nevertheless, from a practical point of view, you can treat them as regular functions ### `size` Returns the size of different types. The cost is proportional to the number of entries on lists and maps. ``` size() -> size() -> size() -> size() -> ``` Examples ```js size("hello") // returns 5 size(["hi","there"]) // returns 2 size({"hi":1,"there":1}) // returns 2 ``` ### `in` Checks the membership of an element in a list or a map. The time cost is proportional to the product of the size of both arguments. ``` in -> ``` Examples ```js 1 in [1,2,3] // returns true "harry" in ["sally","freddy"] // returns false "foo" in {"foo": 1, "bar": 1} // returns true '::1' in req_headers['X-Forwarded-For'] // true on loopback address for IPv6 timestamp(now).getDayOfWeek() in JWT.enabled_days // true if claim allows access today ``` ### `has` Checks if a specific field exists. ``` has() -> ``` Examples ```js // Given a response data { "foo": 2} has(resp_data.foo) // returns true has(resp_data.unexisting) // returns false ``` ### `all` Checks whether a predicate `p` holds true **for all elements** of a list or map `e`. The `i` parameter is the index or key to be used when building `p`. All predicates must return true (*AND* operation). Otherwise, the function will return false. Errors are ignored. ``` e.all(i, p) ``` Examples ```js [10,20,30].all(i,i>5) // returns true because all are greater than 5 [10,20,30].all(i,i>20) // returns false ["apple", "animal"].all(i,i.startsWith('a')) // returns true {"apple":1, "animal":2}.all(i,i.contains('ap')) // returns false ``` ### `exists` Like the `all()` macro, but it combines the predicate results with the *OR* operator. Useful to check whether a specific element exists. ``` e.exists(i, p) ``` Examples ```js [10,20,30].exists(i,i==20) // returns true [-10,0,5].exists(i,i<0) // returns true ["apple", "animal"].exists(i,i.startsWith('a')) // returns true ["apple", "animal"].exists(i,i.contains('ap')) // returns true {"apple":1, "animal":2}.exists(i,i == 'zx') // returns false ``` ### `exists_one` like the `exists()` macro, but evaluates to true only if the predicate of **exactly one element** or key evaluates to true, and the rest to false. Any other combination of boolean results evaluates to false, and any predicate error causes the macro to raise an error. ``` e.exists_one(i, p) ``` Examples ```js [10,20,30].exists_one(i,i==20) // returns true [-10,2,5].exists_one(i,i>0) // returns false ["apple", "animal"].exists_one(i,i.startsWith('a')) // returns false ["apple", "animal"].exists_one(i,i.contains('ap')) // returns true {"apple":1, "animal":2}.exists_one(i,i == 'zx') // returns false ``` ### `map` Transforms a list `e` by taking each element `i` to the element given by the expression `t`, which can use the variable `i`. Any evaluation error for any element causes the macro to raise an error. The `map()` macro is not supported when `e` is a map. ``` e.map(i, p) ``` Examples ```js [1,2,3].map(n, n*n) // returns [1,4,9] [-10,2,5].map(i,i+1) // returns [-9,3,6] ``` ### `filter` Returns the sublist of all elements `i` of list `e`, which evaluates to true in the predicate expression `p`. If no elements evaluate to be true, the result is an empty list. Any evaluation error for any element causes the macro to raise an error. The `filter()` macro is not supported on maps. ``` e.filter(i, p) ``` Examples ```js [1,2,3].filter(i, i % 2 > 0)] // returns [1,3] ["apple", "animal"].filter(i,i.startsWith('ap')) // returns ["apple"] ["apple", "animal"].filter(i,i == 'xz') // returns [] ``` ## String functions ### `contains` Checks if a substring is contained in a string. ``` .contains() -> ``` Examples ```js // Given a path /testing req_path.contains('tes') // returns true req_path.contains('toe') // returns false ``` ### `startsWith` Checks if a substring is the start of a string ``` .startsWith() -> ``` Examples ```js // Given a path /testing req_path.startsWith('/test') // returns true req_path.startsWith('/toast') // returns false ``` ### `endsWith` Checks if a substring is the end of a string ``` .endsWith() -> ``` Examples ```js // Given a path /testing req_path.endsWith('ing') // returns true req_path.endsWith('test') // returns false ``` ### `matches` Checks a string against a **regular expression** ([RE2 syntax](https://github.com/google/re2/wiki/Syntax)) ``` .matches() -> matches(,) -> ``` Examples ```js "test".matches('^/[a-zA-Z]{4}$') // returns true "soap".matches('^/[a-zA-Z]{4}$') // returns true "sites".matches('^/[a-zA-Z]{4}$') // returns false matches('123', '[0-9]+') // returns true matches('test', '[0-9]+') // returns false // Given an endpoint /hey/{id} called as /hey/kevin req_params.Id.matches('k.*') // returns true req_params.Id.matches('m.*') // returns false ``` ## Time functions ### `timestamp` Returns the timestamp from a string in RFC3339 format. ``` timestamp() -> ``` Examples ```js timestamp(now) // returns current timestamp ``` ### `getDate` Returns the day of the month. Index starts at 1 ``` .getDate() -> ``` Examples ```js // Given it's February 1st timestamp(now).getDate() // returns 1 ``` ### `getMonth` Returns the month from the date. Starts with index 0 on January and ends at 11 ``` .getMonth() -> ``` Examples ```js timestamp(now).getMonth() // returns 2 on March ``` ### `getFullYear` Returns the year. ``` .getFullYear() -> ``` Examples ```js timestamp(now).getFullYear() // returned 2023 when writing this example ``` ### `getDayOfMonth` Returns the day of the month. The first month starts at index 0. ``` .getDayOfMonth() -> ``` Examples ```js // Given it's February 1st timestamp(now).getDayOfMonth() // returns 0 ``` ### `getDayOfWeek` Returns the day of the week. Starts on Sunday with an index 0. ``` .getDayOfWeek() -> ``` Examples ```js timestamp(now).getDayOfWeek() // returns 0 on Sunday timestamp(now).getDayOfWeek() // returns 1 on Monday timestamp(now).getDayOfWeek() // returns 6 on Saturday (timestamp(now).getDayOfWeek() + 6) % 7 <= 4 // returns false on weekend (Sat-Sun) (timestamp(now).getDayOfWeek() + 6) % 7 <= 4 // returns true on weekdays (Mon-Fri) ``` ### `getDayOfYear` Returns the day of the year. Starts with index 0. ``` .getDayOfYear() -> ``` Examples ```js timestamp(now).getDayOfYear() // returns 0 on January 1st timestamp(now).getDayOfYear() // returns 10 on January 11th ``` ### `getHours` Returns the hours from the date or a duration string. Starts with index 0 and ends at 23 ``` .getHours() -> .getHours() -> ``` Examples ```js timestamp(now).getHours() // returns 0 at midnight timestamp(now).getHours() // returns 7 when the alarm clock sounds ``` ### `getSeconds` Returns the seconds from the date or a duration string. Starts with index 0 and ends at 999 ``` .getSeconds() -> .getSeconds() -> ``` Examples ```js timestamp(now).getSeconds() // returns 5 at 23:01:05 ``` ### `getMilliseconds` Returns the millisecons from the date or a duration string. Starts with index 0 and ends at 999 ``` .getMilliseconds() -> .getMilliseconds() -> ``` Examples ```js timestamp(now).getMilliseconds() // returns 0 at midnight sharp ``` --- # [Security Policies Playbook (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/security-policies/playbook/) An example is sometimes self-explanatory and lets you see the potential of the kind of stuff you can do with policies. The following examples demonstrate who to apply different policies to your API, but possibilities are endless! ## Check if user has one of the selected roles (RBAC) or attributes (ABAC) This configuration would go inside the `endpoint`’s extra\_config. Checks that the role is *admin* or *root*, and that the department is also contained in a list: ```json { "extra_config": { "security/policies": { "jwt": { "policies": [ "JWT.role in ['admin', 'root'] || JWT.permissions.can_write_users", "JWT.department in ['IT','HR']" ] } }, "auth/validator": { "@comment": "Add here your auth/validator settings to enable 'jwt' policies" } } } ``` ## Create a maintenance window Disable requests between the 10am and noon on the 1st of January (UTC -5), and return a custom `500` status code: ```json { "extra_config": { "security/policies": { "req": { "policies": [ "timestamp(now)< timestamp('2023-01-01T10:00:20.000-05:00') || timestamp(now)> timestamp('2023-01-01T12:00:20.000-05:00')" ], "error": { "body": "The system is down for maintenance", "status": 500 } } } } } ``` ## Endpoint lifecycle You can decide to publish an endpoint on a specific date (or unpublish it). Here’s an example to deploy an endpoint on a date: ```json { "extra_config": { "security/policies": { "req": { "policies": [ "timestamp(now) > timestamp('2023-01-01T10:00:20.000-05:00')" ], "error": { "body": "The endpoint /foo is not yet implemented", "status": 404 } } } } } ``` ## User is from a specific country Requires enabling the [GeoIP integration](/docs/enterprise/endpoints/geoip/) and allowing the `X-Geoip` header in. Example to allow traffic from the US or France only. ```json { "input_headers": ["X-Geoip"], "extra_config": { "security/policies": { "req": { "policies": [ "geoIP().Country.IsoCode in ['US','FR']" ] } } } } ``` ## Enforce query string validation Make sure the query string `user` contains a value that matches a regexp. ```json { "input_query_strings": ["user"], "extra_config": { "security/policies": { "req": { "policies": [ "req_querystring.user.exists(u,u.matches('[0-9]+'))" ] } } } } ``` Similar alternative policies: - `hasQuerystring('user')`: checks the user is not empty. - `int(req_querystring.user[0]) > 1000`: checks if the user is larger than 1000. - `req_querystring.user[0].isAlphanumeric()`: checks if the user is an alphanumeric string. ## Ensure a JWT claim matches part of the URL Suppose a JWT token contains a claim `sub`, and we want to make sure users can access only the endpoint that matches their profile, and is part of the URL requested. For instance, `/users/john` is accessible only with valid JWT `{"sub": "john"}`. ```json { "endpoint": "/users/{id}", "input_headers":["X-Example-Claim"], "extra_config": { "security/policies":{ "req": { "policies": [ "req_headers['X-Example-Claim'][0].lowerAscii() == req_params.Id.lowerAscii()" ] } }, "auth/validator": { "@comment": "irrelevant config details omitted", "propagate_claims": [ ["sub", "X-Example-Claim"] ] } } } ``` Notice that the `auth/validator` is the first component in receiveing the request, and propagates the claim `sub` as the header `X-Example-Claim`. The header is added into the `input_headers` to make sure the endpoint can work with it. Finally the policy converts both the parameter `{id}` and the header to lower case (normalize) and checks that they have the same value. ## A cookie is set and sent to consume an endpoint Check that a specific cookie is set to proceed with a tracking option. For instance, there is a cookie `GDPR=yes` set by the user. ```json { "endpoint": "/tracking/{id}", "input_headers":["Cookie"], "extra_config": { "security/policies":{ "req": { "policies": [ "getCookie('GDPR') == 'yes'" ], "error": { "body": "Tracking not allowed by user", "status": 403 } } } } } ``` ## The response lacks attributes Checking that a specific response from the backend(s) contains essential data, otherwise fail. ```json { "extra_config": { "security/policies":{ "resp": { "policies": [ "has(resp_data.important_attribute)" ], "error": { "body": "Couldn't fetch the required data", "status": 503 } } } } } ``` --- # [Authorization and Authentication (authZ + authN)](https://www.krakend.io/docs/authorization/) Authorization and authentication are fundamental to managing access to your APIs. While authentication verifies the identity of a user or service (e.g., *is this a valid user/pass?*), the authorization checks their access rights (e.g., *Should John see this resource?*). KrakenD offers a versatile set of features enabling you to control who can access your APIs and what actions they are authorized to perform. Whether your use case involves simple client credentials or complex multi-provider authentication scenarios, KrakenD ensures your system is both secure and scalable. **This introduction provides an overview of KrakenD’s key authorization and authentication features.** ## How does auth change when you have a gateway? When you work with a gateway, you add a piece of software **in the middle of the client-server communication**. Because it is in the middle, you have the opportunity to handle the authentication between the two sides separately and independently, adding more options to your setup. ![auth-overview.mmd diagram](/images/documentation/diagrams/auth-overview.mmd.svg) The possible scenarios you can have are: | Auth Scenarios | | |---------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------| | **No auth**: Public endpoints. No authentication is needed to use them. | ![auth-overview-no-auth.mmd diagram](/images/documentation/diagrams/auth-overview-no-auth.mmd.svg) | | **Delegated auth**: The gateway does nothing other than forwarding the auth headers to the backend | ![auth-overview-forward.mmd diagram](/images/documentation/diagrams/auth-overview-forward.mmd.svg) | | **Clients auth**: The client is authenticated, but the communication between the gateway and the service is open | ![auth-overview-client.mmd diagram](/images/documentation/diagrams/auth-overview-client.mmd.svg) | | **Clients and gateway auth**: Both the client and the gateway use authentication, they can be completely different. | ![auth-overview-all.mmd diagram](/images/documentation/diagrams/auth-overview-all.mmd.svg) | | **Gateway auth**: Public access for clients, but auth between the gateway and the services | ![auth-overview-backend.mmd diagram](/images/documentation/diagrams/auth-overview-backend.mmd.svg) | It is important to realize that since Authorization and Authentication are independent of each other, you can **mix authentication methods** and add new options on a platform that does not support them. You can enable **machine-to-machine** between the gateway and the services and other methods with the clients. A few use cases of different auth implementations could be: - Your platform does not have any authentication today, but you want to open it protected to the world - You have a classic monolith with user/password/session, and you want to modernize it with JWT - Your internal service requires a hardcoded user and password, but you would like to use granular JWT tokens to publish its functionality. - You want to implement different authentication methods between the users and the gateway (e.g, publish with API keys, connect to services with mTLS) - etc. You can make many possible combinations, and the independence of these two sides gives you a lot of power. KrakenD provides multiple tools to get this job done. ## Authentication (AuthN) Authentication takes care of **recognizing** legitimate users. Generally, you will delegate the authentication to an Identity Provider (for short, **IdP**). The IdP can be your existing monolithic application that checks a user and password or a more fancy IdP using industry standards, whether it’s SaaS or self-hosted (e.g., Auth0, Azure AD, Google Firebase, Keycloak…). The IdP takes care of the authentication methods you want to support. E.g., Password-based User Authentication, One-time Password (OTP), Single Sign-On (SSO), Biometric/Passwordless Authentication, etc. The IdP will then generate a signed token that KrakenD can use to authorize the user. Aside from tokens, you can also use a certificate to authenticate to your gateway or the gateway to connect to a backend service. Both scenarios are supported on [Mutual TLS (mTLS)](/docs/authorization/mutual-authentication/). In addition, the Enterprise Edition adds other methods like [Google Cloud service-to-service](/docs/enterprise/authentication/gcloud/) or more “classic” (and less secure) authentication methods, such as [API-key authentication](/docs/enterprise/authentication/api-keys/), [Basic Authentication](/docs/enterprise/authentication/basic-authentication/), or even [Microsoft’s NTLM (NT Lan Manager) Authentication](/docs/enterprise/authentication/ntlm/). ## Authorization (AuthZ) Authorization determines what authenticated users or systems are **permitted** to do. KrakenD provides flexible options for fine-grained access control. The most common method is Token-based authorization (see below). In addition to JWT, you can also use [OAuth2 Client Credentials](/docs/enterprise/authorization/client-credentials/) for machine-to-machine communication. KrakenD verifies the client’s credentials with this flow and enforces scope-based access controls. KrakenD’s ability to integrate with multiple identity providers simultaneously (Enterprise ) offers great flexibility. Whether catering to different user groups, migrating, multi-tenant, or implementing complex organizational structures, KrakenD adapts to your needs. ## Token-based authorization Today, most applications use JSON Web Tokens (JWTs), a widely used method for securing APIs. They are our first recommended way to limit the access of end-users to your API. KrakenD does not issue the JWT tokens (as we said, this is the job of the identity provider) but validates them to ensure their authenticity and can check claims, such as audience, issuer, and expiration, to name a few examples. Read the [JWT Overview](/docs/authorization/jwt-overview/) for an introduction to token-based authorization in KrakenD. When working with tokens, there are different things you can do: - [JWT Signing](/docs/authorization/jwt-signing/): If you don’t yet have an Identity Provider, you can make your classic login controller return a JSON response emulating a token and let KrakenD sign it with secure cryptography. This enables your system to provide trusted, verifiable tokens for authenticated clients even when you have not transitioned your users to an identity service. - [JWT Validation](/docs/authorization/jwt-validation/): This is the core functionality, where a user sends a token generated by an Identity Provider, and KrakenD checks for its validity and any required permissions to access the resource. - [JWK Key Caching](/docs/authorization/jwk-caching/): As identity providers don’t change their signing private keys very often, it is recommended that you cache the public key in the gateway to avoid unnecessary internal calls to fetch public keys, improving performance without sacrificing security. Apart from that, you can also apply business rules to incoming tokens using [CEL](/docs/endpoints/common-expression-language-cel/) or [Security Policies](/docs/enterprise/security-policies/), or [revoke valid tokens](/docs/authorization/revoking-tokens/) to handle scenarios like compromised credentials or access revocation. The [Revoke Server](/docs/enterprise/authentication/revoke-server/) centralizes and manages token blocklists, ensuring invalidated tokens cannot be reused. If you have [multiple identity providers](/docs/enterprise/authentication/multiple-identity-providers/) (Enterprise feature), you can let KrakenD work with all of them simultaneously. ## Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC) It is important to know who can access a resource and decide if a user has a specific role or attribute that grants them access. The different components of authz in KrakenD allow you to set custom rules validated on runtime during **requests, responses, and token validation** itself. From issuer, audience, or [role validation](/docs/authorization/jwt-validation/#roles) in JWT tokens to [custom policies](/docs/enterprise/security-policies/) with Security Policies (Enterprise ), you can control in the gateway itself who sees what and offload this responsibility from your services. The policies allow you to implement all sorts of validations and user access control, from parameter compliance, to **RBAC** (Role-Based Access Control) and **ABAC** (Attribute-Based Access Control) strategies. ## Integration with External Identity Providers Because KrakenD adopted security standards, you can use it **with any provider**: we still need to find a single major provider that doesn’t follow the JSON Web Encryption (RFC 7516), JSON Web Signature (RFC 7515), or JSON Web Token (RFC 7519) specifications. That being said, it means that you have native integration to validate tokens, manage user sessions, and enforce rules with SaaS providers and self-hosted tools. Some examples are: - Keycloak - Auth0 /Okta - Google - Azure AD - OneLogin - PingIdentity - Descope - Gluu - and many, many more. ## Do I need to expose my IdP? A common question we get is whether **it is necessary to publish my IDP to the Internet**? Not really. If you have a self-hosted IdP, you can expose it to the Internet, but you can also place it behind the gateway and publicly enable the routes that users will use. For instance, you could have a setup like this: ![auth-overview-architecture.mmd diagram](/images/documentation/diagrams/auth-overview-architecture.mmd.svg) In this scenario, the `/token` endpoint passes through KrakenD, allowing you to set rate limiting or any other protection mechanism you consider to secure even more your IdP. The client stores locally the token for the next interactions, and send the bearer to access resources through KrakenD. The IdP is not contacted again until the token is expired (when the client will refresh it). --- # [API Key Authentication in KrakenD (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/authentication/api-keys/) API key authentication enables a **Role-Based Access Control (RBAC)** and a **rate-limiting** mechanism based on an API key passed by the client. For the desired endpoints, KrakenD rejects requests from users who do not provide a valid **key**, users trying to access a resource with insufficient permissions for their **role**, or exceeding the defined quota. The authentication is granular and works **per endpoint**, meaning you can combine public endpoints (no API Key needed) and private endpoints in the same configuration. The API Key component requires you to declare at least two different blocks of configuration: 1. The declaration of all known users and roles, placed at the root’s `extra_config` middleware section. 2. The association of which endpoints are protected, placed at each endpoint. ## Declaring API Key users and roles The first block of configuration declares how KrakenD retrieves keys, which ones are available, and the roles recognized by the system. It requires at least the `keys` attribute with a list of objects declaring each client’s `key` and `roles`. It looks like this: ```json { "version": 3, "extra_config": { "auth/api-keys": { "strategy": "header", "identifier": "Authorization", "keys": [ { "key": "4d2c61e1-34c4-e96c-9456-15bd983c5019", "roles": ["role1", "role2"], "@description": "ACME Inc." }, { "key": "58427514-be32-0b52-b7c6-d01fada30497", "roles": ["role1","role3"], "@description": "Administrators Inc." } ] } } } ``` The configuration above is a simple implementation that stores API keys in plain text. Hashing API keys instead of saving them in plain text While the example above shows readable API keys, we recommend saving the hashed API keys in the configuration. The list of possible configuration attributes is as follows: ##### Fields of API-key Authentication \* required fields ##### [`hash`](https://www.krakend.io/docs/enterprise/authentication/api-keys/#hash "Click to copy link to hash") The hashing function used to store the value of the key. When you use `plain` the API key is written as it will passed by the user. The rest of the hashes require you to save the API key after applying the desired function. Possible values are: `"plain"` , `"fnv128"` , `"sha256"` , `"sha1"` Defaults to `"plain"` ##### [`identifier`](https://www.krakend.io/docs/enterprise/authentication/api-keys/#identifier "Click to copy link to identifier") string The header name or the query string name that contains the API key. Defaults to `key` when using the `query_string` strategy and to `Authorization` when using the `header` strategy. The identifier set here is used across all endpoints with API key authentication enabled, but they can override this entry individually. Examples: `"Authorization"` , `"X-Key"` Defaults to `"Authorization"` ##### [`keys`](https://www.krakend.io/docs/enterprise/authentication/api-keys/#keys "Click to copy link to keys") * array of objects A list of objects defining each API Key. Each item of keys accepts the following properties: ##### [`key`](#keys-key "Click to copy link to key") string The secret key used by the client to access the resources. Don’t have a key? Execute in a terminal `uuidgen` to generate a random one. ##### [`roles`](#keys-roles "Click to copy link to roles") array of strings All the roles this user has. See roles as all the identifying labels that belong to this client. ##### [`propagate_role`](https://www.krakend.io/docs/enterprise/authentication/api-keys/#propagate_role "Click to copy link to propagate_role") string The name of a header that will propagate to the backend containing the matching role. The backend receives no header when the string is empty, or the attribute is not declared. Otherwise, the backend receives the declared header name containing **the first matching role of the user**. The header value will be `ANY` when the endpoint does not require roles. For instance, if an API key has roles `[A, B]`, and the endpoint demands roles `[B, C]`, the backend will receive a header with the value `B`. Example: `"X-Krakend-Role"` Defaults to `""` ##### [`salt`](https://www.krakend.io/docs/enterprise/authentication/api-keys/#salt "Click to copy link to salt") string A salt string for the desired hashing function. When provided, the API key is concatenated after the salt string and both hashed together. Example: `"mySalt"` Defaults to `""` ##### [`strategy`](https://www.krakend.io/docs/enterprise/authentication/api-keys/#strategy "Click to copy link to strategy") Specifies where to expect the user API key, whether inside a header or as part of the query string. The strategy set here is used across all endpoints with API key authentication enabled, but they can override this entry individually. Possible values are: `"header"` , `"query_string"` Defaults to `"header"` Schema: [https://www.krakend.io/schema/v2.13/auth/api-keys.json](https://www.krakend.io/schema/v2.13/auth/api-keys.json "JSON schema definition") It is also shown in the example a `@description` attribute to help you identify who this key belongs to. This attribute is metadata and unneeded by KrakenD, but it eases the administration of keys. Use keys starting with `@` to place comments in the file. ### Saving API keys hashed When you declare the API keys in the list, if you don’t declare a hashing function, you write the `key` you want to pass when consuming endpoints as is. When you declare a `hash` function (other than the default `plain` text), you must write the API key in the configuration after hashing it with the corresponding algorithm. These are the supported hashing functions: - `plain`: No hashing. Key saved in plain text. - `fnv128`: Hash the key using the non-cryptographic function [128-bit FNV-1](https://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function). - `sha256`: Hash the key using [SHA256](https://pkg.go.dev/crypto/sha256#New) ([RFC6234](https://www.rfc-editor.org/rfc/rfc6234.html)) - `sha1`: Hash the key using [SHA1](https://pkg.go.dev/crypto/sha1#New) ([RFC3174](https://www.rfc-editor.org/rfc/rfc3174.html)) In addition, you can pass a `salt` string, which will be prepended to the API key when resolving an API key: ```js string_to_hash = salt + api_key ``` #### Example: Saving API keys hashed with SHA256 Let’s say you want to give a user the API key `abc`. To hash the key, you can add a salt string, like `mySalt`, to spice up the output hash. Then you could type in a terminal: Generate SHA256 of an API Key  ``` $echo -n "mysaltabc" | sha256sum 2ad27678dd4fdf811ffe7f9c77801ab66e2cc62d08abb393451ddda48c1eba3f - ``` Mac users might need to replace `sha256sum` with `shasum -a 256`. Then, the configuration needs to know the `salt` you chose to hash the key, and which `hash` function, so the gateway can produce the same output. It would look like this: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "debug_endpoint": true, "extra_config": { "auth/api-keys": { "salt": "mySalt", "hash": "sha256", "keys": [ { "@description": "ACME Inc.", "key": "2ad27678dd4fdf811ffe7f9c77801ab66e2cc62d08abb393451ddda48c1eba3f", "@example": "echo -n 'mySaltabc' | sha256sum", "roles": [ "user", "whitelabel" ] } ] } }, "endpoints": [ { "endpoint": "/foo", "backend": [ { "url_pattern": "/__debug/user", "host": [ "http://localhost:8080" ] } ], "extra_config": { "auth/api-keys": { "roles": [ "user" ] } } } ] } ``` Notice that the `key` in the configuration is the hashed value, not the API key. A leakage of the KrakenD configuration would not reveal the original API key `abc` that allows connecting to the endpoint. #### Example: Saving API keys hashed with FNV-1 Here is another example with a small script in Go that generates the hash of an API key with a given salt in FNV-1 ([test it online here](https://go.dev/play/p/hHksLLCUJy3)): ```go package main import ( "fmt" "hash/fnv" ) func main() { hash := fnv.New128() api_key := "4d2c61e1-34c4-e96c-9456-15bd983c5019" salt := "mySalt" hash.Write([]byte(salt + api_key)) fmt.Printf("%x", hash.Sum(nil)) } ``` The configuration for this case would be as follows: ```json { "auth/api-keys": { "strategy": "header", "identifier": "X-Key", "hash": "fnv128", "salt": "mySalt", "@hashed-with": "https://go.dev/play/p/hHksLLCUJy3", "keys": [ { "@key-plain": "4d2c61e1-34c4-e96c-9456-15bd983c5019", "key": "e0f7fce642685956791e58b835e26786", "roles": [ "admin" ], "@description": "ACME Inc." } ] } } ``` ## Protecting endpoints by API Key Now that all users and roles are declared, it’s time to reference them in the endpoints. You must include the namespace `auth/api-keys` in its `extra_config` for all the endpoints needing API Key validation. Any endpoints not having the namespace are not API Key protected. For example, the endpoints could look like this: ```json { "endpoint": "/admin", "backend": [ { "url_pattern": "/__debug/admin", "host": [ "http://localhost:8080" ] } ], "extra_config": { "auth/api-keys": { "roles": [ "admin" ], "client_max_rate": 5 } } } ``` The endpoint configuration accepts the following parameters: ##### Fields of API-key validation \* required fields ##### [`client_max_rate`](https://www.krakend.io/docs/enterprise/authentication/api-keys/#client_max_rate "Click to copy link to client_max_rate") number If you want to limit the endpoint usage to this specific user at a number of requests per second. Exceeding the number of requests per second will give the client a `429 Too Many Requests` HTTP status code. ##### [`identifier`](https://www.krakend.io/docs/enterprise/authentication/api-keys/#identifier "Click to copy link to identifier") string The header name or the query string name that contains the API key. By default uses any value declared in the `auth/api-keys` component in the service level. ##### [`roles`](https://www.krakend.io/docs/enterprise/authentication/api-keys/#roles "Click to copy link to roles") * array of strings The list of roles allowed to access the endpoint. Values must match (case sensitive) definitions in the `keys` section at the service level of `auth/api-keys`. API Keys not having the right role, or unauthenticated requests, will receive a `401 Unauthorized`. ##### [`strategy`](https://www.krakend.io/docs/enterprise/authentication/api-keys/#strategy "Click to copy link to strategy") Specifies where to expect the user API key, whether inside a header or as part of the query string. When you change the strategy at the endpoint level, **you should also set the identifier**, otherwise you could have for instance, a query string strategy expecting to have a URL like `/foo?Authorization=YOUR-KEY`. Possible values are: `"header"` , `"query_string"` Schema: [https://www.krakend.io/schema/v2.13/endpoint\_extra\_config.json](https://www.krakend.io/schema/v2.13/endpoint_extra_config.json "JSON schema definition") Here is a full example of two different API Keys that can access three different endpoints: ```json { "version": 3, "extra_config": { "auth/api-keys": { "keys": [ { "@description": "ACME Inc.", "key": "4d2c61e1-34c4-e96c-9456-15bd983c5019", "roles": ["user", "whitelabel"] }, { "@description": "Administrators Inc.", "key": "58427514-be32-0b52-b7c6-d01fada30497", "roles": ["admin", "user"] } ] } }, "endpoints": [ { "endpoint": "/public", "backend": [ { "url_pattern": "/__debug/public", "host": [ "http://localhost:8080" ] } ] }, { "endpoint": "/admin", "backend": [ { "url_pattern": "/__debug/admin", "host": [ "http://localhost:8080" ] } ], "extra_config": { "auth/api-keys": { "roles": [ "admin" ], "client_max_rate": 5 } } }, { "endpoint": "/user", "backend": [ { "url_pattern": "/__debug/user", "host": [ "http://localhost:8080" ] } ], "extra_config": { "auth/api-keys": { "roles": [ "user" ] } } } ] } ``` ## Authenticating requests using API Key When you don’t set any `strategy` in the configuration clients or use the `header` strategy, clients need to make requests to KrakenD adding the `Authorization: Bearer` or `Authorization: Basic`, but if you pass neither, the API Key is the whole content of the header value. Therefore, the value inside the header must strictly match the API key defined in the configuration after applying the selected procedure. See below. ### Using `Bearer` The format of the header is as follows: ``` Authorization: Bearer YOUR-KEY ``` For instance, having declared in the configuration a key `4d2c61e1-34c4-e96c-9456-15bd983c5019` that should be capable of seeing `/foo`, you should make a call like this: Passing the key with Bearer  ``` $curl -H'Authorization: Bearer 4d2c61e1-34c4-e96c-9456-15bd983c5019' http://localhost:8080/foo {"message":"pong"} ``` You write the API key after the bearer precisely as declared. ### Using `Basic` authentication instead of `Bearer` Another way of authenticating is using `Basic` authentication. However, it does not present any benefit compared to `Bearer` other than backward compatibility with legacy implementations. The difference is that **clients must pass the key in `base64`** : ``` Authorization: Basic base64(YOUR-KEY:) ``` Notice that the encoding of the key contains an ending `:`. The final colon prevents your client from being asked for a password. #### `Basic` authentication example Repeating the previous `Bearer` example, let’s connect using `Basic` authentication now. The first step is to base64-encode: Convert Key to base64  ``` $echo -n "4d2c61e1-34c4-e96c-9456-15bd983c5019:" | base64 NGQyYzYxZTEtMzRjNC1lOTZjLTk0NTYtMTViZDk4M2M1MDE5Ogo= ``` And then pass the header: Basic authentication  ``` $curl -H"Authorization: Basic NGQyYzYxZTEtMzRjNC1lOTZjLTk0NTYtMTViZDk4M2M1MDE5Ogo=" http://localhost:8080/foo {"message":"pong"} ``` You can also not do the base64 encoding if you use a client with regular basic auth implemented, which will encode automatically (again, notice the `:` at the end of the key): Basic authentication  ``` $curl -i --user 4d2c61e1-34c4-e96c-9456-15bd983c5019: http://localhost:8080/foo {"message":"pong"} ``` In case you forget the colon `:` you’ll be prompted for the password (when there isn’t): Basic authentication  ``` $curl -i --user 4d2c61e1-34c4-e96c-9456-15bd983c5019 http://localhost:8080/user Enter host password for user '4d2c61e1-34c4-e96c-9456-15bd983c5019': HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 X-Krakend: Version X-Krakend-Completed: true Date: Tue, 10 Nov 2020 17:37:09 GMT Content-Length: 18 {"message":"pong"} ``` ### Using an API key directly as a value Another way of authenticating is passing the API Key directly in a header, without the `Basic` or the `Bearer`. When this happens, the full value of the header is used as API key. For instance, when your clients call the gateway as follows: Passing the api key directly  ``` $curl -H'x-api-key: 4d2c61e1-34c4-e96c-9456-15bd983c5019' http://localhost:8080/foo {"message":"pong"} ``` You must set an `identifier` such as `X-Api-Key`. ### Using a custom `Authorization` header You can replace `Authorization` with any other header using the `identifier` option. But in all cases, the `Bearer`, the `Basic`, or no prefix are evaluated as described above. For instance: ```json { "auth/api-keys": { "strategy": "header", "identifier": "X-Key" } } ``` Clients will need to make calls like Passing the key with Bearer  ``` $curl -H'X-Key: Bearer 4d2c61e1-34c4-e96c-9456-15bd983c5019' http://localhost:8080/foo {"message":"pong"} ``` Or Passing the key without a Bearer  ``` $curl -H'X-Key: 4d2c61e1-34c4-e96c-9456-15bd983c5019' http://localhost:8080/foo {"message":"pong"} ``` ### Passing the API key using a query string If you set the `strategy` to `query_string`, clients can directly pass the key on the URL using ’ ?key=xxxx\`. For instance: Accessing the /user endpoint with an API key on the URL  ``` $curl http://localhost:8080/user?key=4d2c61e1-34c4-e96c-9456-15bd983c5019 {"message":"pong"} ``` You can set a different `identifier` section instead of `key` to something else. For instance, let’s use `?mycustomapikey=xxxx`: ```json { "auth/api-keys": { "strategy": "query_string", "identifier": "mycustomapikey", "keys": [{}] } } ``` A configuration like the above will require users to make calls like this: Using custom query strings  ``` $curl http://localhost:8080/user?mycustomapikey=4d2c61e1-34c4-e96c-9456-15bd983c5019 {"message":"pong"} ``` ## Complete API Key configuration example The following configuration snippet is fully functional. Run KrakenD with the debug flag (`krakend run -d`) to test it locally, as it uses itself as a backend. Explanation below: ```json { "version": 3, "extra_config": { "auth/api-keys": { "strategy": "header", "identifier": "Authorization", "propagate_role": "X-Krakend-Role", "keys": [ { "@description": "ACME Inc.", "key": "4d2c61e1-34c4-e96c-9456-15bd983c5019", "roles": [ "user", "whitelabel" ] }, { "@description": "Administrators Inc.", "key": "58427514-be32-0b52-b7c6-d01fada30497", "roles": [ "admin", "user" ] } ] } }, "endpoints": [ { "endpoint": "/public", "backend": [ { "url_pattern": "/__debug/public", "host": [ "http://localhost:8080" ] } ] }, { "endpoint": "/admin", "backend": [ { "url_pattern": "/__debug/admin", "host": [ "http://localhost:8080" ] } ], "extra_config": { "auth/api-keys": { "roles": [ "admin" ] } } }, { "endpoint": "/user", "backend": [ { "url_pattern": "/__debug/user", "host": [ "http://localhost:8080" ] } ], "extra_config": { "auth/api-keys": { "roles": [ "user" ] } } }, { "endpoint": "/custom-header", "backend": [ { "url_pattern": "/__debug/custom-header", "host": [ "http://localhost:8080" ] } ], "extra_config": { "auth/api-keys": { "roles": [ "admin" ], "@comment": "Instead of the Authorization header, we will use X-ADMIN-KEY", "identifier": "X-User-Key", "strategy": "header" } } } ] } ``` In this example, we have enabled two different API users. One with key `4d2c61e1-34c4-e96c-9456-15bd983c5019` (let’s say a customer of *ACME Inc.*) and another one with the key `58427514-be32-0b52-b7c6-d01fada30497` (an administrator of my company). Both users share the role `users`, and they can access all the protected endpoints as this role is included. Nevertheless, the `/admin` endpoint is reachable by the administrator only, as the role “admin” is required. The backend also receives a header `X-Krakend-Role` with the matching role. The endpoint `/public` is always accessible, with or without the `Authorization` header, as it does not include the API key middleware configuration. Finally, all endpoints expected the `Authorization` header, but in the endpoint `/custom-header`, we decided to use a different one and override it for this endpoint only. Therefore, we will use the `X-User-Key` header instead. And now some sample interactions: Accessing the /user endpoint with a valid user (Bearer)  ``` $curl -H'Authorization: Bearer 4d2c61e1-34c4-e96c-9456-15bd983c5019' http://localhost:8080/user {"message":"pong"} ``` Accessing the /user endpoint (Basic)  ``` $curl -i --user 4d2c61e1-34c4-e96c-9456-15bd983c5019: http://localhost:8080/user HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 X-Krakend: Version X-Krakend-Completed: true Date: Tue, 10 Nov 2020 17:07:46 GMT Content-Length: 18 {"message":"pong"} ``` Accessing the /admin endpoint with a non-admin user  ``` $curl -iG -H'Authorization: Bearer 4d2c61e1-34c4-e96c-9456-15bd983c5019' http://localhost:8080/admin HTTP/1.1 401 Unauthorized Date: Tue, 10 Nov 2020 17:01:03 GMT Content-Length: 0 ``` Accessing the /user endpoint with an invalid key  ``` $curl -iG -H'Authorization: Bearer INVALID-API-KEY' http://localhost:8080/user HTTP/1.1 401 Unauthorized Date: Tue, 10 Nov 2020 16:59:35 GMT Content-Length: 0 ``` Accessing the /public endpoint  ``` $curl http://localhost:8080/public {"message":"pong"} ``` Accessing the /custom-header endpoint  ``` $curl -iG -H'X-User-Key: Bearer 4d2c61e1-34c4-e96c-9456-15bd983c5019' http://localhost:8080/custom-header {"message":"pong"} ``` ## Rate limiting usage of API Keys The attribute `client_max_rate` allows you to set the maximum requests per second you allow in an endpoint or in the service. The rate limit you set is the same for all the API Keys. For instance, given the following endpoint configuration: ```json { "endpoint": "/members", "backend": [ { "url_pattern": "/api/v1/members" } ], "extra_config": { "auth/api-keys": { "roles": [ "admin", "user" ], "client_max_rate": 5 } } } ``` With this configuration, API Keys are limited to 5 requests per second. But what if you wanted to set different limits for the `admin` and the `user` role? In that case you can use instead the [rate limiting based on tiers](/docs/enterprise/service-settings/tiered-rate-limit/) and remove the `client_max_rate` from this configuration. ## Onboard new API key customers without restarts As the KrakenD configuration is static and only changes once you restart the process or you do a deployment, you might find it inconvenient to onboard new customers with new API keys. The following advice can be handy. Create a pool of available API keys instead of waiting for user registration in your system to insert a new API key. KrakenD has stable performance; it doesn’t change if you have 100 keys or 1 million. An easy way to generate API keys could be as follows: Creating an API key  ``` $uuidgen ``` If you want to generate several keys and write part of the configuration file, you could use something like: Creating a pool of 50 API keys  ``` $for i in $(seq 50) do key=$(uuidgen) echo "{ \"key\": \"$key\", \"roles\": [\"silver-plan\"], \"@status\": \"free\" }," done ``` Then you would use the output above to create a partial file `api-keys.json` that would be used on KrakenD with [Flexible Configuration](/docs/configuration/flexible-config/). Finally, your backend should have this list somewhere, and when a new user registers, you assign one of the free keys to the new user. You can deploy from time to time when you are running out of API keys in the pool or if you want to write the assignment in the configuration. --- # [Basic Authentication (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/authentication/basic-authentication/) The Basic Authentication functionality protects access to selected endpoints using basic username and password credentials. The functionality works at the router level, and the backend is never hit when the authentication fails. The plugin version is now deprecated Prior to v2.2, the basic authentication was provided by a plugin. The plugin’s usage is still supported for backward compatibility but is considered deprecated. Please follow the steps at the end of this document to upgrade. ## How does it work KrakenD feeds from a regular`.htpasswd` file that declares all the possible combinations of users and credentials that KrakenD recognizes. You can also declare users inline directly in the configuration file. You must store these credentials in `bcrypt` format. There is no special tooling required to administer this file other than having the `htpasswd` command in the development machine (the command is part of the `apache2-utils` package in Linux). When KrakenD starts, it reads the `.htpasswd` file if declared in the configuration and loads it in memory. As a result, the credential-checking process does not need to open the file, ensuring that the system does not rely on I/O. The basic authentication applies only to the endpoints containing an `auth/basic` entry. The rest are public or use other authentication/authorization mechanisms. When a user fails to provide valid credentials, a `401 Unauthorized` status code is returned. ## Configuration The namespace you need to enable basic authorization is `auth/basic`, and you can place it at the **endpoint level** (required), the **service level** (optional), or **both**. Include the `auth/basic`’s `extra_config` in the desired **endpoint** that needs to check the credentials with the credentials file and/or usernames. If you want to use the same credential sets across multiple endpoints, you can place the namespace at the **service level** instead (root of the file) and include an empty `extra_config` reference (`"auth/basic": {}`) which will inherit its settings. When the endpoint includes a non-empty configuration object, if you have declared settings at the service level, then you are overriding the whole object. For instance: ```json { "version": 3, "extra_config": { "auth/basic": { "@comment": "The following configuration applies to any endpoint with auth/basic{} config", "htpasswd_path": "/path/to/.htpasswd", "@comment": "Additional users to the ones in htpasswd:", "users": { "admin": "$2y$05$HpdPmv2Z3h3skMCVaf/CEep/UUBuhZ...", "user2": "$2y$05$HpdPmv2Z3h3skMCVaf/CEep/UUBuhZ..." } } }, "endpoints": [ { "endpoint": "/user/{id}", "extra_config": { "@comment": "The empty inclusion enables basic auth for this endpoint with the config above ", "auth/basic": {} } }, { "endpoint": "/checkout", "extra_config": { "@comment": "Exceptionally an endpoint can override all settings", "auth/basic": { "htpasswd_path": "/path/to/.htpasswd_secondary", } } } ] } ``` ##### Fields of Basic Authentication \* required fields ##### [`htpasswd_path`](https://www.krakend.io/docs/enterprise/authentication/basic-authentication/#htpasswd_path "Click to copy link to htpasswd_path") string Absolute Path to the `htpasswd` filename (recommended) or relative `./` to the workdir (less secure). Example: `"/path/to/.htpasswd"` ##### [`users`](https://www.krakend.io/docs/enterprise/authentication/basic-authentication/#users "Click to copy link to users") object **Additional** users to the `htpasswd` file can be declared directly inside the configuration. The content of both places will be merged (and this list will overwrite users already defined in the htpasswd file). The key of each entry is the username, and the value the bcrypt. Example: `{"admin":"$2y$05$HpdPmv2Z3h3skMCVaf/CEep/UUBuhZ...","user2":"$2y$05$HpdPmv2Z3h3skMCVaf/CEep/UUBuhZ..."}` Properties of `users` can use a name matching the pattern `(.*)`, and their content is an `string` Schema: [https://www.krakend.io/schema/v2.13/auth/basic.json](https://www.krakend.io/schema/v2.13/auth/basic.json "JSON schema definition") ## User administration The htpasswd file is loaded only during startup, and KrakenD needs to be restarted or redeployed if you want to modify which users can access the resources. The same happens with any users declared inside the `users` array. KrakenD only works with passwords encrypted with `bcrypt`. Generate a file using the following command (notice the `-B` flag for bcrypt): Generate access file  ``` $htpasswd -Bbc .htpasswd yourUser yourPassword ``` Or generate the output in stdout: Ask for password and create hash  ``` $htpasswd -nB yourUser New password: Re-type new password: yourUser:$2y$05$s8eiQOQtfvOPB3K4vr212eZyZFdtdnKap6RfEVd479xXtXlfx7Nsq ``` A string like the one below represents the user and the password hash, which is inserted in the `.htpasswd` file or directly inside the KrakenD configuration under `users`. Generate access file  ``` $myName:$2y$05$c4WoMPo3SXsafkva.HHa6uXQZWr7oboPiC2bT/r7q1BB8I2s0BRqC ``` To delete users, edit the file and remove the desired line. ## Upgrade from plugin-based Basic Auth If you used the basic authentication plugin previous to v2.2, then you should switch to the new configuration format for the new core component that offers the following **benefits**: - Now, all rejections appear in the telemetry - You can override the configuration at the endpoint level - Easier to maintain configuration The basic auth plugin is considered deprecated, although it’s still supported for backward compatibility. ### Configuration upgrade Below you can find the same configuration for the deprecated plugin and the core component. **DEPRECATED configuration**: ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "extra_config": { "plugin/http-server": { "name": [ "basic-auth" ], "basic-auth": { "htpasswd_path": "/path/to/.htpasswd", "endpoints": [ "/user/{id}", "/checkout" ], "users": { "admin": "$2y$05$HpdPmv2Z3h3skMCVaf/CEep/UUBuhZ...", "user2": "$2y$05$HpdPmv2Z3h3skMCVaf/CEep/UUBuhZ..." } } } } } ``` **CURRENT configuration**: ```json { "version": 3, "extra_config": { "auth/basic": { "@comment": "The following configuration applies to any endpoint with auth/basic config", "htpasswd_path": "/path/to/.htpasswd", "users": { "admin": "$2y$05$HpdPmv2Z3h3skMCVaf/CEep/UUBuhZ...", "user2": "$2y$05$HpdPmv2Z3h3skMCVaf/CEep/UUBuhZ..." } } }, "endpoints": [ { "endpoint": "/user/{id}", "extra_config": { "@comment": "The simple inclusion of the component enables basic authentication for this endpoint", "auth/basic": {} } }, { "endpoint": "/checkout", "extra_config": { "auth/basic": { "@comment": "Additionally an endpoint can override any settings", "htpasswd_path": "/path/to/.htpasswd_secondary", } } } ] } ``` As you can see, your configuration in the plugin moves to a new `extra_config` section named `auth/basic`. And instead of having a list of endpoints, you declare the usage directly in the endpoint. In summary: - You don’t longer need the `plugins` entry - The global configuration moves: - From `extra_config` / `plugin/http-server`/ `basic-auth` - To `extra_config` / `auth/basic` - The `endpoints` list disappears in the global configuration - You include in each `endpoint` the `extra_config` -> `auth/basic` as an empty object - Optionally, you can override the configuration on a specific endpoint by passing new settings. --- # [JWT Overview - Authorization](https://www.krakend.io/docs/authorization/jwt-overview/) The **JSON Web Token** specification is an industry standard to represent claims securely between two parties. The **JWT** is a `base64` encoded JSON object that contains key-value pairs of attributes that are signed by a trusted authority. When JWT shields a specific set of endpoints, requests to the API gateway must provide a token. Verification of the token takes place in every request, including the check of the signature and optionally the assurance that its issuer, roles, and audience are sufficient to access the endpoint. No external access is needed other than the initial load of the JWK URL to validate tokens. Only in the case that the token is valid and passes all the checks, **the user is authorized to access the endpoint** and continue with the request. ## KrakenD JWT implementations KrakenD implements both [JWT signing](/docs/authorization/jwt-signing/) and [JWT validation](/docs/authorization/jwt-validation/) models to protect endpoints from undesired users that are not entitled to use the information, reinforcing security. - [Sign tokens](/docs/authorization/jwt-signing/) when you have no identity server yet (like a classic monolithic application with a `/login` endpoint) and let KrakenD take care of the token signing with the private key. - [Validate tokens](/docs/authorization/jwt-validation/) issued by a third party or the [JWT signing middleware](/docs/authorization/jwt-signing/), ensuring their integrity and proper claims. A stateless system like KrakenD **does not issue tokens**, this is the responsibility of your backend or identity server. ## Key concepts The **JSON Web Token** carries the information your end-users pass to the system to be recognized as legitimate users with other metadata. KrakenD uses **standard JWT tokens** to protect endpoints, using JSON Web Signature (**JWS**), to check the tokens’ digital signature integrity of the contained claims and defending against attacks using tampered tokens. A JWT token is a `base64` encoded string with the structure `header.payload.signature`. A typical request to an endpoint requiring JWT validation includes a `Bearer` in the `Authorization` header:   ``` $GET /resource HTTP/1.1 Host: krakend.example.com Authorization: Bearer eyJhbGciOiJIUzI1NiIXVCJ9.(truncated).ktIOfzak2ekD7IrCa9-UiO4QA ``` Or instead, you can send the token **inside a cookie** (see [`cookie_key`](/docs/authorization/jwt-validation/#jwt-validation-settings)). All tokens transmitted between users and KrakenD have to be signed using **JWS** to ensure they are legitimate and not forged by an attacker. JWS represents digitally signed content using JSON data structures that are base64url encoded using the format `header.payload.signature`. Finally, KrakenD needs to retrieve the keys from the trusted authority (your Identity Provider) that let the system validate the signature. These keys transmit between KrakenD and the IdP using the **JWK** format, a JSON object representing a set of cryptographic keys. Objects will use one or another algorithm depending on the system and implementation in your IdP. **JWA** represents the set of algorithms you can use to sign your tokens. The introduction above is very superficial; the recommended read is the RFC: - **JWT**: [Definition of tokens: structure and composition of header and payload](https://tools.ietf.org/html/rfc7519) - **JWS** [Signature](https://tools.ietf.org/html/rfc7515) - **JWK** [Key transmission](https://tools.ietf.org/html/rfc7517) - **JWA** [Definition of cyphering and signing algorithms](https://tools.ietf.org/html/rfc7518) - **JWE** is not supported by KrakenD (Premise: Sensitive data should not be transmitted using tokens). New to JWT? If you are not familiar with JWT yet, read the “[Introduction to JSON Web Tokens](https://jwt.io/introduction/)” --- # [JWT Validation](https://www.krakend.io/docs/authorization/jwt-validation/) **Protect endpoints from public usage** by validating JWT tokens generated by any industry-standard **OpenID Connect (OIDC)** integration. Before digging any further, here are some answers to frequently asked questions: 1. **KrakenD does not generate the tokens itself**. Still, you can plug it into any SaaS or self-hosted OpenID Identity Provider (**IdP**) using industry standards (e.g., Auth0, Azure AD, Descope, Google Identity Platform/Firebase, Keycloak, etc.) 2. **KrakenD does not need to validate all calls using your IdP**. KrakenD validates every incoming call’s signature and **it doesn’t make token introspection** (asking for the IdP data about the token owner). 3. **If you don’t have an identity server**, you can still use your classic monolith/backend login system and adapt it to return a JWT payload (a simple JSON). From here, let KrakenD [sign the token for you](/docs/authorization/jwt-signing/) and start using tokens immediately. 4. **Your self-hosted identity server doesn’t need to be exposed to the Internet**, as it can live behind KrakenD and let the token generation requests be proxied through KrakenD. 5. **I have tokens generated by the provider XYZ, are they supported?** Yes. We still need to find a single major provider that doesn’t follow the JSON Web Encryption (RFC 7516), JSON Web Signature (RFC 7515), or JSON Web Token (RFC 7519) specifications. 6. **If you are new to JWT validation**, start reading the [JSON Web Tokens overview](/docs/authorization/jwt-overview/) ## JWK URL requirements Whether you decide to load the public key that guarantees that a token is properly signed from a remote URL (`jwk_url`) or locally (`jwk_local_path`), you must use the **JWK Set Format** as described in the [RFC-7517](https://datatracker.ietf.org/doc/html/rfc7517#section-5). This means that your JWK file **must contain a `keys` field** with the array of keys you want to validate. ## JWT header requirements Make sure the token header has `kid` and `alg` When KrakenD decodes the `base64` token string passed in the `Bearer` or a cookie, it expects to find in its **header section** (NOT THE PAYLOAD!) the `alg` and `kid`. You can paste your token into the [JWT debugger](https://jwt.io/#debugger-io) and make sure these fields are under “header”. The value provided in the `kid` must match with the `kid` declared at the `jwk_url` or `jwk_local_path`. Here’s an example of the **header section** of a decoded token: ```json { "alg": "RS256", "kid": "MDNGMjU2M0U3RERFQUEwOUUzQUMwQ0NBN0Y1RUY0OEIxNTRDM0IxMw" } ``` The `alg` and `kid` values depend on your implementation, but they must be present. The example above used [this public key](https://albert-test.auth0.com/.well-known/jwks.json). Notice how the `kid` matches the single key present in the JWK document and the token header. **KrakenD is built with security in mind** and uses **JWS** (instead of plain JWT or JWE), and the `kid` points to the right key in the JWS. This is why this entry is mandatory to validate your tokens. ## Basic JWT validation The JWT validation must be present inside every endpoint definition needing it. If several endpoints are going to require JWT validation, consider using the [flexible configuration](/docs/configuration/flexible-config/) to avoid repetitive declarations. Enable the JWT validation by adding the namespace `"auth/validator"` inside the `extra_config` of the desired `endpoint`. Do not forget to add caching. For instance, to protect the endpoint `/protected/resource`: ```JSON { "endpoint": "/protected/resource", "extra_config": { "auth/validator": { "alg": "RS256", "audience": ["http://api.example.com"], "roles_key": "http://api.example.com/custom/roles", "roles": ["user", "admin"], "jwk_url": "https://albert-test.auth0.com/.well-known/jwks.json", "cache": true } }, "backend": [ { "url_pattern": "/" } ] } ``` This configuration makes sure that: - The signature of the token matches with the keys under the JWK URL - The key from the identity server is cached to avoid hammering it - The token is well-formed and didn’t expire - The token has a valid signature - The role of the user is either `user` or `admin` (taken from a key in the JWT payload named `http://api.example.com/custom/roles`) - The token is not revoked in the bloom filter (see [revoking tokens](/docs/authorization/revoking-tokens/)) ## JWT validation settings The following settings are available for JWT validation. There are many options, although generally only the **fields `alg` and `jwk_url` or `jwk_local_path` are mandatory**, and the rest of the keys can be added or not at your best convenience or depending on other options. These options are for the `extra_config`’s namespace `"auth/validator"` placed in every endpoint (use [flexible configuration](/docs/configuration/flexible-config/) to avoid code repetition): ##### Fields of JWT validator \* required fields Minimum configuration needs **one of**: `alg` + `jwk_local_path` , or `alg` + `jwk_url` ##### [`alg`](https://www.krakend.io/docs/authorization/jwt-validation/#alg "Click to copy link to alg") The hashing algorithm used by the token issuer. Possible values are: `"EdDSA"` , `"HS256"` , `"HS384"` , `"HS512"` , `"RS256"` , `"RS384"` , `"RS512"` , `"ES256"` , `"ES384"` , `"ES512"` , `"PS256"` , `"PS384"` , `"PS512"` Defaults to `"RS256"` ##### [`audience`](https://www.krakend.io/docs/authorization/jwt-validation/#audience "Click to copy link to audience") array of strings Reject tokens that do not contain ALL audiences declared in the list. Example: `["audience1"]` ##### [`auth_header_name`](https://www.krakend.io/docs/authorization/jwt-validation/#auth_header_name "Click to copy link to auth_header_name") string Allows to parse the token from a custom header. Example: `"X-Custom-Auth"` Defaults to `"Authorization"` ##### [`cache`](https://www.krakend.io/docs/authorization/jwt-validation/#cache "Click to copy link to cache") boolean Set this value to `true` (recommended) to stop downloading keys on every request and store them in memory for the next `cache_duration` period and avoid hammering the key server, as recommended for performance. Do not use this flag when using `jwk_local_ca`. Defaults to `false` ##### [`cache_duration`](https://www.krakend.io/docs/authorization/jwt-validation/#cache_duration "Click to copy link to cache_duration") integer The cache duration in seconds when the `cache` is enabled. 15 minutes when unset. Defaults to `900` ##### [`cipher_suites`](https://www.krakend.io/docs/authorization/jwt-validation/#cipher_suites "Click to copy link to cipher_suites") array Override the default cipher suites. Use it if you want to enforce an even higher security standard. Defaults to `[49199,49195,49200,49196,52392,52393]` ##### [`cookie_key`](https://www.krakend.io/docs/authorization/jwt-validation/#cookie_key "Click to copy link to cookie_key") string Add the key name of the cookie containing the token when it is not passed in the headers Example: `"cookie_jwt"` ##### [`cypher_key`](https://www.krakend.io/docs/authorization/jwt-validation/#cypher_key "Click to copy link to cypher_key") string The cyphering key. ##### [`disable_jwk_security`](https://www.krakend.io/docs/authorization/jwt-validation/#disable_jwk_security "Click to copy link to disable_jwk_security") boolean When true, disables security of the JWK client and allows insecure connections (plain HTTP) to download the keys. Useful for development environments. Defaults to `false` ##### [`failed_jwk_key_cooldown`](https://www.krakend.io/docs/authorization/jwt-validation/#failed_jwk_key_cooldown "Click to copy link to failed_jwk_key_cooldown") string When a request comes with a token declaring an unknown `kid` (or the key strategy you choose), and the JWK is in a remote destination, KrakenD downloads the JWK from the Identity Provider for its recognition. Suppose there is a network failure, or the key is not in the list (e.g., you rotated the keys without anticipation). In that case, you can tell the gateway not to contact the Identity Provider again during the time specified here. We recommend setting this value, even with a low time (e.g., `10s`), to prevent misconfigurations from hammering your Identity Providers. Any values under one second are ignored. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Examples: `"1m"` , `"10s"` , `"1h"` ##### [`issuer`](https://www.krakend.io/docs/authorization/jwt-validation/#issuer "Click to copy link to issuer") string When set, tokens not matching the issuer are rejected. Example: `"issuer"` ##### [`jwk_fingerprints`](https://www.krakend.io/docs/authorization/jwt-validation/#jwk_fingerprints "Click to copy link to jwk_fingerprints") array of strings A list of fingerprints (the certificate’s unique identifier) for certificate pinning and avoid man-in-the-middle attacks. Add fingerprints in base64 format. ##### [`jwk_local_ca`](https://www.krakend.io/docs/authorization/jwt-validation/#jwk_local_ca "Click to copy link to jwk_local_ca") string Path to the CA’s certificate verifying a secure connection when downloading the JWK. Use when not recognized by the system (e.g., self-signed certificates). ##### [`jwk_local_path`](https://www.krakend.io/docs/authorization/jwt-validation/#jwk_local_path "Click to copy link to jwk_local_path") string Local path to the JWK public keys, has preference over `jwk_url`. Instead of pointing to an external URL (with `jwk_url`), public keys are kept locally, in a plain JWK file (security alert!), or encrypted. When encrypted, also add `secret_url` and `cypher_key`. Example: `"./jwk.txt"` ##### [`jwk_url`](https://www.krakend.io/docs/authorization/jwt-validation/#jwk_url "Click to copy link to jwk_url") string The URL to the JWK endpoint with the public keys used to verify the token’s authenticity and integrity. Use with `cache` to avoid re-downloading the key on every request. Consider enabling [shared caching](https://www.krakend.io/docs/authorization/jwk-caching/) too. The identity server will receive an HTTP(s) request from KrakenD with a KrakenD user agent, and the identity server must reply with a JSON object and a content-type `application/jwk-set+json` or `application/json`. Examples: `"https://some-domain.auth0.com/.well-known/jwks.json"` , `"http://KEYCLOAK:8080/auth/realms/master/protocol/openid-connect/certs"` , `"https://yourOktaBaseUrl/v1/keys"` ##### [`key_identify_strategy`](https://www.krakend.io/docs/authorization/jwt-validation/#key_identify_strategy "Click to copy link to key_identify_strategy") Allows strategies other than `kid` to load keys. Possible values are: `"kid"` , `"x5t"` , `"x5t#S256"` , `"kid_x5t"` ##### [`leeway`](https://www.krakend.io/docs/authorization/jwt-validation/#leeway "Click to copy link to leeway") string A margin of time where you will accept an already expired token. You should not accept expired tokens other than enabling two environments that are not perfectly synchronized and have minor clock drifts to accept each other differences. Any value specified here will be rounded to seconds, with a minimum of one second. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Examples: `"1m"` , `"1s"` Defaults to `"1s"` ##### [`operation_debug`](https://www.krakend.io/docs/authorization/jwt-validation/#operation_debug "Click to copy link to operation_debug") boolean When `true`, any JWT **validation operation** gets printed in the log with a level `ERROR`. You will see if a client does not have sufficient roles, the allowed claims, scopes, and other useful information. Defaults to `false` ##### [`propagate_claims`](https://www.krakend.io/docs/authorization/jwt-validation/#propagate_claims "Click to copy link to propagate_claims") array of arrays Enables passing claims in the backend’s request header. You can pass nested claims using the dot `.` operator. E.g.: `realm_access.roles`. ##### [`propagate_claims_preserve_array`](https://www.krakend.io/docs/authorization/jwt-validation/#propagate_claims_preserve_array "Click to copy link to propagate_claims_preserve_array") boolean When set to `true`, the JWT claims that are propagated to the backend will preserve their array structure as multi-value headers, if applies. If set to `false`, arrays will be converted to comma-separated strings. Defaults to `false` ##### [`roles`](https://www.krakend.io/docs/authorization/jwt-validation/#roles "Click to copy link to roles") array of strings When set, the JWT token not having at least one of the listed roles is rejected. ##### [`roles_key`](https://www.krakend.io/docs/authorization/jwt-validation/#roles_key "Click to copy link to roles_key") string When validating users through roles, provide the key name inside the JWT payload that lists their roles. If this key is nested inside another object, add `roles_key_is_nested` and use the dot notation `.` to traverse each level. E.g.: `resource_access.myclient.roles` represents the payload `{resource_access: { myclient: { roles: ["myrole"] } }`. Notice that the roles object you choose is a list, not a map. Example: `"resource_access.myclient.roles"` ##### [`roles_key_is_nested`](https://www.krakend.io/docs/authorization/jwt-validation/#roles_key_is_nested "Click to copy link to roles_key_is_nested") boolean If the roles key uses a nested object using the `.` dot notation, you must set it to `true` to traverse the object. ##### [`scopes`](https://www.krakend.io/docs/authorization/jwt-validation/#scopes "Click to copy link to scopes") array of strings A list of scopes to validate. The token, after decoding it, can have the scopes declared as a space-separated list, e.g.: `"my_scopes": "resource1:action1 resource3:action7"` or inside a list, e.g.: `"my_scopes": ["resource1:action1","resource3:action7"]`. ##### [`scopes_key`](https://www.krakend.io/docs/authorization/jwt-validation/#scopes_key "Click to copy link to scopes_key") string The key name where KrakenD can find the scopes. The key can be a nested object using the `.` dot notation, e.g.: `data.access.my_scopes`. ##### [`scopes_matcher`](https://www.krakend.io/docs/authorization/jwt-validation/#scopes_matcher "Click to copy link to scopes_matcher") Defines if the user needs to have in its token at least one of the listed claims (`any`), or `all` of them. Possible values are: `"any"` , `"all"` Defaults to `"any"` ##### [`secret_url`](https://www.krakend.io/docs/authorization/jwt-validation/#secret_url "Click to copy link to secret_url") string An URL with a custom scheme using one of the supported providers (e.g.: `awskms://keyID`) (see providers). Examples: `"base64key://smGbjm71Nxd1Ig5FS0wj9SlbzAIrnolCz9bQQ6uAhl4="` , `"awskms://keyID"` , `"azurekeyvault://keyID"` , `"gcpkms://projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEY_RING]/cryptoKeys/[KEY]"` , `"hashivault://keyID"` Schema: [https://www.krakend.io/schema/v2.13/auth/validator.json](https://www.krakend.io/schema/v2.13/auth/validator.json "JSON schema definition") Here there is an example using an external `jwk_url`: ```json { "endpoint": "/foo", "extra_config": { "auth/validator": { "alg": "RS256", "jwk_url": "https://url/to/jwks.json", "cache": true, "audience": [ "audience1" ], "roles_key": "department", "roles_key_is_nested": false, "roles": [ "sales", "development" ], "scopes_key": "my_scopes", "scopes_matcher": "any", "scopes": [ "resource1:action1", "resource2:action1", "resource1:action2" ], "issuer": "http://my.api.com", "cookie_key": "TOKEN", "disable_jwk_security": true, "jwk_fingerprints": [ "S3Jha2VuRCBpcyB0aGUgYmVzdCBnYXRld2F5LCBhbmQgeW91IGtub3cgaXQ==" ], "cipher_suites": [ 10, 47, 53 ], "operation_debug": true } } } ``` Performance considerations If you use cryptographic algorithms that require high computation such as `RS512`, make sure your KrakenD instances have a proper CPU setting. Additionally, enable `cache` to avoid hammering your identity servers and save internal network traffic. ### Caching keys A crucial decision when using JWT is [defining a caching strategy](/docs/authorization/jwk-caching/) that works with your **key rotation** policy. If you don’t set any caching option, KrakenD will default to the **more pessimistic scenario** where you rotate the keys continuously, and KrakenD needs to download the keys in the JWK URL every single time. In most cases, this option will **add a lot of pressure to your identity server**. Therefore, we encourage you to enable `cache` in each endpoint, and even better if you add the second-level shared cache in the JWK client with `shared_cache_duration`. For instance: ```json { "version": 3, "extra_config": { "auth/validator": { "@comment": "Enable a JWK shared cache amongst all endpoints of 15 minutes", "shared_cache_duration": 900 } }, "endpoints": [{ "endpoint": "/protected", "extra_config": { "auth/validator": { "cache": true, "cache_duration": 3600, "@comment": "Rest of the validator options omitted for simplicity" } } }] } ``` [Read more about JWK caching](/docs/authorization/jwk-caching/) ### Validation process KrakenD does the following validation to let users hit protected endpoints: - The `jwk_url` must be accessible by KrakenD at all times ([caching is recommended](/docs/authorization/jwk-caching/)) - The token is [well formed](https://jwt.io/#debugger-io) - The `kid` in the **token header** (not payload) is listed in the `jwk_url` or `jwk_local_path` under `keys`. - The content of the JWK Keys (`k`) is **base64** urlencoded - The algorithm `alg` is supported by KrakenD and matches exactly the one used in the endpoint definition. - The token hasn’t expired - The signature is valid. - The given `issuer` matches (if present in the configuration), and is a *string* - The given `audience` matches (if present in the configuration), and is a *string* - The given claims are within the endpoint accepted `roles` (if present in the configuration)) - All numeric claims checking is represented as a float64 (decimal). The configuration allows you to define the set of required roles. For example, a user who passes a token with roles `A` and `B`, can access an endpoint requiring `"roles": ["A","C"]` as it has one of the required options (`A`). If the token is expired, the configuration inside the namespace is incorrect, the signature doesn’t match, the required claims do not match, or the token is revoked, a `401 Unauthorized` is returned. When the token doesn’t include the defined ACL’s required roles, a `403 Forbidden` is returned. When you generate tokens for end-users, make sure to set a **low expiration**. Tokens are supposed to have short lives and should expire in a few minutes or hours. ### Accepted providers for encrypting payloads When using a `jwk_local_path`, the `secret_url` scheme accepts different providers: #### Local secrets The local secrets require an URL with the following scheme: ``` base64key://base64UrlEncondedContent ``` The URL host must be `base64 url encoded` and must decode to exactly 32 bytes. Here is an example of the `extra_config`: ```json { "jwk_local_path":"./jwk.txt", "secret_url":"base64key://smGbjm71Nxd1Ig5FS0wj9SlbzAIrnolCz9bQQ6uAhl4=", "cypher_key":"gCERmfqHMoEu3+utqBa/R1oMZYIvh0OOKtJmnX/hDPDxbXCGXGvO3SF7B5FWxrJnRW7rnjGIV4eP2VLrYX2q9pJM49BpP+A9" } ``` This config will use the key `smGbjm71Nxd1Ig5FS0wj9SlbzAIrnolCz9bQQ6uAhl4=` for decrypting de `cypher_key` (base64 standard enconded) and then decrypting the content of the file `./jwt.txt`. See this test to [understand how to generate and encrypt payloads](https://github.com/krakend/krakend-jose/blob/master/jwk_test.go). #### Amazon KMS ``` awskms://keyID ``` The URL Host + Path is used as the key ID, which can be an Amazon Resource Name (ARN), alias name, or alias ARN. Note that ARNs may contain “:” characters, which cannot be escaped in the Host part of a URL, so you should use the `awskms:///` form. [More information about AWS KMS](https://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html#find-cmk-id-arn) #### Azure’s Key Vault ``` azurekeyvault://keyID ``` The credentials are taken from the environment unless the `AZURE_KEYVAULT_AUTH_VIA_CLI` environment variable is set to true, in which case it uses the `az` command line. [More information about Azure Key Vault](https://docs.microsoft.com/en-us/azure/key-vault/general/basic-concepts) #### Google Cloud KMS ``` gcpkms://projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEY_RING]/cryptoKeys/[KEY] ``` You can take the URL [from the GCP console](https://cloud.google.com/kms/docs/object-hierarchy#key). #### Hashicorp’s Vault ``` hashivault://keyID ``` Environment variables `VAULT_SERVER_URL` and `VAULT_SERVER_TOKEN` are used. ## Passing claims to the backend URL Since KrakenD 1.2.0, it is possible to use data present in the claims to inject it into the backend’s final URL. The notation of the `url_pattern` field includes the parsing of `{JWT.some_claim}`, where `some_claim` is an attribute of your claim. For instance, when your JWT payload is represented by something like this: ```json { "sub": "1234567890", "name": "Mr. KrakenD" } ``` Having a `backend` defined with: ```json { "url_pattern": "/foo/{JWT.sub}", "method": "POST" } ``` The call to your backend would produce the request: ```nginx POST /foo/1234567890 ``` Keep in mind that this syntax in the `url_pattern` field is only available if the backend loads the extra\_config `"auth/validator"` and that **it does not work with nested attributes** in the payload. If KrakenD can’t replace the claim’s content for any reason, the backend receives a request to the literal URL `/foo/{JWT.sub}`. ## Propagate JWT claims as request headers It is possible to forward claims in a JWT as request headers. It is a common use case to have, for instance, the sub claim added as an `X-User` header to the request. The propagation makes that other KrakenD components, such as rate-limiting, can work with information in the token. **Important:** The endpoint `input_headers` needs to be set as well, so the backend can see it. ```json { "endpoint": "/foo", "input_headers": ["X-User", "X-Role"], "extra_config": { "auth/validator": { "propagate_claims": [ ["sub", "X-User"], ["realm_access.role", "X-Role"] ] } } } ``` In this case, the `sub` claim’s value will be added as `x-user` header to the request. If the claim does not exist, the mapping is just skipped. In addition, the nested property `role` (inside `realm_access`) is passed as an `x-role` header. ## A complete running example The [KrakenD Playground](/docs/overview/playground/) demonstrates how to protect endpoints using JWT and includes two examples ready to use: - Integration with an external third party using a [Single Page Application from Auth0](https://auth0.com/docs/applications/spa/) - Integration with an internal identity provider service (mocked) using a symmetric key algorithm and a signer middleware. To try it, [clone the playground](https://github.com/krakend/playground-community) and follow the README. ## Supported hashing algorithms and cipher suites ### Hashing algorithms Accepted values for the `alg` field are: - `EdDSA`: EdDSA - `HS256`: HMAC using SHA-256 - `HS384`: HMAC using SHA-384 - `HS512`: HMAC using SHA-512 - `RS256`: RSASSA-PKCS-v1.5 using SHA-256 - `RS384`: RSASSA-PKCS-v1.5 using SHA-384 - `RS512`: RSASSA-PKCS-v1.5 using SHA-512 - `ES256`: ECDSA using P-256 and SHA-256 - `ES384`: ECDSA using P-384 and SHA-384 - `ES512`: ECDSA using P-521 and SHA-512 - `PS256`: RSASSA-PSS using SHA256 and MGF1-SHA256 - `PS384`: RSASSA-PSS using SHA384 and MGF1-SHA384 - `PS512`: RSASSA-PSS using SHA512 and MGF1-SHA512 ### Cipher suites Accepted values for cipher suites are: - `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 - `60`: TLS\_RSA\_WITH\_AES\_128\_CBC\_SHA256 - `156`: TLS\_RSA\_WITH\_AES\_128\_GCM\_SHA256 - `157`: TLS\_RSA\_WITH\_AES\_256\_GCM\_SHA384 - `49159`: TLS\_ECDHE\_ECDSA\_WITH\_RC4\_128\_SHA - `49161`: TLS\_ECDHE\_ECDSA\_WITH\_AES\_128\_CBC\_SHA - `49162`: TLS\_ECDHE\_ECDSA\_WITH\_AES\_256\_CBC\_SHA - `49169`: TLS\_ECDHE\_RSA\_WITH\_RC4\_128\_SHA - `49170`: TLS\_ECDHE\_RSA\_WITH\_3DES\_EDE\_CBC\_SHA - `49171`: TLS\_ECDHE\_RSA\_WITH\_AES\_128\_CBC\_SHA - `49172`: TLS\_ECDHE\_RSA\_WITH\_AES\_256\_CBC\_SHA - `49187`: TLS\_ECDHE\_ECDSA\_WITH\_AES\_128\_CBC\_SHA256 - `49191`: TLS\_ECDHE\_RSA\_WITH\_AES\_128\_CBC\_SHA256 **Default suites** are: - `49199`: TLS\_ECDHE\_RSA\_WITH\_AES\_128\_GCM\_SHA256 - `49195`: TLS\_ECDHE\_ECDSA\_WITH\_AES\_128\_GCM\_SHA256 - `49200`: TLS\_ECDHE\_RSA\_WITH\_AES\_256\_GCM\_SHA384 - `49196`: TLS\_ECDHE\_ECDSA\_WITH\_AES\_256\_GCM\_SHA384 - `52392`: TLS\_ECDHE\_RSA\_WITH\_CHACHA20\_POLY1305 - `52393`: TLS\_ECDHE\_ECDSA\_WITH\_CHACHA20\_POLY1305 --- # [Shared JWK Caching for Authorization](https://www.krakend.io/docs/authorization/jwk-caching/) The [JWT validation](/docs/authorization/jwt-validation/) and [JWT signing](/docs/authorization/jwt-signing/) components do not apply cache by default. Validating tokens in a high-throughput scenario can be a consuming operation. To refrain the gateway from downloading on each request the signing keys, you can enable caching. It’s usually a bad idea to not cache the content of the JWK URL as your identity provider would receive a huge amount of traffic. Caching does not apply to `jwk_local_path` When instead of using `jwk_url` you have the keys on disk and you use `jwk_local_path`, then you don’t need to set any cache at all. ## What happens when you don’t use caching If, for instance, you have 1000 endpoints in your configuration, when KrakenD starts, your identity server(s) will receive an initial blast of 1000 connections requesting their corresponding JWK URL for the first time. In addition, each request to a secure endpoint will generate another hit to your identity server. An identity server has another function and is not designed to support high pressure like an API gateway. Plus, as you can see, there is no point in stressing it to retrieve the same content repeatedly. Therefore, you should limit the interaction between the gateway and the identity provider by implementing one or two possible caching layers. ## Enabling JWK URL caching There are **two levels** of cache you can apply designed to support together the most extreme conditions: - Per-endpoint cache (`cache` on the `auth/validator` in the endpoint) - Shared JWK cache between all endpoints ( `shared_cache_duration` on the `auth/validator` at the service level) We encourage you to configure at least a per-endpoint caching, but adding a client cache will offer you even more control over the traffic you send to the identity provider(s). The caching works for the `jwk_url`, an HTTP-based location that contains the signing keys and allows KrakenD to determine whether a token is legitimate. Therefore, when the content of this URL changes (you are doing **key rotation**), the gateway needs to use a TTL that is compatible. ### Per-endpoint JWK cache Whenever an endpoint requires JWT validation, you should always enable its `cache` and `cache_duration` properties. Otherwise, **every request to the endpoint creates a request to the identity server**. You can configure per-endpoint cache using the following options in the `auth/validator`: ##### Fields of `"auth/validator"` \* required fields ##### [`cache`](https://www.krakend.io/docs/authorization/jwk-caching/#cache "Click to copy link to cache") boolean Set this value to `true` (recommended) to stop downloading keys on every request and store them in memory for the next `cache_duration` period and avoid hammering the key server, as recommended for performance. Do not use this flag when using `jwk_local_ca`. Defaults to `false` ##### [`cache_duration`](https://www.krakend.io/docs/authorization/jwk-caching/#cache_duration "Click to copy link to cache_duration") integer The cache duration in seconds when the `cache` is enabled. 15 minutes when unset. Defaults to `900` Schema: [https://www.krakend.io/schema/v2.13/auth/validator.json](https://www.krakend.io/schema/v2.13/auth/validator.json "JSON schema definition") If you want that several endpoints consuming the same `jwk_url` share a unique cache entry, then you must enable a per-service cache as explained below. ### Shared JWK cache If you have a lot of endpoints, each of them will need to retrieve the JWK URL to cache it. You can add a shared JWK cache where one endpoint call will enable the cache for another one using the same origin URL. This option is not configured in the endpoint, but at the service level. The shared cache requires the following configuration, plus **having `cache` in all desired endpoints**: ##### Fields of Shared JWK cache \* required fields ##### [`shared_cache_duration`](https://www.krakend.io/docs/authorization/jwk-caching/#shared_cache_duration "Click to copy link to shared_cache_duration") * integer The cache duration in seconds for the JWK client retrieving the `jwk_url`. The endpoint must enable the `cache` option in order to use this second level cache. Schema: [https://www.krakend.io/schema/v2.13/auth/jose.json](https://www.krakend.io/schema/v2.13/auth/jose.json "JSON schema definition") The `cache` flag is still required **Important**: Setting the flag alone does not work unless you add at least one endpoint with the `cache` flag set to `true`. Example: ```json { "version": 3, "extra_config": { "auth/validator": { "@comment": "Enable a JWK shared cache amongst all endpoints of 15 minutes", "shared_cache_duration": 900 } }, "endpoints": [ { "endpoint": "/protected-1", "extra_config": { "auth/validator": { "cache": true, "cache_duration": 3600, "jwk_url": "https://your-id-provider", "@comment": "Rest of the validator options omitted for simplicity" } } } ] } ``` ### Cache recommendations **Combining the two levels of cache is usually the ideal scenario**. On one side, each endpoint has a scoped local cache entry and is the perfect strategy to handle contention. On the other side, adding the JWK client cache makes that when each endpoint cache expires, can still rely on a more global level of cache which allows you to control the exact time between requests to your identity server. You might thing that one global cache level would be enough, but having this granularity makes the system way more efficient, performant, and error-free. In all, you would have a configuration like this: ```json { "version": 3, "extra_config": { "auth/validator": { "@comment": "Enable a JWK shared cache amongst all endpoints of 15 minutes", "shared_cache_duration": 900 } }, "endpoints": [ { "endpoint": "/protected-1", "extra_config": { "auth/validator": { "cache": true, "cache_duration": 3600, "jwk_url": "https://your-id-provider", "@comment": "Rest of the validator options omitted for simplicity" } } }, { "endpoint": "/protected-2", "extra_config": { "auth/validator": { "cache": true, "cache_duration": 3600, "jwk_url": "https://your-id-provider", "@comment": "Rest of the validator options omitted for simplicity" } } } ] } ``` In the example above, each endpoint will cache the JWK URL for 1 hour, but when requesting again upon expiration, the cache client will return its cached content at a pace of 1 request every 15 minutes (900 seconds). --- # [JWT Signing](https://www.krakend.io/docs/authorization/jwt-signing/) The JWT signing component creates a **wrapper for your existing login endpoint** that signs with your secret key the selected fields of the backend payload right before returning the content to the end-user. The primary usage for this component is in **migrations from monolith to microservices**, or in ecosystems where there is no Identity/OAuth server yet, as it allows the immediate adoption of signed JSON Web Tokens without the need to implement a new service. ## How does it work KrakenD relies in your existing login functionality and does all the heavy-lifting of the cryptography so you can focus on validating the user and password. Your backend needs to implement a **login endpoint** that after validating the username and password **it returns a JSON reponse**, and optionally another endpoint for refreshing tokens. For example: ```json { "access_token": { "aud": "http://api.example.com", "iss": "https://krakend.io", "sub": "1234567890qwertyuio", "jti": "mnb23vcsrt756yuiomnbvcx98ertyuiop", "roles": ["role_a", "role_b"], "exp": 1735689600 }, "refresh_token": { "aud": "http://api.example.com", "iss": "https://krakend.io", "sub": "1234567890qwertyuio", "jti": "mnb23vcsrt756yuiomn12876bvcx98ertyuiop", "exp": 1735689600 }, "exp": 1735689600 } ``` The response payload has the [structure of a JWT token](https://www.rfc-editor.org/rfc/rfc7519#section-4.1) which contains fields like the `sub`ject (a.k.a id\_user), the `jti` (a *uniqid* for instance), or the `exp`iration of the token to name a few. When KrakenD receives this JSON payload, it signs the selected group of claims with your secret key. The secret key can be kept in the gateway or URL-downloaded from a trusted machine that you own. With the token signing, you are in control of the private key, and you don’t need to trust an external service to keep it for you. ### Example For instance, your backend could have an endpoint like `/token-issuer` that when receives the right combination of username and password via `POST` can identify the user and, instead of setting the session, returns an output like this: Example  ``` $curl -X POST --data '{"user":"john","pass":"doe"}' https://your-backend/token-issuer { "access_token": { "aud": "https://your.krakend.io", "iss": "https://your-backend", "sub": "1234567890qwertyuio", "jti": "mnb23vcsrt756yuiomnbvcx98ertyuiop", "roles": ["role_a", "role_b"], "exp": 1735689600 }, "refresh_token": { "aud": "https://your.krakend.io", "iss": "https://your-backend", "sub": "1234567890qwertyuio", "jti": "mnb23vcsrt756yuiomn12876bvcx98ertyuiop", "exp": 1735689600 }, "exp": 1735689600 } ``` Besides these example keys, the payload can contain any other elements you might need. If you come from a classic login system, based on cookie sessions, you’ll realize that adapting your `/login` to this output is straightforward. See [how to generate a token](/docs/authorization/jwt-signing/#how-to-generate-a-jwt-token) at the end of the document for more details. ## JWT signing settings The following settings are available to sign JWT: ```json { "endpoints": [ { "endpoint": "/token", "method": "POST", "extra_config": { "auth/signer": { "alg": "HS256", "jwk_url": "http://your-backend/jwk/symmetric.json", "keys_to_sign": [ "access_token", "refresh_token" ], "kid": "sim2", "cipher_suites": [ 5, 10 ], "jwk_fingerprints": [ "S3Jha2VuRCBpcyB0aGUgYmVzdCBnYXRld2F5LCBhbmQgeW91IGtub3cgaXQ==" ], "full": false, "disable_jwk_security": false } } } ] } ``` The example above contains every single option available, but you don’t need them all. See them explained below: ##### Fields of JWT signer \* required fields Minimum configuration needs **one of**: `alg` + `jwk_local_path` + `disable_jwk_security` , or `alg` + `jwk_url` ##### [`alg`](https://www.krakend.io/docs/authorization/jwt-signing/#alg "Click to copy link to alg") * The hashing algorithm used by the issuer. Usually `RS256`. The algorithm you choose directly affects the CPU consumption. Possible values are: `"EdDSA"` , `"HS256"` , `"HS384"` , `"HS512"` , `"RS256"` , `"RS384"` , `"RS512"` , `"ES256"` , `"ES384"` , `"ES512"` , `"PS256"` , `"PS384"` , `"PS512"` ##### [`cipher_suites`](https://www.krakend.io/docs/authorization/jwt-signing/#cipher_suites "Click to copy link to cipher_suites") array Override the default cipher suites (see [JWT validation](https://www.krakend.io/docs/authorization/jwt-signing/)). Unless you have a legacy JWK, **you don’t need to set this value**. Defaults to `[49199,49195,49200,49196,52392,52393]` ##### [`cypher_key`](https://www.krakend.io/docs/authorization/jwt-signing/#cypher_key "Click to copy link to cypher_key") string The cyphering key. ##### [`disable_jwk_security`](https://www.krakend.io/docs/authorization/jwt-signing/#disable_jwk_security "Click to copy link to disable_jwk_security") boolean Disables HTTP security of the JWK client and allows insecure connections (plain HTTP) to download the keys. The flag should be `false` when you use HTTPS, and `true` when using plain HTTP or loading the key from a local file. Defaults to `false` ##### [`full`](https://www.krakend.io/docs/authorization/jwt-signing/#full "Click to copy link to full") boolean Use JSON format instead of the compact form JWT provides. Defaults to `false` ##### [`jwk_fingerprints`](https://www.krakend.io/docs/authorization/jwt-signing/#jwk_fingerprints "Click to copy link to jwk_fingerprints") array of strings A list of fingerprints (the unique identifier of the certificate) for certificate pinning and avoid man in the middle attacks. Add fingerprints in base64 format. ##### [`jwk_local_ca`](https://www.krakend.io/docs/authorization/jwt-signing/#jwk_local_ca "Click to copy link to jwk_local_ca") string Path to the CA’s certificate verifying a secure connection when downloading the JWK. Use when not recognized by the system (e.g., self-signed certificates). ##### [`jwk_local_path`](https://www.krakend.io/docs/authorization/jwt-signing/#jwk_local_path "Click to copy link to jwk_local_path") string Local path to the JWK public keys, has preference over `jwk_url`. Instead of pointing to an external URL (with `jwk_url`), public keys are kept locally, in a plain JWK file (security alert!), or encrypted. When encrypted, also add `secret_url` and `cypher_key`. Example: `"./jwk.txt"` ##### [`jwk_url`](https://www.krakend.io/docs/authorization/jwt-signing/#jwk_url "Click to copy link to jwk_url") string The URL to the JWK endpoint with the private keys used to sign the token. Example: `"http://your-backend/jwk/symmetric.json"` ##### [`keys_to_sign`](https://www.krakend.io/docs/authorization/jwt-signing/#keys_to_sign "Click to copy link to keys_to_sign") * array of strings List of all the specific keys that need signing (e.g., `refresh_token` and `access_token`). Example: `["access_token","refresh_token"]` ##### [`kid`](https://www.krakend.io/docs/authorization/jwt-signing/#kid "Click to copy link to kid") * string The key ID purpose is to match a specific key, as the jwk\_url might contain several keys. Example: `"sim2"` ##### [`leeway`](https://www.krakend.io/docs/authorization/jwt-signing/#leeway "Click to copy link to leeway") string A margin of extra time where you will still accept the token after its expiration date. You should not accept expired tokens other than enabling two environments that are not perfectly synchronized and have minor clock drifts to accept each other differences. Any value specified here will be rounded to seconds, with a minimum of one second. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Examples: `"1m"` , `"1s"` Defaults to `"1s"` ##### [`secret_url`](https://www.krakend.io/docs/authorization/jwt-signing/#secret_url "Click to copy link to secret_url") string An URL with a custom scheme using one of the supported providers (e.g.: `awskms://keyID`) ([see providers](https://www.krakend.io/docs/authorization/jwt-validation/#accepted-providers-for-encrypting-payloads)). Examples: `"base64key://smGbjm71Nxd1Ig5FS0wj9SlbzAIrnolCz9bQQ6uAhl4="` , `"awskms://keyID"` , `"azurekeyvault://keyID"` , `"gcpkms://projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEY_RING]/cryptoKeys/[KEY]"` , `"hashivault://keyID"` Schema: [https://www.krakend.io/schema/v2.13/auth/signer.json](https://www.krakend.io/schema/v2.13/auth/signer.json "JSON schema definition") ## Basic JWT signing Your backend application knows how to issue tokens now, so the gateway can sign them before passing to the user. To achieve that, instead of publishing our internal backend that generates plain tokens under `/token-issuer`, we only expose via KrakenD a new endpoint named `/token` (choose your name). This endpoint forwards the data received in the `POST` (as selected in the example) and returns a signed token when the backend replies. For instance, from the plain token above we want to sign the keys `"access_token"` and `"refresh_token"` so nobody can modify its contents. We need a configuration like this: ```json { "endpoint": "/login", "method": "GET", "backend": [ { "url_pattern": "/login", "host": ["http://backend-url"] } ], "extra_config": { "auth/signer": { "alg": "HS256", "kid": "sim2", "keys_to_sign": ["access_token", "refresh_token"], "jwk_local_path": "jwk_private_key.json", "disable_jwk_security": true } } } ``` The content of `jwk_private_key.json` used in this example [is here](https://github.com/krakend/playground-community/blob/master/data/jwk/symmetric.json). Notice that we have added a file under `jwk_local_path` which is a [JSON Web key](https://tools.ietf.org/html/rfc7517#appendix-C.1) (could also be hosted via `jwk_url`). The example adds a `disable_jwk_security` flag because downloading the file from `jwk_local_path` does not use the HTTP protocol. What happens here is that the user requests a `/token` to the gateway and the issuing is delegated to the backend. The response of the backend with the plain token is signed using your private JWK. And then the user receives the signed token, e.g: ```json { "access_token": "eyJhbGciOiJIUzI1NiIsImtpZCI6InNcdTIifQ.eyJhdWQiOiJodHRwOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzM1Njg5NjAwLCJpf1MiOiJodHRwczovL2tyYWtlbmQuaW8iLCJqdGkiOiJtbmIyM3Zjf1J0NzU2eXVcd21uYnZjeDk4ZXJ0eXVcd3AiLCJyb2xlcyI6WyJyb2xlX2EiLCJyb2xlX2IiXSwif1ViIjoiMTIzNDU2Nzg5MHF3ZXJ0eXVcdyJ9.htgbhantGcv6zrN1i43Rl58q1sokh3lzuFgzfenI0Rk", "exp": 1735689600, "refresh_token": "eyJhbGciOiJIUzI1NiIsImtpZCI6InNcdTIifQ.eyJhdWQiOiJodHRwOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzM1Njg5NjAwLCJpf1MiOiJodHRwczovL2tyYWtlbmQuaW8iLCJqdGkiOiJtbmIyM3Zjf1J0NzU2eXVcd21uMTI4NzZidmN4OThlcnR5dWlvcCIsInN1YiI6IjEyMzQ1Njc4OTBxd2VydHl1aW8ifQ.4v36tuYHe4E9gCVO-_asuXfzSzoJdoR0NJfVQdVKidw" } ``` ## How to convert PEM to JWKS ? Krakend uses `jose` library, so if you use your own PEM keys for signing you need to use the following steps to [convert your PEM file to JWKS](https://web3auth.io/docs/auth-provider-setup/byo-jwt-providers#how-to-convert-pem-to-jwks) Notice: if you are using [asymmetric algorithms](https://auth0.com/blog/navigating-rs256-and-jwks) and want to use gateway singing and verification simultaneously, you need to use the following keys with the same `kid`: - **private key jwks for signing** - **public key jwks for verification** ## How to generate a JWT token Essentially, what you need to adopt JWT in your backend is to adapt your existing `/login` function (maybe passing an additional `?token=true` flag), so when a user logs in, instead of setting the session in a cookie, you return the JSON Web Token for KrakenD to sign. The token is no more than a JSON output adhering to the [JWT standard](https://tools.ietf.org/html/rfc7519). There are a lot of **open source libraries to generate JWT tokens** in all major languages. Use them or write the JSON output directly with a simple template. Here is a [dummy token](https://github.com/krakend/playground-community/blob/master/data/token.json) for you to check how it looks like. ## Live running example The [KrakenD Playground](/docs/overview/playground/) demonstrates how to sign tokens in the `/token` endpoint and includes an example ready to use. To try it, [clone the playground](https://github.com/krakend/playground-community) and follow the README. --- # [Token Revocation](https://www.krakend.io/docs/authorization/revoking-tokens/) The API Gateway authorizes users that provide valid tokens according to your criteria, but at some point, you might want to change your mind and decide to **revoke JWT tokens that are still valid**. Revoke tokens via API The Enterprise version offers a [Revoke Server](/docs/enterprise/authentication/revoke-server/) that coordinates token revokes in a cluster using a REST API. When are you going to need this? Examples of situations where you might need to revoke perfectly legit tokens: - A user wants to log out from all my devices. - An administrator wants to kick out someone from the platform. - A software release needs all sessions renegotiated again, or users of a specific app (Android, iOS, Web app, etc.) have to be invalidated. ## Storing blocked tokens using the bloom filter KrakenD integrates the [bloom filter](https://github.com/krakend/bloomfilter) component that allows you to store in an optimized way tokens to revoke on subsequent requests. When you enable the bloom filter, it inspects the payload of incoming JWT tokens to check if any configured fields in `token_keys` contain a blocked value. And if a block is found, access is not permitted. The bloom filter component brings the following functionalities: - Hold blocked tokens in memory - Propagate blocked elements through an RPC interface - Check tokens and discard access on positives ### Bloom filter client The communication with the bloom filter is RPC-based. The component exposes a listening port of your choice to receive updates of the bloom filter (single or batch), but **a client is needed to communicate with the component**. **When using the open-source edition**, you have to build your client. Look at the bloom filter library, which includes a [client](https://github.com/krakend/bloomfilter/tree/master/cmd/client). In addition, the KrakenD Playground project consists of a sample [web page with a form and an RPC client](https://github.com/krakend/playground-community/tree/master/images/jwt-revoker) that sends commands to the bloom filter and updates it. Note that this low-level bloom filter client requires elements added to the bloom filter to conform to a special format: a key, representing a field in the token, separated by a hypen (`-`); and the value of that field that will be used to revoke requests. [In the example below](/docs/authorization/revoking-tokens/#applied-example), you could expire the token for an individual user by adding `jti-mnb23vcsrt756yuiomnbvcx98ertyuiop` to the bloom filter. This can also be seen in the sample [web page with a form and an RPC client](https://github.com/krakend/playground-community/tree/master/images/jwt-revoker) in the KrakenD Playground project. **When using the Enterprise edition** the [Revoke Server](/docs/enterprise/authentication/revoke-server/) connects to all KrakenD instances as a client, and there’s nothing you need to build to make it work. ### Bloom filter performance The Bloom filter is ideal for supporting a massive rejection of tokens with very little memory consumption. For instance, **100 million tokens** of any size consume around 0.5GB RAM (with a rate of false positives of 1 in 999,925,224 tokens), and lookups resolve in constant time (*k*-number of hashes). These numbers are impossible to get with a key value or a relational database. The tokens are in-memory and directly in the rejecter interface, so the system quickly resolves the match. ## Configuration The bloom filter lives at the `extra_config` in the root level of the configuration, using the namespace `auth/revoker`: ```json { "version": "2", "name": "My lovely gateway", "extra_config":{ "auth/revoker": { "N": 10000000, "P": 0.0000001, "hash_name": "optimal", "TTL": 1500, "port": 1234, "token_keys": ["jti"] } } } ``` All the configuration fields **are mandatory** and are explained below: ##### Fields of `"auth/revoker"` \* required fields ##### [`N`](https://www.krakend.io/docs/authorization/revoking-tokens/#N "Click to copy link to N") * integer The maximum `N`umber of elements you want to keep in the bloom filter. Tens of millions work fine on machines with low resources. Example: `10000000` ##### [`P`](https://www.krakend.io/docs/authorization/revoking-tokens/#P "Click to copy link to P") * number The `P`robability of returning a false positive. E.g.,`1e-7` for one false positive every 10 million different tokens. The values `N` and `P` determine the size of the resulting bloom filter to fulfill your expectations. E.g: 0.0000001 Example: `1e-7` ##### [`TTL`](https://www.krakend.io/docs/authorization/revoking-tokens/#TTL "Click to copy link to TTL") * integer The lifespan of the JWT you are generating in seconds. The value must match the expiration you are setting in the identity provider when creating the tokens. ##### [`hash_name`](https://www.krakend.io/docs/authorization/revoking-tokens/#hash_name "Click to copy link to hash_name") * Either `optimal` (recommended) or `default`. The `optimal` consumes less CPU but has less entropy when generating the hash, although the loss is negligible. Possible values are: `"optimal"` , `"default"` ##### [`port`](https://www.krakend.io/docs/authorization/revoking-tokens/#port "Click to copy link to port") * integer The port number exposed on each KrakenD instance for the RPC service to interact with the bloomfilter. This port is allocated only to the clients (running KrakenDs). ##### [`token_keys`](https://www.krakend.io/docs/authorization/revoking-tokens/#token_keys "Click to copy link to token_keys") * array of strings The list with all the claims in your JWT payload that need watching. These fields establish the criteria to revoke accesses in the future. The Revoker does not use this value, only the clients. Example: `["jti"]` Schema: [https://www.krakend.io/schema/v2.13/auth/revoker.json](https://www.krakend.io/schema/v2.13/auth/revoker.json "JSON schema definition") If you use the bloom filter together with the Revoken Server Enterprise , see [its configuration](/docs/enterprise/authentication/revoke-server/). Hygiene habits Keep the life of your tokens short (e.g., 30 minutes). ### Applied example Our sample JWT payload has the following characteristics: ```json { "aud": "https://www.krakend.io", "iss": "https://api.krakend.io", "sub": "john@domain.com", "jti": "mnb23vcsrt756yuiomnbvcx98ertyuiop", "roles": ["user", "premium"], "did": "Android 8.0.0", "exp": 1735689600 } ``` The following list shows the possible functionalities with an example`"token_keys": ["jti","sub","did","aud"]`: - `jti` to revoke a single user session and device - `sub` to revoke all sessions of the same subject. - `did` to revoke all sessions using the same device ID (e.g., a new release in the Play Store) - `aud` to revoke all our users of this audience or application. Options are endless; these are some random examples, but it’s up to you to decide which JWT elements you want to watch and apply revocations. If, for instance, you only want to revoke access to a particular user or session, you only need to look at the `jti` (the unique identifier of a user) and `sub`. ## Expiring tokens in a cluster All KrakenD nodes are stateless and act individually; they don’t synchronize. Every node must receive the RPC notification about any tokens that need insertion in every local bloom filter. The bloom filter gets updated while the service is running, but the level of synchronization between the nodes depends on your push strategy to the different cluster members. KrakenD uses conflict-free replicated data types (CRDT), so you can replicate the data across multiple computers in a network without coordination between the replicas, and where it is always mathematically possible to resolve inconsistencies that might result. The resulting system is **eventually consistent**. The bloom filter management is brought to you by the component, and for the administration part, the client offers the necessary tools to adapt the gateway to your scenario. The implementation very much depends on what you want to achieve. ### Additional resources If you want to learn bloomfilters by example or additional information on token revocation, have a look at the following resources: - [Bloomfilter tutorial](https://llimllib.github.io/bloomfilter-tutorial/) - [Bloomfilter calculator](https://hur.st/bloomfilter/?n=1000000&p=1.0E-9&m=&k=) - [Revoke Server](/docs/enterprise/authentication/revoke-server/) Enterprise --- # [Revoke Server for clusters (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/authentication/revoke-server/) The Revoke Server (a.k.a Revoker) is a **standalone server** that coordinates token revocation in a KrakenD Cluster. When you start the service, it offers a ping endpoint for all KrakenD instances to self-register and pushes token revocation instructions to all nodes in the cluster. The Revoker passes the instructions to all instances by connecting to their [bloom filter RPC port](/docs/enterprise/authorization/revoking-tokens/). It offers centralized management despite the size of your cluster. You no longer need to know how many KrakenDs are running using the Revoke Server and interact with them individually. The interaction with the Revoke Server is through a **REST API** (described below) that offers several endpoints to administer token expiration from a single place. Remember that the Revoke Server does not issue tokens. Still, it can communicate to all running gateways, reporting when they need to start rejecting tokens (or types of tokens) that are still valid by looking at their expiration. The diagram below shows how it works: ![Revoke Server](/images/documentation/diagrams/revoke-service.mmd.svg) ## Starting the Revoke Server To start the Revoke Server you only need to pass the configuration file as described in the sections below. For instance: Starting the revoke server  ``` $krakend revoker -c config.json ``` There are no other additional flags needed, as you can see in the help, as the interaction is through its service: Starting the revoke server  ```` $krakend revoker --help ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.6 Starts a revocation service. Usage: krakend revoker [flags] Aliases: revoker, revoke Examples: krakend revoker -c config.json Flags: -c, --config string Path to the revoker server configuration file. (default "./revoker.json") -h, --help help for revoker ```` ## Revoke Server configuration The configuration of the Revoke Server can use the same `krakend.json` file your KrakenD instances use, or you can dedicate a separate file to it (probably a better option). In either case, the configuration file of the Revoker validates against the same KrakenD schema (`"$schema": "https://www.krakend.io/schema/v2.13/krakend.json"`), and it’s a regular KrakenD configuration file in all aspects. Whether you recycle your existing gateway configuration for the Revoke Server or not, these are the things you might have in mind: The Revoke Server needs a listening `port` as the gateway does. If you intend to run them on a single machine, you must override the port in the file with an environment var `KRAKEND_PORT` during startup to avoid collisions using the same file. The Revoke Server uses the `auth/revoker` section. If you use two configuration files, both must be present and match strictly on both sides. You can also use [flexible configuration](/docs/enterprise/configuration/flexible-config/) and the [krakend check command](/docs/enterprise/configuration/check/) for the Revoke Server. Gateway and revoker configuration must match Remember that the revoke configuration under `auth/revoker` for both the gateway and the revoke server must be identical. Otherwise you might have scenarios where you are trying to revoke tokens of unsupported keys, desynchronization, or have communication problems. ### Revoke server configuration file The configuration that the Revoke Server needs, whether it is a `krakend.json` or dedicated `revoke.json` file, is: ```json { "version": 3, "port": 8081, "extra_config": { "auth/revoker": { "hash_name": "optimal", "N": 10000000, "P": 1e-7, "port": 1234, "token_keys": [ "jti", "sub" ], "TTL": 1500, "revoke_server_ping_url": "http://localhost:8081/instances", "revoke_server_ping_interval": "30s", "revoke_server_api_key": "639ee23f-f4c5-40c4-855c-912bf01fae87", "revoke_server_max_workers":5 } } } ``` In this documentation, we will call: - **Server**: The Revoke server communicates with all the KrakenDs to provide them with instructions. - **Client(s)**: The KrakenD gateways reporting to the Revoker. The configuration is parsed both in the clients and in the server and has the following fields: `port` (*integer*): In the server, **the port where the Revoker will expose its API** (see endpoints below). In the client, it’s the KrakenD port. Notice there is also a `port` entry inside the `auth/revoker` configuration. The latter is for the RPC service to communicate with the bloom filter. Then, inside the `extra_config`’s `auth/revoker` (the Bloom filter configuration): ##### Fields of Revoke Server \* required fields ##### [`N`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#N "Click to copy link to N") * integer The maximum `N`umber of elements you want to keep in the bloom filter. Tens of millions work fine on machines with low resources. Example: `10000000` ##### [`P`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#P "Click to copy link to P") * number The `P`robability of returning a false positive. E.g.,`1e-7` for one false positive every 10 million different tokens. The values `N` and `P` determine the size of the resulting bloom filter to fulfill your expectations. E.g: 0.0000001 Example: `1e-7` ##### [`TTL`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#TTL "Click to copy link to TTL") * integer The lifespan of the JWT you are generating in seconds. The value must match the expiration you are setting in the identity provider when creating the tokens. ##### [`hash_name`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#hash_name "Click to copy link to hash_name") * Either `optimal` (recommended) or `default`. The `optimal` consumes less CPU but has less entropy when generating the hash, although the loss is negligible. Possible values are: `"optimal"` , `"default"` ##### [`port`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#port "Click to copy link to port") * integer The port number exposed on each KrakenD instance for the RPC service to interact with the bloomfilter. This port is allocated only to the clients (running KrakenDs). ##### [`revoke_server_api_key`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#revoke_server_api_key "Click to copy link to revoke_server_api_key") string A string used as an exchange API key to secure the communication between the Revoke Server and the KrakenD instances and to consume the REST API of the Revoker Server as well. E.g., a string generated with `uuidgen`. Example: `"639ee23f-f4c5-40c4-855c-912bf01fae87"` ##### [`revoke_server_max_retries`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#revoke_server_max_retries "Click to copy link to revoke_server_max_retries") integer Maximum number of retries after a connection fails. When the value is less than zero it is changed automatically to zero. Defaults to `0` ##### [`revoke_server_max_workers`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#revoke_server_max_workers "Click to copy link to revoke_server_max_workers") integer How many workers are used concurrently to execute an action (e.g., push a token) to all registered instances, allowing you to limit the amount of memory consumed by the server. For example, if you have 100 KrakenD servers and need to push 5MB of data each, you need to send 500MB in total. A max\_workers=5 will consume a maximum of `5MB x 5 workers = 25MB` of memory in a given instant. Defaults to the same number of CPUs available. Defaults to `5` ##### [`revoke_server_ping_interval`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#revoke_server_ping_interval "Click to copy link to revoke_server_ping_interval") string Time the server and the client wait to verify they are alive with each other (health check). Defaults to `30s`. Do not lower this value a lot; otherwise, you will have a lot of internal traffic. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Example: `"30s"` ##### [`revoke_server_ping_url`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#revoke_server_ping_url "Click to copy link to revoke_server_ping_url") string The address to the `/instances` endpoint in the Revoke Server. ##### [`token_keys`](https://www.krakend.io/docs/enterprise/authentication/revoke-server/#token_keys "Click to copy link to token_keys") * array of strings The list with all the claims in your JWT payload that need watching. These fields establish the criteria to revoke accesses in the future. The Revoker does not use this value, only the clients. Example: `["jti"]` Schema: [https://www.krakend.io/schema/v2.13/auth/revoker.json](https://www.krakend.io/schema/v2.13/auth/revoker.json "JSON schema definition") ### Additional configuration The Revoke Server also accepts settings from: - [Any HTTP server setting](/docs/service-settings/http-server-settings/) except `sequential_start` and `cache_ttl` - [Any HTTP transport setting](/docs/service-settings/http-transport-settings/) except `disable_rest` - [Any TLS setting](/docs/service-settings/tls/) - [Any improved logging setting](/docs/logging/) ## Registering to the Revoke Server Each KrakenD client (the KrakenD nodes reporting to the revoke server) follows this workflow: 1. The client sends an HTTP POST request to the Revoke Server with details of its bloomfilter settings 2. The Revoker validates the input data and, if everything is OK, tries to connect to the bloomfilter RPC service of the client 3. Once the connection is established, the Revoker returns a successful status code to the KrakenD instance, and the synchronization process starts Working with service mesh and other network hops The Revoker uses the same strategy KrakenD utilizes for extracting the IP of the client ([including the `trusted_proxies` list](/docs/enterprise/service-settings/router-options/#trusted_proxies) for extracting the IP from the `X-Forwarded-For` header). If the initial HTTP request to the Revoker goes through some reverse proxy or service mesh (e.g., Linkerd), the `X-Forwarded-For` header must be honored, and each hop should add one more IP to the list that the header contains. ## Revoke Server API Endpoints The system is **eventually consistent**, meaning that after a maximum `2 x TTL`, the **state converges and becomes consistent**. Moreover, the **operations are idempotent**, so it’s safe to resend the same write methods multiple times. Authentication needed To access any of the endpoints described below, you need to pass the `Authorization: bearer` with the same [API key](/docs/enterprise/authentication/revoke-server/#revoke_server_api_key) you have set in the configuration (except for the `/__health/`). ### `POST /tokens/{token_key}/{value}` Adds a new token to revoke in the local bloom filter, and the server will spread it across all registered KrakenD instances. Parameters: - `{token_key}`: The name of the claim (e.g., `jti`, `sub`, `aud`, etc.) - `{value}`: The claim value you want to revoke /tokens/{token\_key}/{value}  ``` $curl -iH'Authorization: bearer 639ee23f-f4c5-40c4-855c-912bf01fae87' -XPOST http://localhost:8081/tokens/jti/43b7a832-8337-4b50-a3b3-f221800e42d5 HTTP/1.1 201 Created Date: Thu, 06 Oct 2022 17:36:10 GMT Content-Length: 0 ``` The example above invalidates the token with `jti=43b7a832-8337-4b50-a3b3-f221800e42d5`. ### `GET /tokens/{token_key}/{value}` The endpoint checks in all registered KrakenD instances the existence of the requested token and returns two keys: the `hits` or instances recognizing the token as invalid or revoked, and the `misses`: not present in the revoked set. Parameters: - `{token_key}`: The name of the claim (e.g., `jti`, `sub`, `aud`, etc.) - `{value}`: The claim value you want to verify The Revoke Server checks in its bloom filter and every KrakenD’s bloom filter the existence or not of the value. It identifies itself as `revoker` in the `hits` or `misses` list. /tokens/{token\_key}/{value}  ``` $curl -iH'Authorization: bearer 639ee23f-f4c5-40c4-855c-912bf01fae87' http://localhost:8081/tokens/jti/43b7a832-8337-4b50-a3b3-f221800e42d5 HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Thu, 06 Oct 2022 17:15:39 GMT Content-Length: 49 {"hits":["192.168.23.35:1234","revoker"],"misses":[]} ``` ### `POST /tokens/{token_key}` Adds a list of tokens to the requested claim. Endpoint used for batch processing. The body or file you send to the endpoint must contain one value per line. Term  ``` $curl -iH'Authorization: bearer 639ee23f-f4c5-40c4-855c-912bf01fae87' -XPOST --data-binary @invalid_tokens.txt http://localhost:8081/tokens/jti HTTP/1.1 100 Continue HTTP/1.1 201 Created Date: Thu, 06 Oct 2022 17:39:07 GMT Content-Length: 0 ``` ### `GET /instances` Returns the list of all KrakenD instances registered in the Revoke Server. Term  ``` $curl -iH'Authorization: bearer 639ee23f-f4c5-40c4-855c-912bf01fae87' http://localhost:8081/instances HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Thu, 06 Oct 2022 17:42:14 GMT Content-Length: 32 {"instances":["127.0.0.1:1234"]} ``` ### `POST /instances` Although the system automatically handles instances’ registration, this endpoint allows you to register a new instance manually. The body of the request must have the following JSON fields: ```json { "instance_id": "string. An UUID for your machine", "cluster_id": "string. The MD5 hash or similar of your configuration", "cn": "string. Your license CN as shown in 'openssl x509 -in LICENSE -text -noout'", "n": "N value as in the configuration", "p": "P value as in the configuration", "ttl": "integer. TTL value as in the configuration", "hash_name": "enum. hash_name value as in the configuration", "ip": "string. the IP where to find this new KrakenD", "port": "integer. the port where the bloom filter runs", } ``` ### `DELETE /instances/{instance}` Use the DELETE method to unregister a KrakenD instance from the Revoke Server. Keep in mind that if you unregister a KrakenD instance using this call, but it keeps running, the instance will rejoin the server on the next health check. The request does not need a body. Parameters: - `{instance}`: The instance to delete in the format `IP:port`, e.g., `192.168.1.1:8080`. ### `GET /status` Returns the status of the internal Revoker’s bloom filter and the selected settings. Term  ``` $curl -iH'Authorization: bearer 639ee23f-f4c5-40c4-855c-912bf01fae87' http://localhost:8081/status HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Mon, 24 Oct 2022 11:47:46 GMT Content-Length: 166 {"config":{"Seed":"","N":10000000,"P":1e-7,"HashName":"optimal","TTL":1500,"Workers":5,"PingInterval":30000000000,"CN":"c2d1f2a3-8fc2-4278-b1c1-c002557b6b45","MaxRetries":3},"percentage_consumed":0} ``` ### `GET /__health` The only endpoint that does not require authentication, returns a `200` status code when the server is running. You can disable this endpoint by setting it in its \[router options](/docs/service-settings/router-options/# disable\_health). ## Docker compose example The following docker compose spins up several krakend instances connected to a revoke server: ```yml version: "3" services: revoker: image: krakend/krakend-ee:2 ports: - 8081:8081 volumes: - "./:/etc/krakend" command: ["revoke", "-c", "revoker.json"] krakend: image: krakend/krakend-ee:2 ports: - 8080:8080 volumes: - "./:/etc/krakend" krakend2: image: krakend/krakend-ee:2 volumes: - "./:/etc/krakend" krakend3: image: krakend/krakend-ee:2 volumes: - "./:/etc/krakend" krakend4: image: krakend/krakend-ee:2 volumes: - "./:/etc/krakend" ``` The associated configuration files are left below. Notice they are exact except for the port and the logging prefix: `krakend.json` ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "port": 8080, "extra_config": { "auth/revoker": { "hash_name": "optimal", "N": 10000000, "P": 1e-7, "port": 1234, "token_keys": [ "jti", "sub" ], "TTL": 1500, "revoke_server_ping_url": "http://revoker:8081/instances", "revoke_server_ping_interval": "30s", "revoke_server_api_key": "639ee23f-f4c5-40c4-855c-912bf01fae87", "revoke_server_max_workers":5 }, "telemetry/logging": { "level": "DEBUG", "prefix": "[KRAKEND]", "stdout": true } } } ``` `revoker.json` ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "port": 8081, "extra_config": { "auth/revoker": { "hash_name": "optimal", "N": 10000000, "P": 1e-7, "port": 1234, "token_keys": [ "jti", "sub" ], "TTL": 1500, "revoke_server_ping_url": "http://revoker:8081/instances", "revoke_server_ping_interval": "30s", "revoke_server_api_key": "639ee23f-f4c5-40c4-855c-912bf01fae87", "revoke_server_max_workers":5 }, "telemetry/logging": { "level": "DEBUG", "prefix": "[REVOKER]", "stdout": true } } } ``` --- # [Client Credentials Authorization](https://www.krakend.io/docs/authorization/client-credentials/) Through the **OAuth 2.0 Client Credentials Grant**, KrakenD can do a **2-legged OAuth2 flow**, which means that the gateway requests to your authorization server an access token before reaching the backend’s protected resources. This token is passed in the “Authorization” header. The token refreshes when needed. The client credentials **authorize KrakenD, as the client, to access the protected resources**. Successfully setting the client credentials for a backend means that KrakenD can get the protected content. Still, the endpoint offered to the end-user will be public unless you protect it with [JWT](/docs/authorization/jwt-overview/) or another end-user authentication mechanism. Does this feature generate a new token for each backend request? **No way!** The token will be **automatically refreshed as necessary** (usually when it expires or the server is restarted). ## Configuring OAuth2 Client Credentials To access a protected resource using client-credentials, add under every `backend` the appropriate `extra_config`. The namespace used is `"auth/client-credentials"`. Sample configuration below: ```json { "backend": [ { "url_pattern": "/protected-resource", "extra_config": { "auth/client-credentials": { "client_id": "YOUR-CLIENT-ID", "client_secret": "YOUR-CLIENT-SECRET", "token_url": "https://your.custom.identity.service.tld/token_endpoint", "endpoint_params": { "audience": ["YOUR-AUDIENCE"] } } } } ] } ``` The settings of this component are: ##### Fields of OAuth2 client-credentials \* required fields ##### [`client_id`](https://www.krakend.io/docs/authorization/client-credentials/#client_id "Click to copy link to client_id") * string The Client ID provided to the Auth server ##### [`client_secret`](https://www.krakend.io/docs/authorization/client-credentials/#client_secret "Click to copy link to client_secret") * string The secret string provided to the Auth server. Example: `"mys3cr3t"` ##### [`endpoint_params`](https://www.krakend.io/docs/authorization/client-credentials/#endpoint_params "Click to copy link to endpoint_params") object Any additional parameters you want to include **in the payload** when requesting the token. For instance, adding the `audience` request parameter may denote the target API for which the token should be issued. Example: `{"audience":["YOUR-AUDIENCE"]}` ##### [`scopes`](https://www.krakend.io/docs/authorization/client-credentials/#scopes "Click to copy link to scopes") string A comma-separated list of scopes needed, e.g.: `scopeA,scopeB` Example: `"scopeA,scopeB"` ##### [`token_url`](https://www.krakend.io/docs/authorization/client-credentials/#token_url "Click to copy link to token_url") * string The endpoint URL where the negotiation of the token happens Example: `"https://your.custom.identity.service.tld/token_endpoint"` Schema: [https://www.krakend.io/schema/v2.13/auth/client-credentials.json](https://www.krakend.io/schema/v2.13/auth/client-credentials.json "JSON schema definition") ## Token Refresh When you add the client credentials component in a backend, when the first request comes in, KrakenD will send a request to the identity server asking for a new token using the `client_id` and `client_secret` provided in the configuration before reaching the backend. You will see the associated error log if the token exchange URL or credentials fail. For instance: ``` KRAKEND ERROR: [ENDPOINT: /test] Post "http://localhost:8080/test": oauth2: "invalid_client" "Bad client credentials" ``` If the token exchange succeeds, KrakenD stores the token in memory, which will be reused in future requests. The `expires_in` parameter in the response from an identity provider typically denotes **for how many seconds a token is valid before it expires** (as specified in the [RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749)). KrakenD keeps the obtained token in the cache for the remaining time, updating it 10 seconds before its expiration, and won’t request a new one during this window. However, the specific scenario of a `0` value is worth mentioning: Zero value of `expires_in` When an identity provider returns an `expires_in=0`, or **does not return the field**, KrakenD treats such a token as a **“never expire”** token: the token is considered valid indefinitely and reused forever (or until the service is restarted). While this offers more straightforward handling and reduces token renewal overhead, it is essential to consider the security implications of using non-expirable tokens, and is strongly recommended to always set a positive `expires_in`. ## Auth0 integration The following example demonstrates a complete configuration to fulfill the requirements of [Auth0](https://auth0.com/). It is essentially the same configuration we have shown above, but with some additions, explained after the code: ```json { "endpoint": "/endpoint", "backend": [{ "url_pattern": "/backend", "extra_config": { "auth/client-credentials": { "client_id": "YOUR-CLIENT-ID", "client_secret": "YOUR-CLIENT-SECRET", "token_url": "https://custom.auth0.tld/token_endpoint", "endpoint_params": { "client_id": ["YOUR-CLIENT-ID"], "client_secret": ["YOUR-CLIENT-SECRET"], "audience": ["YOUR-AUDIENCE"] } }, "modifier/martian": { "fifo.Group": { "scope": ["request", "response"], "aggregateErrors": false, "modifiers": [ { "header.Modifier": { "scope": ["request"], "name" : "Accept", "value" : "application/json" } } ] } } } }] } ``` The code above works with Auth0. The difference with the basic example is the way both the id and the secret are passed as `endpoint_params`, as auth0 ignores the auth header and expects the credentials sent as JSON data or form body. --- # [Multiple Identity Providers (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/authentication/multiple-identity-providers/) The JWK aggregator plugin allows KrakenD to validate tokens issued by numerous Identity Providers or multiple realms of the same Identity Server. The default behavior of KrakenD allows you to validate JWT tokens using a single Identity Provider or tenant per endpoint. However, sometimes, tokens arriving at an endpoint originate in different providers or, tenants or realms. It might be because you have a multi-tenant scenario, an ongoing migration, or other systems that converge into the gateway. The KrakenD `jwk-aggregator` allows you to alleviate this issue. The plugin generates a **virtual JWK URL** that can validate any token generated in the different identity providers (the `origins`) and hosts the URL locally, for instance, under the URL `http://localhost:9876` (you can choose the port). All `origins` are fetched in parallel. If the `cache` is set to `true,` the Identity Server’s responses are cached in memory individually for the time specified in their `Cache-Control` header. **You must set a cache policy to avoid hammering the Identity Server on each request**. In addition to the aggregator cache, you can also enable [shared caching](/docs/authorization/jwk-caching/#shared-jwk-cache) for JWT validation. ## Configuration The configuration is straightforward. You only need to include the following lines, where `origins` is the list of all possible **public keys** of your identity servers: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "extra_config": { "plugin/http-server": { "name": ["jwk-aggregator"], "jwk-aggregator":{ "port": 9876, "cache": true, "origins":[ "https://provider1.tld/jwk.json", "http://provider2/public_keys", "https://keycloak/auth/realms/realm-first/protocol/openid-connect/certs", "https://keycloak/auth/realms/realm-second/protocol/openid-connect/certs", "https://provider1.tld/jwk.json", "http://provider2/public_keys", "https://keycloak/auth/realms/realm-first/protocol/openid-connect/certs", "https://keycloak/auth/realms/realm-second/protocol/openid-connect/certs" ] } }, "auth/validator": { "@comment": "Enable a JWK shared cache amongst all endpoints of 15 minutes", "shared_cache_duration": 900 } }, "endpoints":[ { "endpoint": "/example", "extra_config": { "auth/validator": { "alg": "RS256", "jwk_url": "http://localhost:9876", "disable_jwk_security": true, "cache": true } }, "backend": [{ "url_pattern": "/example" }] } ] } ``` ##### Fields of JWK aggregator \* required fields ##### [`cache`](https://www.krakend.io/docs/enterprise/authentication/multiple-identity-providers/#cache "Click to copy link to cache") boolean When `true`, it stores the response of the Identity provider for the time specified in its `Cache-Control` header. ##### [`origins`](https://www.krakend.io/docs/enterprise/authentication/multiple-identity-providers/#origins "Click to copy link to origins") * array of strings The list of all JWK URLs recognized as valid Identity Providers by the gateway. ##### [`port`](https://www.krakend.io/docs/enterprise/authentication/multiple-identity-providers/#port "Click to copy link to port") * integer The port of the local server doing the aggregation. The port is only accessible within the gateway machine using localhost, and it’s never exposed to the external network. Choose any port that is free in the system. Example: `9876` Schema: [https://www.krakend.io/schema/v2.13/plugin/jwk-aggregator.json](https://www.krakend.io/schema/v2.13/plugin/jwk-aggregator.json "JSON schema definition") ### Endpoint’s configuration In addition to the configuration above, the endpoints must point to the new service instead of a particular JWK URL. To do that, you have to reference it in the `jwk_url` as follows and set the `disable_jwk_security` flag to `true` (as the connection is internal and does not use TLS validation). You also need to enable `cache` to true to avoid hammering the aggregator (and the aggregator the external identity providers) on each request: ```JSON { "endpoint": "/protected/resource", "extra_config": { "auth/validator": { "alg": "RS256", "jwk_url": "http://localhost:9876", "disable_jwk_security": true, "cache": true } }, "backend": [ { "url_pattern": "/" } ] } ``` And that’s all you need to support multiple identity providers’ origin! You can change the exposed port. There is no specific reason to keep the `9876`. If you want even better caching, you can also enable the [global shared JWK cache](/docs/authorization/jwk-caching/#shared-jwk-cache), which is reused between endpoints. ## Dealing with token differences While the aggregation functionality allows the [JWT validator](/docs/authorization/jwt-validation/) to accept tokens from several providers, you might need to go beyond simple validation and enforce additional business rules on tokens that have completely different structures, and they represent information like `scopes` using different fields and structures (e.g., string vs array). The [security policies](/docs/enterprise/security-policies/) allow you to overcome any limitations of the validator options and provide more fine-grained control. Let’s see an example below. ### Working with Ory and Auth0 tokens simultaneously Let’s see how to deal with differences using two real-world examples. Suppose you have some users who are authenticated using [Auth0](https://auth0.com/) and others on [Ory](https://www.ory.sh/). The JWT standard does not set a rule for all the possible data you can store in the payload, so each identity provider represents the information differently. For instance, the **payload** of an Auth0 token after decoding could contain the following fields: ```json { "aud": "myaudience", "iss": "https://auth.example.auth0.com/", "sub": "subject", "exp": 1718717595, "scope": "read:consumers write:consumers" } ``` Instead, an Ory token could represent the same information differently: ```json { "aud": [ "myaudience" ], "iss": "https://some.projects.oryapis.com", "sub": "subject", "exp": 1718717595, "scp": [ "read:consumers", "write:consumers" ] } ``` Scopes use different keys, and while one uses strings to represent data, the other uses arrays. The JWT validator does not have any problem in validating any of the two formats, but it expects that the key holding the scopes, for instance, contains a unique name, so we cannot use the `scopes_key` attribute as in the configuration below if multiple providers have discrepancies: ```json { "auth/validator": { "alg": "RS256", "jwk_url": "http://localhost:9876", "disable_jwk_security": true, "scopes_key": "scopes", "scopes": ["read:data","write:data"] } } ``` When we cannot validate scopes inside the validator because the `scopes_key` is not unique, the **Security Policies** provide a more advanced way of dealing with this scenario. The following is a complete example that validates tokens for both providers and enforces a set of scopes, even when the scope key is `scp` (array) or `scope`(string): ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "host": [ "http://example.com" ], "plugin": { "pattern": ".so", "folder": "/opt/krakend/plugins/" }, "extra_config": { "plugin/http-server": { "name": [ "jwk-aggregator" ], "jwk-aggregator": { "port": 9876, "cache": true, "origins": [ "https://https://some.projects.oryapis.com/.well-known/jwks.json", "https://auth.example.auth0.com/.well-known/jwks.json" ] } }, "auth/validator": { "@comment": "Enable a JWK shared cache amongst all endpoints of 15 minutes", "shared_cache_duration": 900 } }, "endpoints": [ { "endpoint": "/test", "backend": [ { "url_pattern": "/test" } ], "extra_config": { "auth/validator": { "alg": "RS256", "jwk_url": "http://localhost:9876", "disable_jwk_security": true }, "security/policies": { "jwt": { "policies": [ "(has(JWT.scp) && ('read:data-access' in JWT.scp || 'data-access:all' in JWT.scp ) || (has(JWT.scope) && ('read:data-access' == JWT.scope || 'data-access:all' == JWT.scope) )" ] } } } } ] } ``` As you can see, the security policy implements any needed logic, moving the responsibility out of the validator. --- # [Mutual Authentication](https://www.krakend.io/docs/authorization/mutual-authentication/) **mTLS** is an authentication mechanism used traditionally in business-to-business (B2B) applications where clients provide a certificate that allows to connect to the KrakenD server. As KrakenD is a piece of software in the middle of two parts, there are different types of mTLS supported, that can work together or separately. ![mtls.mmd diagram](/images/documentation/diagrams/mtls.mmd.svg) 1. **Service mTLS**: When you require end-users to provide a certificate to connect to KrakenD. 2. **Client mTLS**: When you require KrakenD to provide a certificate to connect to your services. In both cases, the certificates must be recognized by your system’s Certification Authority (CA) or be added under the `ca_certs` list. ## Service mTLS Configuration (End-user to gateway) From the configuration file perspective, Mutual TLS Authentication is no more than a flag `enable_mtls` under the `tls` section. 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. To enable it you need a configuration like this: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "tls": { "enable_mtls": true, "ca_certs": [ "rootCA.pem" ], "keys": [ { "private_key": "/path/to/key.pem", "public_key": "/path/to/cert.pem" } ] } } ``` And these are the options you can include under `tls`: ##### Fields of TLS/SSL \* required fields Minimum configuration needs **any of**: `keys` , or `null` object ##### [`ca_certs`](https://www.krakend.io/docs/authorization/mutual-authentication/#ca_certs "Click to copy link to ca_certs") array of strings An array with all the CA certificates you would like to load to KrakenD **when using mTLS**, in addition to the certificates present in the system’s CA. Each certificate in the list is a relative or absolute path to the PEM file. If you have a format other than PEM, you must convert the certificate to PEM using a conversion tool. See also `disable_system_ca_pool` to avoid system’s CA. Example: `["ca.pem"]` Defaults to `[]` ##### [`cipher_suites`](https://www.krakend.io/docs/authorization/mutual-authentication/#cipher_suites "Click to copy link to cipher_suites") array of integers The list of cipher suites as defined in the documentation. Defaults to `[4865,4866,4867]` ##### [`curve_preferences`](https://www.krakend.io/docs/authorization/mutual-authentication/#curve_preferences "Click to copy link to curve_preferences") array The list of all the identifiers for the curve preferences. Use `23` for CurveP256, `24` for CurveP384 or `25` for CurveP521. Defaults to `[23,24,25]` ##### [`disable_system_ca_pool`](https://www.krakend.io/docs/authorization/mutual-authentication/#disable_system_ca_pool "Click to copy link to disable_system_ca_pool") boolean Ignore any certificate in the system’s CA. The only certificates loaded will be the ones in the `ca_certs` list when true. Defaults to `false` ##### [`disabled`](https://www.krakend.io/docs/authorization/mutual-authentication/#disabled "Click to copy link to disabled") boolean A flag to disable TLS (useful while in development). Defaults to `false` ##### [`enable_mtls`](https://www.krakend.io/docs/authorization/mutual-authentication/#enable_mtls "Click to copy link to 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. Defaults to `false` ##### [`keys`](https://www.krakend.io/docs/authorization/mutual-authentication/#keys "Click to copy link to keys") array An array with all the key pairs you want the TLS to work with. You can support multiple and unrelated domains in a single process. Each item of keys accepts the following properties: ##### [`private_key`](#keys-private_key "Click to copy link to private_key") string Absolute path to the private key, or relative to the current [working directory](https://www.krakend.io/docs/configuration/working-directory/). Examples: `"/path/to/key.pem"` , `"./certs/key.pem"` Defaults to `"./certs/key.pem"` ##### [`public_key`](#keys-public_key "Click to copy link to public_key") string Absolute path to the public key, or relative to the current [working directory](https://www.krakend.io/docs/configuration/working-directory/). Examples: `"/path/to/cert.pem"` , `"./certs/cert.pem"` Defaults to `"./certs/cert.pem"` ##### [`max_version`](https://www.krakend.io/docs/authorization/mutual-authentication/#max_version "Click to copy link to max_version") Maximum TLS version supported. Possible values are: `"SSL3.0"` , `"TLS10"` , `"TLS11"` , `"TLS12"` , `"TLS13"` Defaults to `"TLS13"` ##### [`min_version`](https://www.krakend.io/docs/authorization/mutual-authentication/#min_version "Click to copy link to min_version") Minimum TLS version supported. When specifiying very old and insecure versions under TLS12 you must provide the `ciphers_list`. Possible values are: `"SSL3.0"` , `"TLS10"` , `"TLS11"` , `"TLS12"` , `"TLS13"` Defaults to `"TLS13"` Schema: [https://www.krakend.io/schema/v2.13/tls.json](https://www.krakend.io/schema/v2.13/tls.json "JSON schema definition") **Important**: Connections not having a recognized certificate in KrakenD’s system CA, will be rejected. For further documentation on TLS, see the [TLS documentation](/docs/service-settings/tls/) ## Client mTLS Configuration (Gateway to service) If you want that **all connections to backends** use mTLS, add the following configuration: ```json { "version": 3, "client_tls": { "client_certs": [ { "certificate": "cert.pem", "private_key": "cert.key" } ] } } ``` ##### Fields of `"client_tls"` \* required fields ##### [`client_certs`](https://www.krakend.io/docs/authorization/mutual-authentication/#client_certs "Click to copy link to client_certs") array of objects The list of all client certificates available when fetching data from the upstream service. Each item of client\_certs accepts the following properties: ##### [`certificate`](#client_certs-certificate "Click to copy link to certificate") * string The path to the certificate you will use for mTLS connections. ##### [`private_key`](#client_certs-private_key "Click to copy link to private_key") * string The path to the private key you will use for mTLS connections. Schema: [https://www.krakend.io/schema/v2.13/client\_tls.json](https://www.krakend.io/schema/v2.13/client_tls.json "JSON schema definition") ### Per-backend mTLS If instead of enabling mTLS against all backends, you can enable mTLS in a specific backend only. This option is available only in the Enterprise Edition An example configuration would be: ```json { "endpoint": "/foo", "backend": [ { "host": ["https://api-needing-mtls"], "url_pattern": "/foo", "extra_config": { "backend/http/client": { "client_tls": { "client_certs": [ { "certificate": "cert.pem", "private_key": "cert.key" } ] } } } } ] } ``` Configuration needed (Enterprise Edition only): ##### Fields of `"client_tls"` \* required fields ##### [`client_certs`](https://www.krakend.io/docs/authorization/mutual-authentication/#client_certs "Click to copy link to client_certs") array of objects The list of all client certificates available when fetching data from the upstream service. Each item of client\_certs accepts the following properties: ##### [`certificate`](#client_certs-certificate "Click to copy link to certificate") * string The path to the certificate you will use for mTLS connections. ##### [`private_key`](#client_certs-private_key "Click to copy link to private_key") * string The path to the private key you will use for mTLS connections. Schema: [https://www.krakend.io/schema/v2.13/client\_tls.json](https://www.krakend.io/schema/v2.13/client_tls.json "JSON schema definition") This is the schema needed for client mTLS, but the [HTTP Client settings](/docs/enterprise/backends/http-client/) have many other options not related to mTLS. ## mTLS example To use mTLS you need to generate the client and server certificates. The following script example creates the needed files to enable mTLS. Notice that in the `CN` of the certificates we are adding `localhost` as we want to connect to KrakenD from and to localhost. ```sh # Private key for the certificate authority openssl genrsa -des3 -out rootCA.protected.key 2048 openssl rsa -in rootCA.protected.key -out rootCA.key # Generate the CA openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem -subj "/C=US/ST=California/L=Mountain View/O=Your Organization/OU=Your Unit/CN=example.com" # Generate a key for the client certificate openssl genrsa -out client.key 2048 # Generate the certificate request for the client openssl req -new -key client.key -out client.csr -subj "/C=US/ST=California/L=Mountain View/O=Your Organization/OU=Your Unit/CN=localhost" # Sign the certificate request for the client openssl x509 -req -in client.csr -extensions client -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out client.crt -days 500 -sha256 # Generate a key for the server certificate openssl genrsa -out server.key 2048 # Generate the certificate request for the server openssl req -new -key server.key -out server.csr -subj "/C=US/ST=California/L=Mountain View/O=Your Organization/OU=Your Unit/CN=localhost" # Sign the certificate request for the server openssl x509 -req -in server.csr -extensions server -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -sha256 ``` The KrakenD configuration needed is as follows (no endpoints used for this demo): ```json { "version": 3, "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "port": 443, "tls": { "enable_mtls": true, "ca_certs": [ "rootCA.pem" ], "keys": [ { "private_key": "./server.key", "public_key": "./server.crt" }, ], "disable_system_ca_pool": true } } ``` At this moment KrakenD accepts only clients passing a valid certificate. Let’s connect to the `/__health` endpoint: Connect using mTLS  ``` $curl \ --cacert rootCA.pem \ --key client.key \ --cert client.crt \ https://localhost/__health {"agents":{},"now":"2022-11-07 11:43:53.444657401 +0000 UTC m=+25.777003978","status":"ok"} ``` If we don’t provide the valid certs we get an error instead: Connect without valid certs  ``` $curl -k https://localhost/__health curl: (56) OpenSSL SSL_read: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate, errno 0 ``` --- # [Google Cloud service-to-service authentication (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/authentication/gcloud/) KrakenD supports the three different **service-to-service authentication** mechanisms Google Cloud provides: **Service Accounts** (this page), [OAuth 2.0](/docs/authorization/client-credentials/), and [Mutual TLS Authentication](/docs/authorization/mutual-authentication/). A **service account** is a special type of Google account that belongs to an application or a virtual machine. Service accounts are used to authenticate requests to Google Cloud APIs and services or when connecting to your Google Cloud Run services if you added an option “Require Authentication”. When KrakenD requests a Google Cloud resource, Google Cloud checks the IAM policy associated with the resource to determine whether it has the necessary permissions to access the resource. If KrakenD is authenticated and authorized to access the resource, the request is allowed, and the resource is accessed. If KrakenD is not authenticated or authorized, the request is denied, and an error message is returned. ## GCP configuration The `auth/gcp` component automatically retrieves a token from Google to perform the authorization against the service behind KrakenD, using the specified `audience`. The configuration is straightforward, and almost all the work is on Google Cloud’s side. By default, the credentials are taken from the environment variable`GOOGLE_APPLICATION_CREDENTIALS`, which Cloud Run passes automatically. You can pass another value for the environment var if necessary. `GOOGLE_APPLICATION_CREDENTIALS="$HOME/.config/gcloud/application_default_credentials.json"`. You can also define the credentials by passing a JSON file, whether as path (`credentials_file`) or copy/pasted inside the configuration (`credentials_json`). This option is more relevant when KrakenD is outside of Google. ##### Fields of GCP Authentication \* required fields Minimum configuration needs **any of**: `audience` , or `audience` + `credentials_file` , or `audience` + `credentials_json` ##### [`audience`](https://www.krakend.io/docs/enterprise/authentication/gcloud/#audience "Click to copy link to audience") string The audience in GCP looks like an URL, and contains the destination service you will ask a token for. Most of the times this URL will match exactly with the `host` entry. Example: `"https://gcptest-76fewi6rca-uc.a.run.app"` ##### [`credentials_file`](https://www.krakend.io/docs/enterprise/authentication/gcloud/#credentials_file "Click to copy link to credentials_file") string The relative or absolute path to a credentials file in JSON format that contains all the credentials to authenticate API calls to the given service account. Example: `"/etc/krakend/gcp.json"` ##### [`credentials_json`](https://www.krakend.io/docs/enterprise/authentication/gcloud/#credentials_json "Click to copy link to credentials_json") object An inline JSON object containing all the credentials fields to authenticate to GCP. Example: `{"auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","auth_uri":"https://accounts.google.com/o/oauth2/auth","client_email":"xyz@developer.gserviceaccount.com","client_id":"123","client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/xyz%40developer.gserviceaccount.com","private_key":"-----BEGIN PRIVATE KEY-----\nSOME_KEY\n-----END PRIVATE KEY-----\n","private_key_id":"private_key_id","project_id":"project_id","token_uri":"https://accounts.google.com/o/oauth2/token","type":"service_account"}` ##### [`custom_claims`](https://www.krakend.io/docs/enterprise/authentication/gcloud/#custom_claims "Click to copy link to custom_claims") object Custom private claims that you can optionally add to an ID token. ##### [`s2s_auth_header`](https://www.krakend.io/docs/enterprise/authentication/gcloud/#s2s_auth_header "Click to copy link to s2s_auth_header") string The header name to use in service-to-service authentication. This is useful to honor the original Authorization header in case it’s needed by the backend (for example, CloudRun). Example: `"X-Serverless-Authorization"` Defaults to `""` Schema: [https://www.krakend.io/schema/v2.13/auth/gcp.json](https://www.krakend.io/schema/v2.13/auth/gcp.json "JSON schema definition") Example of configuration ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "endpoints": [ { "endpoint": "/gcp", "output_encoding": "no-op", "backend": [ { "host": [ "https://hello-76fewi6rca-uc.a.run.app" ], "url_pattern": "/someurl", "encoding": "no-op", "extra_config": { "auth/gcp": { "audience": "https://hello-76fewi6rca-uc.a.run.app" } } } ] } ] } ``` ### Additional resources The following resources at Google will help you during your implementation. - [Authentication at Google](https://cloud.google.com/docs/authentication) - [Authentication use cases](https://cloud.google.com/docs/authentication/use-cases#run-functions) - [How Application Default Credentials works](https://cloud.google.com/docs/authentication/application-default-credentials#attached-sa) - [Get an ID token](https://cloud.google.com/docs/authentication/get-id-token) - `auth/gcp` does this job for you - [Creating service-accounts with the CLI](https://cloud.google.com/sdk/gcloud/reference/iam/service-accounts/keys/create) ## Conflicting `Authorization` header For Google Cloud service-to-service authentication, **particularly when invoking services like Cloud Run**, GCP officially supports using the `X-Serverless-Authorization: Bearer TOKEN` header as an alternative to `Authorization` (as detailed in the GCP documentation: [https://docs.cloud.google.com/run/docs/authenticating/service-to-service#acquire-token)](https://docs.cloud.google.com/run/docs/authenticating/service-to-service#acquire-token%29). This provides a clean separation and avoids header conflicts, so the original request `Authorization` header is unmodified and can reach the backend services (CloudRun) safely. A configuration example: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "endpoints": [ { "endpoint": "/gcp", "output_encoding": "no-op", "backend": [ { "host": [ "https://hello-76fewi6rca-uc.a.run.app" ], "url_pattern": "/someurl-that-needs-original-authorization-header", "encoding": "no-op", "extra_config": { "auth/gcp": { "audience": "https://hello-76fewi6rca-uc.a.run.app", "s2s_auth_header": "X-Serverless-Authorization" } } } ] } ] } ``` ## Port limitations on Cloud Run When running KrakenD on Cloud Run, Google allowys to expose one port. Therefore if you want to expose additional services to the gateway such as custom metrics, Cloud Run won’t let you. In this case, you can create a new endpoint that internally uses as the `backend` host this additional port as `localhost:port`. For example, you can have something like this: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "extra_config": { "telemetry/metrics": { "collection_time": "60s", "proxy_disabled": false, "router_disabled": false, "backend_disabled": false, "endpoint_disabled": false, "listen_address": ":8090" } }, "endpoints": [ { "endpoint": "/mymetrics", "backend": [ { "host": ["localhost:8090"], "url_pattern": "/__stats" } ] } ] } ``` This works because your container still has all the necessary ports available, only that Cloud Run won’t expose them. Through KrakenD you can remap them. ## Example of configuring your service account on GCP Screenshots might vary Google Cloud interface keeps changing over time. The following screenshots are examples that might not match exactly what you see in the future. You can integrate GCP when KrakenD is hosted on GCP, or outside of it. Here is an orientation example of how to manually configure it. Log in to the [Google Cloud Console](https://console.cloud.google.com) and access the **Cloud Run** panel. If you don’t have any service yet, click Create New Service. Next, we selected the proposed test image, which is a hello-world application. ![Create a Service on Cloud Run](/images/documentation/gcp/gcp_create_service.png) Then check the “Require Authentication” option when creating the Service, so it cannot be freely accessed. ![Require authentication](/images/documentation/gcp/gcp_require_auth.png) We have named our first service “hello”, and it is now accessible under a newly generated URL and is displayed at the top. ![Create the hello service](/images/documentation/gcp/gcp_1_create_hello_service.png) We will now create the KrakenD configuration. The `host` and the `audience` match the new URL. ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "endpoints": [ { "endpoint": "/gcp", "output_encoding": "no-op", "backend": [ { "host": [ "https://hello-76fewi6rca-uc.a.run.app" ], "url_pattern": "/someurl", "encoding": "no-op", "extra_config": { "auth/gcp": { "audience": "https://hello-76fewi6rca-uc.a.run.app" } } } ] } ] } ``` Now you should decide whether KrakenD will run in Google or not. If we want to host KrakenD on Cloud Run, we will create an image containing the configuration and push it to the Container Registry. Now we will repeat the process of creating a service. This time we will add the `krakend` service, and we will select the image we have just pushed. In the creation screen, we do not set any authentication; the gateway will take care of this with the end user with JWT or similar. ![Create the krakend service](/images/documentation/gcp/gcp_create_service_unauthenticated.png) Depending on your Google Cloud configuration, the integration might immediately work because there could be a default service account allowing KrakenD to access the Hello service. Whether this is the case or not, we want to contemplate the possibility of hosting KrakenD outside, so we will dig a little further into the **Service accounts**. The **Service Accounts** are under the **“IAM & Admin”** section of our console. We are going to create a new Service Account for our project. ![Create the service account](/images/documentation/gcp/gcp_create_service_account.png) ![Create the service account](/images/documentation/gcp/gcp_create_service_account_details.png) If we are going to connect from outside Google, we will need to create the credentials. From the Service Account **Keys** tab we create new ones and select the JSON format. ![Create the service account](/images/documentation/gcp/gcp_create_service_account_keys.png) We can use this key from an external KrakenD to authenticate to GCP once we assign it to the Hello project. The last step is to assign the permissions to the hello application, so it allows KrakenD to interact with it. Select the Service Hello and add permissions by clicking on “Add Principal”. ![Create a Service on Cloud Run](/images/documentation/gcp/gcp_add_permissions.png) In the next screen, choose the Service Account you have created and select the role “Cloud Run Invoker” from the list. Click on Save. ![Create a Service on Cloud Run](/images/documentation/gcp/gcp_grant_access.png) The last step you have to take is to include the credentials we just created. The key you downloaded must be copied in your KrakenD directory and referenced as a `credentials_file` entry, or copy and paste it as `credentials_json` (or even better, use a template and inject it). Your Service is now ready. You should be able to access your Hello service from KrakenD. --- # [Auth0 Integration](https://www.krakend.io/docs/authorization/auth0/) Let’s start with the elephant in the room: when you face the challenge of creating a modern API that has to be scalable, secure, performant, and resilient, it might look like a daunting task. The good news is that combining products like Auth0 and KrakenD helps you solve this exact problem with an implementation that is straightforward. Auth0 can authenticate users and machines, and KrakenD knows your business APIs and validate them in a very efficient and convenient way. Both products work in perfect symbiosis to secure your APIs. ## The Auth0 - KrakenD integration JSON web tokens (JWT) are an open standard ([RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519)) that define a way for securely transmitting information between parties. JWT tokens are compact, secure (digitally signed), and have become an industry-standard used at internet scale. KrakenD supports [any system using this open standard](https://www.krakend.io/docs/authorization/jwt-validation/), and Auth0 has been its preferred SaaS integration. KrakenD offers integration with Auth0 at three different levels: 1. **End-users validation** for those using an app of any kind (3-legged auth), and then users provide a token inside a header or cookie. 2. **Machine-to-gateway communication**. Machine-to-machine, APIs talking to APIs, automated systems, and other uses of non-human communication. Machines have a **client id** and **secret** that allows them to request a JWT token to access protected resources. 3. **Gateway-to-machine communication**, for those cases where the gateway also needs to access protected backends requiring **client credentials**. ## The validation workflow Either if you are trying to protect your API from end-users or machine-to-machine access, the workflow is the same: ![KrakenD-Auth0 validation](/images/documentation/auth0/krakend-auth0-integration-workflow.png) - End-Users use their applications to log in to Auth0 who provides an access token for the session. - Machine-to-machine communication also uses a token from Auth0 after providing a client\_id and a client\_secret. - With the token generated by Auth0, the client passes it to KrakenD in each request inside an HTTP header or cookie - KrakenD authorizes or not the usage of the specific endpoint according to the rules you have configured. As KrakenD can validate the Auth0 signature by itself, it does not need to call an Auth0 server to validate the token every time. Instead, KrakenD queries Auth0 every 15 minutes (configurable) to ensure the signature has not rotated. ## Enabling JWT validation in KrakenD We will create a simple KrakenD configuration with a single endpoint `/auth0-protected`. This endpoint enables JWT validation and makes sure that no user or machine can access the resource without passing a valid token. Paste the following configuration into a `krakend.json` file. ```JSON { "version": 3, "timeout": "3s", "endpoints":[ { "endpoint": "/auth0-protected", "extra_config": { "auth/validator": { "alg": "SIGNING_ALGORITHM", "audience": ["AUDIENCE"], "jwk_url": "https://DOMAIN/.well-known/jwks.json" } }, "backend": [ { "host":["http://localhost:8080"], "url_pattern": "/__health" } ] }] } ``` From the configuration above, we will **replace the UPPERCASED values** with the settings shown in Auth0. Log in to your [Auth0 Dashboard](https://manage.auth0.com/) now to retrieve these values. Using the sidebar menu, you can find them as follows: - `DOMAIN`: Go to **Applications** -> *(Select your application)* -> **Domain** ![Applications view](/images/documentation/auth0/krakend-auth0-integration-application.png) - `SIGNING_ALGORITHM`: Go to **APIs** -> *(Select your API)* -> **Signing Algorithm** (e.g.: `RSA256`) - `AUDIENCE`: Go to **APIs** -> *(Select your API)* -> **Identifier** (e.g.: `http://api.example.com`) ![APIs view](/images/documentation/auth0/krakend-auth0-integration-api.png) The previous screenshots correspond to a configuration like the one it follows (showing only the replaced content): ```json { "alg": "RS256", "audience": ["http://api.example.com"], "jwk_url": "https://krakend-auth0-test.auth0.com/.well-known/jwks.json" } ``` That’s all you need for the basic configuration! You can expand the structure now to include checking specific roles, claims, etc. ### Testing the configuration The gateway will use itself as a backend. Make sure to replace the `backend` configuration with your API configuration. Let’s open a terminal now and from the folder where we create our `krakend.json` file we run: Start the gateway with your configuration  ``` $docker run --rm -v "$PWD:/etc/krakend" -p "8080:8080" krakend:2.13.8 ``` The API Gateway is listening in port `8080` now, let’s check the `/__health` endpoint, which is unprotected: Check the Gateway is alive  ``` $curl -iG http://localhost:8080/__health HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Sun, 23 May 2021 15:32:32 GMT Content-Length: 15 {"status":"ok"} ``` Good, we were able to connect. Now let’s try to access the `/auth0-protected` endpoint **without a token**: Unauthenticated request  ``` $curl -iG http://localhost:8080/auth0-protected HTTP/1.1 401 Unauthorized Date: Sun, 23 May 2021 15:35:52 GMT Content-Length: 0 ``` Here it is, that was expected. The gateway won’t let us in without a valid JWT token. If you check the KrakenD logs, you will also find a line `Error #01: Token not found`. Let’s get a valid token now. If you have a SPA or similar application with Auth0, login and get an access\_token. If you don’t have your application implemented yet, the easiest way to test this in the console is to create a new Application of the type **Machine to Machine** (but any other application will do). The Machine to Machine application provides a Quick Start with a `cURL` example like the following: Machine to Machine token request  ``` $curl --request POST \ --url https://krakend-auth0-test.auth0.com/oauth/token \ --header 'content-type: application/json' \ --data '{"client_id":"i1dXlGrQ9MBlWbmXw82jGLetLzw9O2cO","client_secret":"dvbpjLsBiiuz8m-4lMgHWInegE9sq2nUcJlgXqgYnyAoGFng66MQRicN9oPKaTIQ","audience":"http://api.example.com","grant_type":"client_credentials"}' ``` The response of the call above will provide you with an `access_token`. It looks like this: Access token example  ``` ${"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1ETkdNalUyTTBVM1JFUkZRVUV3T1VVelFVTXdRME5CTjBZMVJVWTBPRUl4TlRSRE0wSXhNdyJ9.eyJpc3MiOiJodHRwczovL2FsYmVydC10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJpMWRYbEdyUTlNQmxXYm1YdzgyakdMZXRMenc5TzJjT0BjbGllbnRzIiwiYXVkIjoiaHR0cDovL2FwaS5leGFtcGxlLmNvbSIsImlhdCI6MTYyMTc4NDQzMSwiZXhwIjoxNjIxODcwODMxLCJhenAiOiJpMWRYbEdyUTlNQmxXYm1YdzgyakdMZXRMenc5TzJjTyIsInNjb3BlIjoicmVhZDpwcm9maWxlIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.HM1H2GqRYhWlwqgxjzJqX35vKUkx0vjOo3KQyaD7Xv-6rUPMlOwFNQjTfX7jACNah3adxG4pmVMl-WNLOFXHOXp-L8V4CMZNQSFR424ggc7y-866IDix2kIIIFbA2WFwvgkW0HRYJy7H47Zrv1qa9-tlOecMkz5oGknHrENJiimesJ7OqYKU_3HVuLgk4OSfReEb3wAqh9FBadWpfuq_dpl_wZ1sCGGWVwO9kWFHyOoHwXq667hupwgET6AbKDKcwnziK4YPbedGajNit4DTsf7Giy-ULv-_MUktlvfVcPJvrV4tA8mt4CUc2Ef_mQHEvM_FY-yA-0qE3XSHqJqeFg","scope":"read:profile","expires_in":86400,"token_type":"Bearer"} ``` There are several fields in the JSON response, notice that `access_token` is what you have to pass to the gateway using a `Authorization: Bearer ACCESS_TOKEN`. Now you are ready to request protected resources from the gateway. In the Quick Start of the Machine to Machine application, you get this second call. Replace the sample URL with your gateway URL: Getting access to the protected endpoint  ``` $curl --request GET \ --url http://localhost:8080/auth0-protected \ --header 'authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1ETkdNalUyTTBVM1JFUkZRVUV3T1VVelFVTXdRME5CTjBZMVJVWTBPRUl4TlRSRE0wSXhNdyJ9.eyJpc3MiOiJodHRwczovL2FsYmVydC10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJpMWRYbEdyUTlNQmxXYm1YdzgyakdMZXRMenc5TzJjT0BjbGllbnRzIiwiYXVkIjoiaHR0cDovL2FwaS5leGFtcGxlLmNvbSIsImlhdCI6MTYyMTc4MzU4NywiZXhwIjoxNjIxODY5OTg3LCJhenAiOiJpMWRYbEdyUTlNQmxXYm1YdzgyakdMZXRMenc5TzJjTyIsInNjb3BlIjoicmVhZDpwcm9maWxlIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.r9GfLsbNqY3UX_1uLza7_lQv7WY--7tWLhWzn-p0bmXOV83leFOkwU5IH0HcxL3KcQaPcG6RFZ5rR1edoF5EJ9r7AXLldFLfzaSi7uYvktgNNTekDRNqg6cePlLCZUYki8Ccx2v2UQKHac4jTQPpx62qJVsg-FDst0au75OlMbXZXvzCRbjMIyXsH52lrv0zuxo22MX8wXZyljquUXUmJOiYHzvXd_I_2vB77IENzcVYWr3zwh7REqGpWSZDpjsIydV6WsV8g91YmwQIFwJQ7eSTOXpVjFko04JKPOcVzkVQpCPhWL_Uwq0QgOq3HoOujh8GCp4oKON2Xt9VS4Y2ag' {"status":"ok"} ``` That’s it! The `{"status": "ok"}` is the response you have from the `/__health` endpoint after being validated as a legitimate user. ### More complex workflows We have completed a basic setup that validates users using access tokens. With this simple configuration, we introduce a highly secure ecosystem that protects your business from undesired access. Now you might want to add **additional checks to your system** and take advantage of all the powerful features of Auth0. Some possibilities are: - Create **Roles** or **Auth Pipeline Rules** in the Auth0 panel and add them as a condition to accessing an endpoint in KrakenD. - Define scopes and validate them on KrakenD - Propagate claims to your backends. All of this is possible by [declaring more elements into the configuration](https://www.krakend.io/docs/authorization/jwt-validation/). ### Conclusion In this walk-through, we have shown how to securely protect your APIs using KrakenD and Auth0 in a few minutes. Combining both systems provides excellent benefits. For one side, Auth0 lets you forget about the arduous task of managing, auditing, securing, or updating standards that an **authentication** system requires. For the other, KrakenD handles the complex part of **authorizing** users, which requires a high level of security, such as dealing with fingerprints and cipher suites while using an approach that allows you to scale linearly ingesting any amount of traffic. Auth0 and KrakenD collaborate efficiently and without coupling during the run-time to reliably identify your users and let them see what they are supposed to. We are hoping that this reading has been helpful, and if you have any questions get in touch with us! --- # [AWS SigV4 Authentication for Service-to-Service Calls (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/authentication/aws-sigv4/) Use AWS SigV4 authentication in KrakenD when your backend services or APIs require signed AWS requests to authorize access. This method signs HTTP requests with AWS Signature Version 4, allowing KrakenD to securely authenticate to AWS services or custom APIs that understand SigV4 signed requests. You might need this component when: - When accessing AWS APIs or services that enforce SigV4 signing (e.g., presign an S3 link). - When your internal services require AWS IAM-based authentication for secure service-to-service communication. - When you want KrakenD to handle AWS SigV4 signing transparently without external tooling. This signing works with any Amazon HTTP service requiring SigV4. ## Configuration of AWS SigV4 Configuring AWS SigV4 authentication involves specifying the service and region details in KrakenD’s backend `extra_config` while the authentication is external (see below). KrakenD will automatically sign requests using configured credentials and SigV4 protocol before forwarding to the backend. ```json { "url_pattern": "/", "extra_config": { "auth/aws-sigv4": { "service": "execute-api", "region": "us-east-1", "assume_role_arn": "arn:aws:sts::{account ID}:assumed-role/{resource}", "sts_region": "us-west-1" } } } ``` The available properties are: ##### Fields of aws-sigv4 Authentication \* required fields ##### [`assume_role_arn`](https://www.krakend.io/docs/enterprise/authentication/aws-sigv4/#assume_role_arn "Click to copy link to assume_role_arn") string The Amazon Resource Name (ARN) of the role to assume. Example: `"arn:aws:sts::{account ID}:assumed-role/{resource}"` ##### [`debug`](https://www.krakend.io/docs/enterprise/authentication/aws-sigv4/#debug "Click to copy link to debug") boolean Enables debug logging for AWS Sigv4 signing process. Defaults to `false` ##### [`region`](https://www.krakend.io/docs/enterprise/authentication/aws-sigv4/#region "Click to copy link to region") * string The AWS region where the service is deployed. Example: `"us-east-1"` ##### [`service`](https://www.krakend.io/docs/enterprise/authentication/aws-sigv4/#service "Click to copy link to service") * string The name of the service in AWS you’d like to sign the request. Example: `"dynamodb"` ##### [`sts_region`](https://www.krakend.io/docs/enterprise/authentication/aws-sigv4/#sts_region "Click to copy link to sts_region") string The AWS region where the STS service is deployed. Example: `"us-west-1"` Schema: [https://www.krakend.io/schema/v2.13/auth/aws-sigv4.json](https://www.krakend.io/schema/v2.13/auth/aws-sigv4.json "JSON schema definition") ### Authentication and connectivity The KrakenD machine needs connectivity with your AWS account and the credentials to do so. There are several ways you can achieve this: - Copying your AWS credentials in the default file, `~/.aws/credentials` (and maybe an additional `~/.aws/config` and the env var `AWS_PROFILE` if you have several profiles) - Passing the environment variables with at least `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` (and maybe `AWS_REGION`) when starting KrakenD. - Having an IAM user with a policy and execution role that lets you invoke the function from the machine #### Authentication examples Mounting an existing `.aws` directory with the credentials in it (notice that the home of the Docker user is `krakend`): Mounting the AWS profile  ``` $docker run --rm -it -p "8080:8080" \ -e "AWS_PROFILE=default" \ -v "/home/user/.aws:/home/krakend/.aws:ro" \ -v "$PWD:/etc/krakend" krakend/krakend-ee:2.13.6 ``` Passing the credentials directly: Passing credentials as env vars  ``` $docker run --rm -it -p "8080:8080" \ -e "AWS_ACCESS_KEY_ID=XXX" \ -e "AWS_SECRET_ACCESS_KEY=XXX" \ -e "AWS_REGION=eu-west-1" \ -v "$PWD:/etc/krakend" krakend/krakend-ee:2.13.6 ``` --- # [Descope Authorization](https://www.krakend.io/docs/authorization/descope/) Securing your APIs and underlying microservices is crucial in modern development. Descope enables you to add advanced authentication capabilities to your KrakenD endpoints efficiently. [Descope](https://www.descope.com/) is a passwordless authentication and user management service designed for developers. KrakenD integrates with Descope via the [JWT validation](/docs/authorization/jwt-validation/) component. ## Descope integration JSON web tokens (JWT) are an open standard ([RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519)) that define a way for securely transmitting information between parties. JWT tokens are compact, secure (digitally signed), and have become an industry-standard used at internet scale. KrakenD supports [any system using this open standard](https://www.krakend.io/docs/authorization/jwt-validation/), including Descope. ## The validation workflow Whether you are trying to protect your API from end-users or machine-to-machine access, the workflow is the same: - End-Users use their applications to log in to Descope who provides an access token for the session. - Machine-to-machine communication also uses a token from Descope after providing a client\_id and a client\_secret. - With the token generated by Descope, the client passes it to KrakenD in each request inside an HTTP header or cookie - KrakenD authorizes or not the usage of the specific endpoint according to the rules you have configured. As KrakenD can validate the Descope signature by itself, it does not need to call the Descope server to validate the token every time. Instead, KrakenD queries Descope every 15 minutes (configurable) to ensure the key has not rotated. ## Protecting endpoints with Descope tokens We will create a simple KrakenD configuration with a single endpoint `/descope-protected`, ensuring only users with valid tokens can access it. Create a `krakend.json` file and add the following configuration: ```JSON { "version": 3, "timeout": "3s", "endpoints":[ { "endpoint": "/descope-protected", "extra_config": { "auth/validator": { "alg": "RS256", "jwk_url": "https://api.descope.com/v2/keys/" } }, "backend": [ { "host":["http://localhost:8080"], "url_pattern": "/__health" } ] }] } ``` Replace \`api.descope.com\` with your \[custom domain](https://docs.descope.com/how-to-deploy-to-production/custom-domain) or your respective localized base url. That’s all you need for the basic configuration! You can expand the structure now to include checking specific roles, claims, etc. ### Testing the configuration From the folder where we create our `krakend.json` file, start the gateway with: Start the gateway with your configuration  ``` $docker run --rm -v "$PWD:/etc/krakend" -p "8080:8080" krakend:2.13 ``` Verify the gateway is running by checking the unprotected /\_\_health endpoint: Check the Gateway is alive  ``` $curl -iG http://localhost:8080/__health HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Sun, 23 May 2021 15:32:32 GMT Content-Length: 15 {"status":"ok"} ``` Now let’s try to access the `/descope-protected` endpoint **without a token**: Unauthenticated request  ``` $curl -iG http://localhost:8080/descope-protected HTTP/1.1 401 Unauthorized Date: Wed, 29 Jan 2025 00:13:29 GMT Content-Length: 0 ``` Since no token is provided, KrakenD correctly denies access. If you check the KrakenD logs, you will also find a line `Error #01: Token not found`. Let’s get a valid M2M token now. Log in to your Descope Console and [create an access key](https://docs.descope.com/m2m-access-keys/m2mauth#setting-up-access-keys). Using your Descope Project Id and Access Key, you can then run the followin `cURL` command to exchange the access key for a JWT: Machine to Machine token request  ``` $curl -X POST "https://api.descope.com/v1/auth/accesskey/exchange" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " ``` The response of the call above will provide you with a `sessionJwt`. Now you are ready to request protected resources from the gateway. Replace the sample URL with your gateway URL and `` with the `sessionJwt` retrieved in the previous step: Getting access to the protected endpoint  ``` $curl --request GET \ --url http://localhost:8080/descope-protected \ --header 'authorization: Bearer ' {"status":"ok"} ``` That’s it! The `{"status": "ok"}` is the response you have from the `/descope-protected` endpoint after being validated as a legitimate user. ### Advanced configurations We have completed a basic setup that validates users using access tokens. With this simple configuration, we introduce a highly secure ecosystem that protects your business from undesired access. Now you might want to add **additional checks to your system** and take advantage of all the powerful features of Descope. Some possibilities are: - Create [**Roles** in Descope](https://docs.descope.com/authorization) and add them as a condition to accessing an endpoint in KrakenD. - Customize JWTs with [Descope JWT templates](https://docs.descope.com/project-settings/jwt-templates) for Users or Access Keys - Propagate user claims to your backend services. For more details, see [KrakenD’s JWT Validation documentation](/docs/authorization/jwt-validation/). ### Conclusion Integrating Descope with KrakenD enhances security while maintaining flexibility. You can designate protected and public endpoints, ensuring controlled access to your APIs. Descope’s seamless integration with KrakenD streamlines authentication and improves user experience, making it an excellent choice for secure API management --- # [Keycloak Authorization](https://www.krakend.io/docs/authorization/keycloak/) The Keycloak integration allows one of the many possible OpenID Connect different security options available in KrakenD. [Keycloak](https://www.keycloak.org/) is an Open Source **Identity and Access Management** project, sponsored by RedHat. The KrakenD configuration for Keycloak relies internally on the [JWT validation](/docs/authorization/jwt-validation/) component. ## Keycloak integration JSON web tokens (JWT) are an open standard ([RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519)) that define a way for securely transmitting information between parties. JWT tokens are compact, secure (digitally signed), and have become an industry-standard used at internet scale. KrakenD supports [any system using this open standard](https://www.krakend.io/docs/authorization/jwt-validation/), including Keycloak. Either if you are trying to protect your API from end-users or machine-to-machine access, the workflow is the same: ![](/images/documentation/keycloak/krakend-keycloak-integration-workflow.png "KrakenD-Keycloak validation") - End-Users use their applications to log in to Keycloak who provides an access token for the session. - Machine-to-machine communication also uses a token from Keycloak after providing a client\_id and a client\_secret. - With the token generated by Keycloak, the client passes it to KrakenD in each request inside an HTTP header or cookie - KrakenD authorizes or not the usage of the specific endpoint according to the rules you have configured. As KrakenD can validate the Keycloak signature by itself, it does not need to call the Keycloak server to validate the token every time. Instead, KrakenD queries Keycloak every 15 minutes (configurable) to ensure the key has not rotated. ## Protecting endpoints with Keycloak tokens We will create a simple KrakenD configuration with a single endpoint `/keycloak-protected`. This endpoint enables JWT validation and makes sure that no user or machine can access the resource without passing a valid token. Paste the following configuration into a `krakend.json` file. ```JSON { "version": 3, "timeout": "3s", "endpoints":[ { "endpoint": "/keycloak-protected", "extra_config": { "auth/validator": { "alg": "RS256", "jwk_url": "http://KEYCLOAK:8080/realms/master/protocol/openid-connect/certs", "disable_jwk_security": true } }, "backend": [ { "host":["http://localhost:8080"], "url_pattern": "/__health" } ] }] } ``` From the configuration above, you have to **replace the `KEYCLOAK:8080` value** to match your installation. If you don’t use the default realm `master` then change it also in the `jwk_url`. Make sure that the JWK URL exists, some set ups do not have the `/auth/` part in the URL. That’s the minimum configuration you need to protect your API from being accessed by someone who has not a valid token from Keycloak. Also notice there is a flag `"disable_jwk_security": true`, this is necessary when we are accessing our JWK address with http instead of https. Now you might want to add **additional checks to your system** and take advantage of all the powerful features of Keycloak. This is possible by [declaring more elements into the configuration](https://www.krakend.io/docs/authorization/jwt-validation/). ## Keycloak demo with KrakenD The repository “[Demo Microservices with keycloak and krakend](https://gitlab.com/bensiewert/demo-microservices-with-keycloak-and-krakend)” by Ben Siewert, is a demonstration project integrating microservices with Keycloak for authentication and KrakenD as an API gateway. It consists in two Node microservices fronted and protected by KrakenD. The identities and tokens are taken from Keycloak and KrakenD validates the token to accept or deny access to the services behind. --- # [NTLM authentication (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/authentication/ntlm/) NTLM (NT Lan Manager) is a suite of Microsoft security protocols used for authentication, integrity, and confidentiality of network communications. It is primarily used for authentication on Windows-based systems but can also be used in other environments. KrakenD NTLM integration does the user impersonation and acts as a client in the communication gateway to the backend. The end-user authentication is independent, and you can offer standard JWT tokens, no authentication, or another authentication option. Microsoft has deprecation plans for NTLM **Microsoft strongly recommends moving away from this protocol and adopting more modern and secure authentication mechanisms such as OAuth**. NTLM has several security weaknesses and limitations, which can make it **vulnerable to various types of attacks**. However, KrakenD supports internal-facing communications using NTLM for legacy systems, as it is still widely used in many Windows-based systems and applications and remains an essential part of the Windows security ecosystem. **KrakenD supports NTLMv2** and no key exchange or encryption. It only supports Unicode (UTF16LE) encoding of protocol strings, no OEM encoding. Internally uses the official Azure Go library. NTLM authentication is used in various Microsoft applications so that you can integrate KrakenD primarily with the following: - **Microsoft Dynamics** and other ERP systems. - **Microsoft SharePoint** (collaboration platform used for document management) NTLM is also used in Windows Server, Microsoft Active Directory, Exchange Server, SQL Server, or IIS, to mention a few others. ## How NTLM works There are two steps in the communication of service consumption. 1. The end-user requests KrakenD content 2. KrakenD retrieves it from an NTLM-protected server ![NTLM authentication](/images/documentation/krakend-ntlm-authentication.png) For the first step (user to KrakenD), you can use JWT tokens for instance, or another authentication option, but we do not enable end-user authentication with NTLM. The NTLM integration covers the second step, which authenticates KrakenD on local and remote servers like Microsoft Dynamics. As you can see, both steps are independent, and you can choose which mechanism protections suit you best. NTLM authentication works by using a challenge-response mechanism. When KrakenD logs on to a Windows-based system or accesses a network resource with the provided credentials, the system challenges the connection. The challenge is a random string of characters that KrakenD must encrypt and send back to the system. The system then checks the response against its copy of the user’s password hash to verify the user’s identity. ### Security considerations From a security perspective, NTLM could be more secure, and as it happens with Basic Authentication, you should refrain from using it in new developments. Its purpose is to cover legacy implementations. NTLMv1 is an ancient protocol, and KrakenD does not support it. NTLMv2 is a newer, more secure protocol (comparatively) that includes additional security features such as mutual authentication and message integrity checking. Still, neither of them is a safe option nor recommendable. ## NTLM configuration The NTLM configuration works at the `backend` level, and you have the following options available: ##### Fields of NTLM Authentication \* required fields ##### [`password`](https://www.krakend.io/docs/enterprise/authentication/ntlm/#password "Click to copy link to password") * string The password you will use, in clear text. Example: `"myp4ssw0rd"` ##### [`user`](https://www.krakend.io/docs/enterprise/authentication/ntlm/#user "Click to copy link to user") * string The username you will send as NTLM authentication user. Example: `"krakendclient"` Schema: [https://www.krakend.io/schema/v2.13/auth/ntlm.json](https://www.krakend.io/schema/v2.13/auth/ntlm.json "JSON schema definition") Below there is a functional example you can test. It should work unless the external site stops responding. ```json { "version": 3, "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "endpoints": [ { "endpoint": "/ntlm", "output_encoding": "no-op", "backend": [ { "url_pattern": "/HTTPAuth/", "host": [ "https://authenticationtest.com" ], "extra_config": { "auth/ntlm": { "user": "user", "password": "pass" } } } ] } ] } ``` --- # [Traffic Management Overview](https://www.krakend.io/docs/throttling/) Traffic management refers to the practice of **monitoring, controlling, and optimizing the flow of requests** to and from an API. It aims to prevent abuse by limiting the volume of requests from individual clients or groups, regulate the flow of traffic, ensure fair usage, and provide predictable API performance. KrakenD offers several traffic management features, ranging from rate-limiting to advanced techniques like circuit breakers and bot detection. These features can be configured independently or combined for a holistic traffic management strategy. You can **combine multiple traffic management features** to address complex use cases. You don’t have to choose one or the other, but implement those that complete your needs ## Rate-Limiting Rate-limiting controls the number of requests users or systems can send. KrakenD allows you to throttle both the traffic of end-users and the traffic of KrakenD against your services. The rate limits mainly cover the following purposes: - Avoid stressing or flooding your backend services with massive requests (proxy rate limit) - Establish a quota of usage for your exposed API (router rate limit) - Create a simple QoS strategy for your API Our approach to rate-limiting has many variants: - [Endpoint Rate Limiting](/docs/endpoints/rate-limit/): Sets the maximum throughput all connected users can have against specific endpoints (stateless). - [Client Rate Limiting](/docs/endpoints/rate-limit/): Sets the maximum throughput each end-user has to specific endpoints (stateless). - [Proxy Rate Limiting](/docs/backends/rate-limit/): Sets the maximum throughput KrakenD can have between an endpoint and your backend services (stateless). Rate-Limiting features implement the [Spike Arrest](/docs/throttling/spike-arrest/), a mechanism triggered after exhausting the burst capacity of the rate-limit, ensuring that a minimum time interval occurs between consecutive requests, helping prevent sudden traffic spikes that could destabilize the system. In addition, on the Enterprise edition there is: - [Service Rate Limiting (stateful, Redis)](/docs/enterprise/throttling/global-rate-limit/): Sets the maximum throughput users can have on a KrakenD cluster, backed on Redis. All machines centralize the counters in a database. - [Service Rate Limiting (stateless)](/docs/enterprise/service-settings/service-rate-limit/): Sets the maximum throughput users can have to a KrakenD instance, but each machine counts its own traffic and no database. - [Endpoint Rate Limiting (stateful)](/docs/enterprise/throttling/endpoint-redis-rate-limit/): Sets the maximum throughput users can have to specific endpoints, backed on Redis. - [Tiered Rate Limiting (stateless)](/docs/enterprise/service-settings/tiered-rate-limit/#stateless-tiered-rate-limit): Same but the counters ara managed independently by each machine and no database. - [Tiered Rate Limiting (stateful, Redis)](/docs/enterprise/service-settings/tiered-rate-limit/#stateful-redis-backed-tiered-rate-limit): Sets the maximum throughput users can send depending on their tier/plan at a cluster level, backed on Redis. ## Circuit Breaker [Circuit Breakers](/docs/backends/circuit-breaker/) are automatic protection mechanisms that help prevent cascading failures in your system by temporarily halting requests to struggling backend services. A simplified diagram would be: ![circuit-breaker-overview.mmd diagram](/images/documentation/diagrams/circuit-breaker-overview.mmd.svg) The circuit breaker watches the state of the connections with your backend(s), with a tolerance to consecutive failures that you define. When the number of failures are reached, it stops all the interaction with the backend for a few seconds (timeout defined by you), and returns errors to clients. After the defined timeout, it tests the system again to see if it is already healthy or if it continues to fail. See the [Circuit Breaker](/docs/backends/circuit-breaker/) for more details. ## Bot Detection [Bot detection](/docs/throttling/botdetector/) identifies and blocks malicious bots that scrape data, spam endpoints, or conduct other abusive behaviors. It allows you to set your own rules for bot detection, and they are based on regular expressions. ![bot detector](/images/krakend-botdetector.png) ## Geofencing With [Geofencing Enterprise](/docs/enterprise/endpoints/geoip/) , you can restrict API usage based on geographical locations, such as specific countries or cities. This feature, combined with [Security Policies](/docs/enterprise/security-policies/playbook/#user-is-from-a-specific-country), enhances regional access control. When [GeoIP](/docs/enterprise/endpoints/geoip/) is enabled, the requests reaching to your backends have an additional enrichment of location metadata as well. ## IP Filtering [IPFiltering Enterprise](/docs/enterprise/throttling/ipfilter/) allows you to block traffic from specific IP addresses, adding an extra layer of security. --- # [Load Balancing and Throttling](https://www.krakend.io/docs/throttling/load-balancing/) The natural placement of an API gateway is between API consumers and your services. When we talk about load balancing, we can refer to both sides of the gateway: **ingress traffic** (user to gateway) or **egress traffic** (gateway to services). The different load balancer placements you can have are illustrated in the image above. ## Balancing ingress traffic (to KrakenD) We recommend having a few containers or servers in production to have high availability. In addition, you should place an **external balancer** to serve as the single point of contact for clients to distribute incoming traffic to all KrakenD nodes. ![load-balancing-to-krakend.mmd diagram](/images/documentation/diagrams/load-balancing-to-krakend.mmd.svg) Cloud providers (and on-prem solutions) offer a variety of products for balancing, like Network balancers, Application balancers, CDN balancers, software balancers, etc. The choice will depend on your needs. **KrakenD does not need any configuration** to receive ingress traffic. ## Balancing egress traffic (to upstream) KrakenD connects to your services using the balancing strategy of your choice, be a **Round Robin** algorithm, a **weighted balancing** connection, or use an **external load balancer** or Kubernetes service instead. The load balancer can work for internal and external services simultaneously, and it’s irrelevant to KrakenD the physical location or networking as long as it can reach the destination. When writing the configuration, you are implicitly setting the **load balancing strategy** on a per-backend basis, depending on what you write in the `host` list and `sd` (service discovery) settings. It means that you can have an endpoint that connects to a set of balanced backends while you have another endpoint that relates to an externally-balanced service. In essence, there are two relevant entries on `backend` you have to be aware of in terms of balancing: ##### Fields of `"backend"` \* required fields ##### [`host`](https://www.krakend.io/docs/throttling/load-balancing/#host "Click to copy link to host") array of strings An array with all the available hosts to [load balance](https://www.krakend.io/docs/throttling/load-balancing/#balancing-egress-traffic-to-upstream) 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 entry in the array with the service name/balancer address. Defaults to the `host` declaration at the configuration’s root level, and the service fails starting when there is none. ##### [`sd`](https://www.krakend.io/docs/throttling/load-balancing/#sd "Click to copy link to sd") The [Service Discovery](https://www.krakend.io/docs/backends/service-discovery/) system to resolve your backend services. Defaults to `static` (no external Service Discovery). Use `dns` to use DNS SRV records. Use `dns-shared` to share the DNS resolution between backends using the same hosts. Possible values are: `"static"` , `"dns"` , `"dns-shared"` Defaults to `"static"` Schema: [https://www.krakend.io/schema/v2.13/backend.json](https://www.krakend.io/schema/v2.13/backend.json "JSON schema definition") KrakenD’s egress load balancer acts at the [backend level](/docs/backends/) and manages the connections to the backends from within the gateway. ### Delegated egress load balancing When you don’t want the gateway to do any balancing for you because either: - You have a single host - The operative system is able to do it, or it’s consuming a DNS that does the balancing - You are connecting to a Kubernetes service - Your infrastructure provides a balancer to your services ![load-balancing-egress-delegated.mmd diagram](/images/documentation/diagrams/load-balancing-egress-delegated.mmd.svg) In these cases, you only need to add a single element in the `host` array with the desired service/networking address: ```json { "sd": "static", "host": ["http://service-or-load-balancer"] } ``` The `sd` entry uses its default value, which can be omitted in the configuration, but it’s shown above to demonstrate the relationship. ### Egress load balancer using Round Robin When you want the gateway to do the balancing to connect to one or more servers inside or outside your network. The internal load balancer sends requests to the backends in a **Round Robin fashion**, and you should expect more or less an equivalent weight and number of hits on each backend in the list. ![load-balancing-egress-internal.mmd diagram](/images/documentation/diagrams/load-balancing-egress-internal.mmd.svg) ```json { "backend": [ { "url_pattern": "/foo", "host": [ "https://instance-01", "https://instance-02", "https://instance-03", ] } ] } ``` To add more instances in the balancing pool, you only need to add them under the `host` list. With the configuration above, when looking at the overall traffic received by each server, you’ll see that each received around 33.33% of the requests. This is because the `host` list treats all the servers with equal weight. The host list does not have any monitoring by KrakenD at this level and **it won’t remove entries from it** if the backend fails. To control failures in your backends, you should use the [Circuit Breaker](/docs/backends/circuit-breaker/). ### Egress using poor man’s weighted load balancing By repeating entries in the `host`, you can change the traffic distribution. Knowing that each item in the list receives an equal amount of traffic, the following example illustrates the *poor man’s weighted balancing*: 75% of the traffic to instance-02 and 25% to instance-01. ```json { "url_pattern": "/foo", "host": [ "https://instance-01", "https://instance-02", "https://instance-02", "https://instance-02" ] } ``` ### Egress using weighted load balancing (DNS SRV) A more sophisticated way of doing **weighted load balancing** is to use an external service that dynamically sets the list of available hosts along with its weight using `DNS SRV` (see [Service Discovery](/docs/backends/service-discovery/)). To use it, add a configuration as follows: ```json { "backend": [ { "url_pattern": "/foo", "sd": "dns", "host": [ "api-catalog.service.consul.srv" ], "disable_host_sanitize": true } ] } ``` The `SRV` record provides the **hostname, port, priority, and weight** that KrakenD uses to balance. KrakenD reads these values **every 30 seconds** and generates an internal balancing list. The balancing list honors the distribution described in the `SRV` records. Nevertheless, KrakenD will use only the records with the **lower priority**. So, for instance, if you have five servers with priority `0` and another with priority `2`, the latter won’t be included in the balancing. As per the weights, KrakenD distributes the traffic in the proportion they represent. To be memory and space-efficient, KrakenD compacts and normalizes the final list of weights if needed. It’s essential to be aware that KrakenD will remove servers with a weight **orders of magnitude inferior** (under 1% of the total representation) from the final list as they are negligible. Some examples on space optimization and removal of neglectable items: - `SRV` passes the weights of 3 servers with values `[100 500 1000]`, and KrakenD builds a list `[1 5 10]`. - `SRV` passes `[25 10000 1000]` and KrakenD compacts it as `[0 10 1]`. The server with a weight of `25` is removed as it is vastly inferior to the rest (0,2% weight). - `SRV` passes `[25 1000 10000 65535]` becomes `[0 1 13 85]`. Again, `25` drops. The rest are converted to a lower value with the same distribution proportion. --- # [Circuit Breaker](https://www.krakend.io/docs/backends/circuit-breaker/) The **Circuit Breaker** is a straightforward **state machine** in the middle of the request and response that monitors all your backend failures. In the image above you can see a simplified version of its behavior. When backends fail to succeed for a number of consecutive times, the circuit breaker will prevent sending more traffic to a failing backend alleviating its pressure under challenging conditions. When KrakenD demands more throughput than your API stack can deliver properly, the Circuit Breaker mechanism will detect the failures and prevent stressing your servers by not sending requests that are likely to fail. It is also helpful for dealing with network and other communication problems by preventing too many requests from dying due to timeouts, etc. It is important to remark that the number of maximum errors are **consecutive errors**, and **not the total** of errors in the period. This approach works better when your traffic is variable, as it’s based on a **probabilistic pattern** and it’s not affected by the volume of traffic you might have. A must have The Circuit Breaker is an **automatic protection measure** for your API stack and **avoids cascade failures**, keeping your API responsive and resilient. It has a small consumption of resources. Try to implement it always. You might start with a big number of errors if you hesitate. ## Circuit breaker configuration The Circuit Breaker is available in the namespace `qos/circuit-breaker` inside the `extra_config` key of every `backend`. The following configuration is an example of how to add circuit breaker capabilities to a backend: ```json { "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: ##### Fields of Circuit Breaker \* required fields ##### [`interval`](https://www.krakend.io/docs/backends/circuit-breaker/#interval "Click to copy link to interval") * integer Time window where the errors count, in seconds. ##### [`log_status_change`](https://www.krakend.io/docs/backends/circuit-breaker/#log_status_change "Click to copy link to log_status_change") boolean Whether to log the changes of state of this circuit breaker or not. Defaults to `false` ##### [`max_errors`](https://www.krakend.io/docs/backends/circuit-breaker/#max_errors "Click to copy link to max_errors") * integer The **CONSECUTIVE** (not total) number of errors within the `interval` window to consider the backend unhealthy. All HTTP status codes different than `20x` are considered an error, except for the `no-op` encoding that does not evaluate status codes and is limited to connectivity/networking, security and component errors. See the definition of error below. ##### [`name`](https://www.krakend.io/docs/backends/circuit-breaker/#name "Click to copy link to name") string A friendly name to follow this circuit breaker’s activity in the logs. Example: `"cb-backend-1"` ##### [`timeout`](https://www.krakend.io/docs/backends/circuit-breaker/#timeout "Click to copy link to timeout") * integer For how many seconds the circuit breaker will wait before testing again if the backend is healthy. Schema: [https://www.krakend.io/schema/v2.13/qos/circuit-breaker.json](https://www.krakend.io/schema/v2.13/qos/circuit-breaker.json "JSON schema definition") ## How the Circuit Breaker works It’s easy to picture the state of the circuit breaker as an electrical component, where an open circuit means no flow of electricity between the ends, and a closed one normal flow: ![](/images/documentation/circuit-breaker.png "Circuit Breaker states") The Circuit Breaker starts with the `CLOSED` state, meaning the electricity can flow to the backends as they are considered healthy (*innocent until proven guilty*). Then the component watches the state of the connections with your backend(s), with a tolerance to **consecutive failures** (`max_errors`) during a time interval (`interval`). it stops all the interaction with the backend for the next N seconds (the `timeout`). We call this state `OPEN`. After waiting for this time window, the state changes to `HALF-OPEN` and allows **a single connection** to pass and **test the system** again. At this stage: - If the test connection fails, the state returns to “open” and the circuit breaker will wait N seconds again to test it again. - If it succeeds, it will return to the “closed “state, and the system is considered healthy. This is the way the states change: ![circuit-breaker-transitions.mmd diagram](/images/documentation/diagrams/circuit-breaker-transitions.mmd.svg) - `CLOSED`: In the initial state, the system is healthy and sending connections to the backend. - `OPEN`: When the consecutive number of errors from the backend (`max_errors`) is **exceeded**, the system changes to `OPEN`, and no further connections are sent to the backend. The system will stay in this state for N seconds ( where N = `timeout`). - `HALF-OPEN`: After the timeout, it changes to this state and allows **one connection** to pass (the test). If the connection succeeds, the state changes to `CLOSED`, and the backend is considered to be healthy again. But if it fails, it switches back to `OPEN` for another timeout. ### Definition of error When the circuit breaker counts the number of consecutive `max_errors`, an error could be anything that prevents having a successful connection with the service and completing the work. `no-op` endpoints do not check HTTP status codes Because a `no-op` does not evaluate the response status code, the circuit breaker does not see the response status code of the backend and the errors are limited to the following list below. An error could be any of the following: - Network or connectivity problems - Security policies - Timeouts - Components in the list returning errors or having issues: - Proxy rate limit (`qos/ratelimit/proxy`) - Lua backend scripts (`modifier/lua-backend`) - CEL in the backend (`validation/cel`) - Lambda (`backend/lambda`) - AMQP or PubSub issues #### For endpoints that DO NOT use `no-op` In addition, only when you work with `json`, or **any other encoding different than `no-op`** , the gateway also takes into account the HTTP responses back from the backend and marks as errors: - Status codes different than `200` or `201` (including client credentials) - Decoding issues - Martian issues --- # [HTTP Circuit Breaker (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/http-circuit-breaker/) Both this **HTTP Circuit Breaker** and the regular [Circuit Breaker](/docs/enterprise/backends/circuit-breaker/) are **state machines** that monitor failures. Once they see more consecutive errors than expected within the defined time window, they prevent further traffic from reaching the backend and enter a cooldown state. The HTTP Circuit Breaker is an **automatic protection measure** for your API stack and **avoids cascade failures**, keeping your API responsive and resilient. It has a low resource consumption. Try to implement it consistently. ## Difference between the Circuit Breaker and the HTTP Circuit Breaker The HTTP Circuit Breaker (`qos/circuit-breaker/http`) behaves mostly like a regular [Circuit Breaker](/docs/enterprise/backends/circuit-breaker/) (`qos/circuit-breaker`), but it has a few differences: - Allows you to **customize the HTTP status codes** considered errors. - Can read HTTP status code **even with no-op** and other encodings - It is executed at a different stage Do not run both You should not run both types of Circuit Breakers in the same backend as they would compete with each other. On the common part, the HTTP Circuit Breaker uses the same settings and adds the status code list, and also considers other errors, such as **connectivity issues or timeouts**. Read [How the Circuit Breaker works](https://www.krakend.io/docs/enterprise/backends/circuit-breaker/#how-the-circuit-breaker-works) to better understand this pattern. ## HTTP Circuit breaker configuration The HTTP Circuit Breaker is available in the namespace `qos/circuit-breaker/http` inside the `extra_config` key of every `backend`. The following configuration is an example of how to add circuit breaker capabilities to a backend. While the regular Circuit Breaker will consider an error anything different than a 200 or 201 status code, you can extend this list using the `valid_status_codes` array, which is the most important configuration: ```json { "endpoints": [ { "endpoint": "/myendpoint", "method": "GET", "backend": [ { "host": [ "http://api.example.com" ], "url_pattern": "/mybackend-endpoint", "extra_config": { "qos/circuit-breaker/http": { "valid_status_codes": [ 200, 201, 400, 404 ], "@comment": "Three errors IN A ROW within a minute, stop sending traffic!", "interval": 60, "timeout": 10, "max_errors": 3, "name": "cb-myendpoint-1", "log_status_change": true } } } ] } ] } ``` The attributes available for the configuration are: ##### Fields of Circuit Breaker \* required fields ##### [`interval`](https://www.krakend.io/docs/enterprise/backends/http-circuit-breaker/#interval "Click to copy link to interval") * integer Time window where the errors count, in seconds. ##### [`log_status_change`](https://www.krakend.io/docs/enterprise/backends/http-circuit-breaker/#log_status_change "Click to copy link to log_status_change") boolean Whether to log the changes of state of this circuit breaker or not. Defaults to `false` ##### [`max_errors`](https://www.krakend.io/docs/enterprise/backends/http-circuit-breaker/#max_errors "Click to copy link to max_errors") * integer The **CONSECUTIVE** (not total) number of errors within the `interval` window to consider the backend unhealthy. All HTTP status codes different than `20x` are considered an error, except for the `no-op` encoding that does not evaluate status codes and is limited to connectivity/networking, security and component errors. See the definition of error below. Example: `5` ##### [`name`](https://www.krakend.io/docs/enterprise/backends/http-circuit-breaker/#name "Click to copy link to name") string A friendly name to follow this circuit breaker’s activity in the logs. Example: `"cb-backend-1"` ##### [`timeout`](https://www.krakend.io/docs/enterprise/backends/http-circuit-breaker/#timeout "Click to copy link to timeout") * integer For how many seconds the circuit breaker will wait before testing again if the backend is healthy. This number of seconds can also be read as the **minimum cooldown** of the backend interaction. Example: `10` ##### [`valid_status_codes`](https://www.krakend.io/docs/enterprise/backends/http-circuit-breaker/#valid_status_codes "Click to copy link to valid_status_codes") * array of integers A list of HTTP status codes that will be considered successful responses. Any response with a status code not in this list will be counted as an error by the circuit breaker. Example: `[200,201,404]` Schema: [https://www.krakend.io/schema/v2.13/qos/circuit-breaker-http.json](https://www.krakend.io/schema/v2.13/qos/circuit-breaker-http.json "JSON schema definition") **IMPORTANT**: The number of maximum errors reflects **consecutive errors**, and **not the aggregated number** of errors in the period. This approach works better when your traffic is variable, as it’s based on a **probabilistic pattern** and isn’t affected by traffic volume. ## Limitations with `backend/http` The HTTP Circuit Breaker is a subtype of the `backend/http`, that means that if the HTTP Circuit Breaker faces a failing status code, any configuration related to [custom detailed errors](/docs/enterprise/backends/detailed-errors/) is ignored. --- # [Service Rate Limiting to Control API Usage (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/) The service rate limit feature allows you to set the **maximum requests per second** a user or group of users can do to KrakenD and works analogously to the [endpoint rate limit](/docs/endpoints/rate-limit/). There are two different strategies to set limits that you can use, simultaneously or individually: - **Service rate-limit**: Defines the rate-limit that **all users** of your API can do together, sharing the same counter. For instance, you might want to limit the interaction from users to KrakenD to 10,000 requests/second to avoid a possible DDoS propagating to your backend services. - **Client rate-limit**: Rate-limits what an individual user can do against all endpoints. For instance, a personal JWT token can make 100 requests/second to the API. Both types keep **in-memory** an updated [Token Bucket](/docs/throttling/token-bucket/) expressing the remaining requests for a user or group of users. For additional types of rate-limiting see the [Traffic management overview](/docs/throttling/). ## Service rate-limiting (`max_rate`) The service rate limit acts on the number of simultaneous transactions KrakenD allows to process. This type of limit protects the service from all customers. In addition, these limits mitigate abusive actions such as rapidly writing content, aggressive polling, or excessive API calls. It consumes an insignificant amount of memory as it only needs **one counter for the whole system**. When the users connected to an endpoint together exceed the `max_rate`, KrakenD starts to reject connections with a status code `503 Service Unavailable` and enables a [Spike Arrest](/docs/throttling/spike-arrest/) policy ## Service client rate-limiting (`client_max_rate`) The client or user rate limit applies to an individual user to all endpoints. Each endpoint can additionally have [sub-limit rates](/docs/endpoints/rate-limit/), but all users are subject to the same rate. The `client_max_rate` tracks one counter per different client. When a single user connected to an endpoint exceeds their `client_max_rate`, KrakenD starts to reject connections with a status code `429 Too Many Requests` and enables a [Spike Arrest](/docs/throttling/spike-arrest/) policy ## Service rate-limiting configuration The configuration allows you to use both types of rate limits at the same time: ```json { "version": 3, "extra_config": { "qos/ratelimit/service": { "max_rate": 50, "client_max_rate": 5, "strategy": "ip" } } } ``` ##### Fields of Endpoint ratelimit \* required fields Minimum configuration needs **any of**: `max_rate` , or `client_max_rate` ##### [`capacity`](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/#capacity "Click to copy link to capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **all users together** at **any given instant**. When the gateway starts, the bucket is full. As requests from users come, the remaining tokens in the bucket decrease. At the same time, the `max_rate` refills the bucket at the desired rate until its maximum capacity is reached. The default value for the `capacity` is the `max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `max_rate`. Defaults to `1` ##### [`cleanup_period`](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/#cleanup_period "Click to copy link to cleanup_period") string The cleanup period is how often the routine(s) in charge of optimizing the memory dedicated will go iterate all counters looking for outdated TTL and remove them. A low value keeps the memory slightly decreasing, but as a trade-off, it will increase the CPU dedicated to achieving this optimization. This is an advanced micro-optimization setting that should be used with caution. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1m"` ##### [`cleanup_threads`](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/#cleanup_threads "Click to copy link to cleanup_threads") integer These are the number of routines that search for and remove outdated rate limit counters. The more routine(s) you add, the faster the memory optimization is completed, but the more CPU it will consume. Generally speaking, a single thread is more than enough because the delete operation is very fast, even with a large number of counters. This is an advanced micro-optimization setting that you should use with caution. Defaults to `1` ##### [`client_capacity`](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/#client_capacity "Click to copy link to client_capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **each individual user** at **any given instant**. Works just as `capacity`, but instead of having one bucket for all users, keeps a counter for every connected client and endpoint, and refills from `client_max_rate` instead of `max_rate`. The client is recognized using the `strategy` field (an IP address, a token, a header, etc.). The default value for the `client_capacity` is the `client_max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `client_max_rate`. Defaults to `1` ##### [`client_max_rate`](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/#client_max_rate "Click to copy link to client_max_rate") number Number of tokens you add to the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) for each individual user (*user quota*) in the time interval you want (`every`). The remaining tokens in the bucket are the requests a specific user can do. It keeps a counter for every client and endpoint. Keep in mind that every KrakenD instance keeps its counters in memory for **every single client**. ##### [`every`](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/#every "Click to copy link to every") string Time period in which the maximum rates operate. For instance, if you set an `every` of `10m` and a rate of `5`, you are allowing 5 requests every ten minutes. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1s"` ##### [`key`](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/#key "Click to copy link to key") string Available when using `client_max_rate` and you have set a `strategy` equal to `header` or `param`. It makes no sense in other contexts. For `header` it is the header name containing the user identification (e.g., `Authorization` on tokens, or `X-Original-Forwarded-For` for IPs). When they contain a list of space-separated IPs, it will take the IP from the client that hit the first trusted proxy. For `param` it is the name of the placeholder used in the endpoint, like `id_user` for an endpoint `/user/{id_user}`. Examples: `"X-Tenant"` , `"Authorization"` , `"id_user"` ##### [`max_rate`](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/#max_rate "Click to copy link to max_rate") number Sets the maximum number of requests all users can do in the given time frame. Internally uses the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) algorithm. The absence of `max_rate` in the configuration or a `0` is the equivalent to no limitation. You can use decimals if needed. ##### [`num_shards`](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/#num_shards "Click to copy link to num_shards") integer All rate limit counters are stored in memory in groups (shards). All counters in the same shard share a mutex (which controls that one counter is modified at a time), and this helps with contention. Having, for instance, 2048 shards (default) and 1M users connected concurrently (same instant) means that each user will need to coordinate writes in their counter with an average of under 500 other users (1M/2048=489). Lowering the shards might increase contention and latency but free additional memory. This is an advanced micro-optimization setting that should be used with caution. Defaults to `2048` ##### [`strategy`](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/#strategy "Click to copy link to strategy") Available when using `client_max_rate`. Sets the strategy you will use to set client counters. Choose `ip` when the restrictions apply to the client’s IP address, or set it to `header` when there is a header that identifies a user uniquely. That header must be defined with the `key` entry. Possible values are: `"ip"` , `"header"` , `"param"` Schema: [https://www.krakend.io/schema/v2.13/qos/ratelimit/router.json](https://www.krakend.io/schema/v2.13/qos/ratelimit/router.json "JSON schema definition") A note on strategies: - `"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. --- # [Rate Limiting API Gateway Endpoints](https://www.krakend.io/docs/endpoints/rate-limit/) The router rate limit feature allows you to set the **maximum requests** a KrakenD endpoint (a route) will accept in a **given time window**. There are two different strategies to set limits that you can use separately or together: - **Endpoint rate-limiting** (`max_rate`): applies simultaneously to all clients using the endpoint, sharing a unique counter. - **User rate-limiting** (`client_max_rate`): sets a counter to each individual user. Both types can coexist and they complement each other, and store the counters **in-memory**. On a cluster, each machine sees and counts only its passing traffic. There are [additional types of rate-limiting](/docs/throttling/). Token Bucket The rate limiting is based internally on the [Token Bucket algorithm](/docs/throttling/token-bucket/). If you are unfamiliar with it, read the link to understand how it works. ## Comparing `max_rate` and `client_max_rate` Imagine you have Mary and Fred using your API, and they connect to an endpoint `/v1/checkout/payment` that you want to rate-limit. If you add a `max_rate`, you limit the activity they generate together. It does not matter who is making more or fewer requests; the endpoint will be inaccessible for everyone once the throughput surpasses the limit you have set. On the other hand, adding the `client_max_rate` monitors Fred and Mary’s activity separately. If one of them is an abuser, the access is cut, while the other can continue to use the endpoint. The `max_rate` (also available as [proxy rate-limit](/docs/backends/rate-limit/)) is an absolute number that gives you exact control over how much traffic you allow to hit the backend or endpoint. In an eventual DDoS, the `max_rate` can help in a way since it won’t accept more traffic than allowed. On the other hand, a single host could abuse the system by taking a significant percentage of that quota. The `client_max_rate` is a limit per client, and it won’t help you if you just want to control the total traffic, as the total traffic supported by the backend or endpoint depends on the number of different requesting clients. A DDoS will then happily pass through, but you can keep any particular abuser limited to its quota. As we said, you can set the two limiting strategies individually or together. Have in mind the following considerations: - Setting the **client rate limit alone** on a platform with many users can lead to a heavy load on your backends. For instance, if you have 200,000 active users in your platform at a given time and you allow each client ten requests per second (`client_max_rate: 10`, `every: 1s`), the permitted total traffic for the endpoint is 200,000 users x 10 req/s = 2M req/s - Setting the **endpoint rate limit alone** can lead to a single abuser limiting all other users in the platform. So, in most cases, it is better to play them together. Adding also a [Circuit Breaker](/docs/backends/circuit-breaker/) is even better. ## Configuration The `max_rate` and `client_max_rate` configurations are under a common namespace, `qos/ratelimit/router` (QoS stands for Quality of Service). For instance, let’s start with a simple and mixed example that sets two limits: - `50` requests every `10m` (10 minutes) among all clients - `5` requests per client every `10m`, identified by ip. ```json { "endpoint": "/limited-endpoint", "extra_config": { "qos/ratelimit/router": { "max_rate": 50, "every": "10m", "client_max_rate": 5, "client_capacity": 5, "strategy": "ip" } } } ``` An expanded and **more explicit configuration** that represents the same idea would be: ```json { "endpoint": "/limited-endpoint", "extra_config": { "qos/ratelimit/router": { "max_rate": 50, "every": "10m", "client_max_rate": 5, "strategy": "ip", "capacity": 50, "client_capacity": 5 } } } ``` In this configuration, we have set the IP `strategy`, which considers that every IP accessing the gateway is a different client. However, a client could be a JWT token, a header, or even a parameter. We have also set the `capacity` for the `max_rate` and the `client_capacity` for the `client_max_rate`, which sets the maximum buffer for any given instant. See below. ## Endpoint rate-limiting (`max_rate`) The endpoint rate limit acts on the number of simultaneous transactions an endpoint can process. This type of limit protects the service for all customers. In addition, these limits mitigate abusive actions such as rapidly writing content, aggressive polling, or excessive API calls. It consumes low memory as it only needs one counter per endpoint. When the users connected to an endpoint together exceed the `max_rate`, KrakenD starts to reject connections with a status code `503 Service Unavailable` and enables a [Spike Arrest](/docs/throttling/spike-arrest/) policy. Example: ```json { "endpoint": "/endpoint", "extra_config": { "qos/ratelimit/router": { "@comment":"A thousand requests every hour", "max_rate": 1000, "every": "1h" } } } ``` ##### Endpoint rate limit options \* required fields ##### [`capacity`](https://www.krakend.io/docs/endpoints/rate-limit/#capacity "Click to copy link to capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **all users together** at **any given instant**. When the gateway starts, the bucket is full. As requests from users come, the remaining tokens in the bucket decrease. At the same time, the `max_rate` refills the bucket at the desired rate until its maximum capacity is reached. The default value for the `capacity` is the `max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `max_rate`. Defaults to `1` ##### [`every`](https://www.krakend.io/docs/endpoints/rate-limit/#every "Click to copy link to every") string Time period in which the maximum rates operate. For instance, if you set an `every` of `10m` and a rate of `5`, you are allowing 5 requests every ten minutes. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1s"` ##### [`max_rate`](https://www.krakend.io/docs/endpoints/rate-limit/#max_rate "Click to copy link to max_rate") number Sets the maximum number of requests all users can do in the given time frame. Internally uses the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) algorithm. The absence of `max_rate` in the configuration or a `0` is the equivalent to no limitation. You can use decimals if needed. Schema: [https://www.krakend.io/schema/v2.13/qos/ratelimit/router.json](https://www.krakend.io/schema/v2.13/qos/ratelimit/router.json "JSON schema definition") ## Client rate-limiting (`client_max_rate`) The client or user rate limit applies one counter to each individual user and endpoint. Each endpoint can have different limit rates, but all users are subject to the same rate. A note on performance Limiting endpoints per user makes KrakenD keep in-memory counters for the two dimensions: *endpoints x clients*. The `client_max_rate` is more resource-consuming than the `max_rate` as every incoming client needs individual tracking. Even though counters are space-efficient and very small in data, many endpoints with many concurrent users will lead to higher memory consumption. When a single user connected to an endpoint exceeds their `client_max_rate,` KrakenD starts rejecting connections with a status code `429 Too Many Requests` and enables a [Spike Arrest](/docs/throttling/spike-arrest/) policy. Each client’s counter is stored in memory only for the time needed to deliver the traffic restriction properly. The needed time is calculated automatically based on your configuration, and we call this time the **TTL**. A specific routine (or more) deletes outdated counters during runtime. See micro-optimizations below for more details. Example: ```json { "endpoint": "/endpoint", "extra_config": { "qos/ratelimit/router": { "@comment":"20 requests every 5 minutes", "client_max_rate": 20, "client_capacity": 20, "every": "5m", "strategy": "ip" } } } ``` The following configuration options are specific to the client rate limiting: ##### Client rate limit options \* required fields ##### [`client_capacity`](https://www.krakend.io/docs/endpoints/rate-limit/#client_capacity "Click to copy link to client_capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **each individual user** at **any given instant**. Works just as `capacity`, but instead of having one bucket for all users, keeps a counter for every connected client and endpoint, and refills from `client_max_rate` instead of `max_rate`. The client is recognized using the `strategy` field (an IP address, a token, a header, etc.). The default value for the `client_capacity` is the `client_max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `client_max_rate`. Defaults to `1` ##### [`client_max_rate`](https://www.krakend.io/docs/endpoints/rate-limit/#client_max_rate "Click to copy link to client_max_rate") number Number of tokens you add to the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) for each individual user (*user quota*) in the time interval you want (`every`). The remaining tokens in the bucket are the requests a specific user can do. It keeps a counter for every client and endpoint. Keep in mind that every KrakenD instance keeps its counters in memory for **every single client**. ##### [`every`](https://www.krakend.io/docs/endpoints/rate-limit/#every "Click to copy link to every") string Time period in which the maximum rates operate. For instance, if you set an `every` of `10m` and a rate of `5`, you are allowing 5 requests every ten minutes. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1s"` ##### [`key`](https://www.krakend.io/docs/endpoints/rate-limit/#key "Click to copy link to key") string Available when using `client_max_rate` and you have set a `strategy` equal to `header` or `param`. It makes no sense in other contexts. For `header` it is the header name containing the user identification (e.g., `Authorization` on tokens, or `X-Original-Forwarded-For` for IPs). When they contain a list of space-separated IPs, it will take the IP from the client that hit the first trusted proxy. For `param` it is the name of the placeholder used in the endpoint, like `id_user` for an endpoint `/user/{id_user}`. Examples: `"X-Tenant"` , `"Authorization"` , `"id_user"` ##### [`strategy`](https://www.krakend.io/docs/endpoints/rate-limit/#strategy "Click to copy link to strategy") Available when using `client_max_rate`. Sets the strategy you will use to set client counters. Choose `ip` when the restrictions apply to the client’s IP address, or set it to `header` when there is a header that identifies a user uniquely. That header must be defined with the `key` entry. Possible values are: `"ip"` , `"header"` , `"param"` Schema: [https://www.krakend.io/schema/v2.13/qos/ratelimit/router.json](https://www.krakend.io/schema/v2.13/qos/ratelimit/router.json "JSON schema definition") Below, you’ll see different interpretations of what a client is. ### Client rate-limiting by token claim Setting a rate limit for every issued token could be as easy as: ```json { "endpoint": "/foo", "extra_config": { "auth/validator": { "@comment": "Omitted for simplicity" }, "qos/ratelimit/router": { "client_max_rate": 100, "client_capacity": 100, "every": "1h", "strategy": "header", "key": "Authorization" } } } ``` The endpoint now limits to 100 requests per hour to every different valid token (valid because the [JWT validator](/docs/authorization/jwt-validation/) takes care of it). But instead of rate limiting based on the whole token, you can also rate limit based on claims of the token by propagating claims as headers. For instance, let’s say you want to **rate-limit a specific department**, and your JWT token contains a claim `department`. If you have token validation and use the client rate-limiting with a `strategy` of `header`, you can set an arbitrary header name for the counter identifier. Propagated headers are available at the endpoint and backend levels, allowing you to set limits based on JWT criteria. You could have a configuration like this: ```json { "endpoint": "/token-ratelimited", "input_headers": [ "x-limit-department" ], "extra_config": { "auth/validator": { "propagate_claims": [ ["department","x-limit-department"] ] }, "qos/ratelimit/router": { "client_max_rate": 100, "client_capacity": 100, "every": "1h", "strategy": "header", "key": "x-limit-department" } } } ``` Notice that the `propagate_claims` in the validator adds the claim `department` value into a new header, `x-limit-department`. The header is also added under `input_headers` because otherwise, the endpoint wouldn’t see it ([zero-trust security](/docs/design/zero-trust/)). Finally, the rate limit uses the new header as a strategy and specifies its name under `key`. The department can now do `100` requests every `hour`. You can extrapolate this to any other claim, like the subject or anything else you need. ### Rate-limiting by URL parameter A different case of the `client_max_rate` is when used with the `strategy` equal to `param`. Instead of limiting a specific user (through a token, a claim, or a header), you consider that the client comes in the URL as a parameter. For instance, you provide an API containing endpoints like `/api/{customer_id}/invoices`, and you want to consider that every different `customer_id` is a different client. In that case, you can rate limit the parameter of the endpoint as follows: ```json { "endpoint": "/api/{customer_id}/invoices", "extra_config": { "qos/ratelimit/router": { "client_max_rate": 5, "client_capacity": 5, "every": "1m", "strategy": "param", "key": "customer_id" } } } ``` The configuration above would allow 5 requests to `/api/1234/invoices` every minute and another 5 to `/api/5678/invoices`. In a scenario like this, it would be advisable that you add a [security policy](/docs/enterprise/security-policies/) Enterprise that makes sure clients cannot abuse the rate limits of others. ### Micro-optimizations of the client\_rate\_limit There are a few advanced values that you can add to the rate limit if you want to fine-tune CPU and Memory consumption. These values are not needed in most of the cases, but the door is open to tune how the rate limit works internally. ##### Micro-optimization of rate limiting \* required fields ##### [`cleanup_period`](https://www.krakend.io/docs/endpoints/rate-limit/#cleanup_period "Click to copy link to cleanup_period") string The cleanup period is how often the routine(s) in charge of optimizing the memory dedicated will go iterate all counters looking for outdated TTL and remove them. A low value keeps the memory slightly decreasing, but as a trade-off, it will increase the CPU dedicated to achieving this optimization. This is an advanced micro-optimization setting that should be used with caution. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1m"` ##### [`cleanup_threads`](https://www.krakend.io/docs/endpoints/rate-limit/#cleanup_threads "Click to copy link to cleanup_threads") integer These are the number of routines that search for and remove outdated rate limit counters. The more routine(s) you add, the faster the memory optimization is completed, but the more CPU it will consume. Generally speaking, a single thread is more than enough because the delete operation is very fast, even with a large number of counters. This is an advanced micro-optimization setting that you should use with caution. Defaults to `1` ##### [`num_shards`](https://www.krakend.io/docs/endpoints/rate-limit/#num_shards "Click to copy link to num_shards") integer All rate limit counters are stored in memory in groups (shards). All counters in the same shard share a mutex (which controls that one counter is modified at a time), and this helps with contention. Having, for instance, 2048 shards (default) and 1M users connected concurrently (same instant) means that each user will need to coordinate writes in their counter with an average of under 500 other users (1M/2048=489). Lowering the shards might increase contention and latency but free additional memory. This is an advanced micro-optimization setting that should be used with caution. Defaults to `2048` Schema: [https://www.krakend.io/schema/v2.13/qos/ratelimit/router.json](https://www.krakend.io/schema/v2.13/qos/ratelimit/router.json "JSON schema definition") Example: ```json { "endpoint": "/api/invoices", "extra_config": { "qos/ratelimit/router": { "client_max_rate": 5, "client_capacity": 5, "every": "1m", "strategy": "ip", "num_shards": 2048, "cleanup_period": "60s", "cleanup_threads": 1 } } } ``` ### Examples of per-second rate limiting The following examples demonstrate a configuration with several endpoints, each one setting different limits. As they don’t set an `every` section, they will use the default of one second (`1s`): - A `/happy-hour` endpoint with unlimited usage as it sets `max_rate = 0` - A `/happy-hour-2` endpoint is equivalent to the previous one, as it has no rate limit configuration. - A `/limited-endpoint` combines `client_max_rate` and `max_rate` together. It is capped at 50 reqs/s for all users, AND their users can make up to 5 reqs/s (where a user is a different IP) - A `/user-limited-endpoint` is not limited globally, but every user (identified with `X-Auth-Token` can make up to 10 reqs/sec). Configuration: ```json { "version": 3, "endpoints": [ { "endpoint": "/happy-hour", "extra_config": { "qos/ratelimit/router": { "max_rate": 0, "client_max_rate": 0 } }, "backend": [ { "url_pattern": "/__health", "host": [ "http://localhost:8080" ] } ] }, { "endpoint": "/happy-hour-2", "backend": [ { "url_pattern": "/__health", "host": [ "http://localhost:8080" ] } ] }, { "endpoint": "/limited-endpoint", "extra_config": { "qos/ratelimit/router": { "max_rate": 50, "client_max_rate": 5, "client_capacity": 5, "strategy": "ip" } } }, { "endpoint": "/user-limited-endpoint", "extra_config": { "qos/ratelimit/router": { "client_max_rate": 10, "client_capacity": 10, "strategy": "header", "key": "X-Auth-Token" } }, "backend": [ { "url_pattern": "/__health", "host": [ "http://localhost:8080" ] } ] } ] } ``` ### Examples of per-minute or per-hour rate limiting The rate limit component measures the router activity using the time window selected under `every`. You can use hours or minutes instead of seconds or you could even set daily or monthly rate-limiting, but taking into account that the counters reset every time you deploy the configuration. To use units larger than an hour, just express the days by hours. Using large units is not convenient if you often deploy (unless you use the persisted [Redis rate limit Enterprise](/docs/enterprise/throttling/global-rate-limit/) ) For example, let’s say you want the endpoint to cut the access at `30 reqs/day`. It means that within a day, whether the users exhaust the 30 requests in one second or gradually across the day, you won’t let them do more than `30` every day. So how do we apply this to the configuration? The configuration would be: ```json { "qos/ratelimit/router": { "@comment": "Client rate limit of 30 reqs/day", "client_max_rate": 30, "client_capacity": 30, "every": "24h", "strategy": "ip" } } ``` Similarly, 30 requests every 5 minutes, could be set like this. ```json { "qos/ratelimit/router": { "@comment": "Endpoint rate limit of 30 reqs/hour", "max_rate": 30, "every": "5m", "capacity": 30 } } ``` In summary, the `client_max_rate` and the `max_rate` set the speed at which you refill new usage tokens to the user. On the other hand, the `capacity` and `client_capacity` let you play with the buffer you give to the users and let them spend 30 requests in a single second (within the 5 minutes) or not. For more information, see the [Token Bucket algorithm](/docs/enterprise/throttling/token-bucket/). --- # [Rate Limit Tiers (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/) The rate limit based on tiers allows you to have **multiple sets of [service](/docs/enterprise/service-settings/service-rate-limit/) and [endpoint](/docs/enterprise/endpoints/rate-limit/) rate limits** that apply differently to users depending on their tier or sometimes called subscription plans. There are four variants of the Tiered Rate Limit: - Service Tiered Rate Limit (stateless) - Endpoint Tiered Rate Limit (stateless) - Service Tiered Rate Limit consolidated to Redis (stateful) - Endpoint Tiered Rate Limit consolidated to Redis (stateful) The **service rate limits apply to all traffic** in the gateway, while the **endpoint rate limits apply to specific endpoints** where you include them. Choose stateless The stateless option is the most performant solution, using the server RAM to manage the activity. The stateful options based on Redis centralize the management in a Redis database, which has an extra network hop and dependency. Choose the stateless solution whenever possible. ## How do tiers work? For example, Mary has a Gold plan that entitles her to make more requests per second than John, who is in an inferior Silver plan: ![tiered-rate-limit-example.mmd diagram](/images/documentation/diagrams/tiered-rate-limit-example.mmd.svg) The tiered rate limit component allows you to get a header from the requests that set the tier a user belongs to. Then, the limits apply depending on the plan. When a request comes in, the gateway must be able to identify two things: - **Who is doing the request**. You will need to specify in the configuration which `strategy` to use to identify the user, and under which `key` is this information (e.g., a header, an IP, a claim in a token, etc.) - **Which tier the user has**. You will need to set in the configuration where to find the tier value (or the plan name). It must be in a header or somewhere else. Once the user is properly identified and associated with a tier, you can set multiple behaviors for the different tiers or even set a fallback behavior. ## Tiered rate-limiting configuration The tiers configuration uses the `extra_config`’s namespace `qos/ratelimit/tiered` and can be done either at the service level (root) or the endpoint level. When you set the tiers at the service level, you define the tiers for all API endpoints. On the endpoint level, you set the tiers for that specific endpoint. The `tiers` configuration object is an array with all the different tiers available, which are **evaluated in order**. This means that if a user can match more than one plan, the first plan matching in the list is the rate limit applied to them. The configuration of the tiers is as follows: ##### Fields of Tiered Rate Limit \* required fields ##### [`tier_key`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#tier_key "Click to copy link to tier_key") * string The header name containing the tier name. The string you provide is case-insensitive. If you need to take the value from a place that is not a header (a token, an API key), you must use propagate functions in the components that convert values to internal headers. ##### [`tiers`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#tiers "Click to copy link to tiers") * array of objects The list of all tier definitions and limits for each. Each item in the list is a tier object. Each item of tiers accepts the following properties: ##### [`ratelimit`](#tiers-ratelimit "Click to copy link to ratelimit") object The rate limit definition. This is an object with the same attributes the [service rate limit](https://www.krakend.io/docs/enterprise/service-settings/service-rate-limit/) has. ##### [`ratelimit_redis`](#tiers-ratelimit_redis "Click to copy link to ratelimit_redis") object The stateful rate limit definition. This is an object with the same attributes the [stateful service rate limit](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/) has. ##### [`tier_value`](#tiers-tier_value "Click to copy link to tier_value") string The tier value. When you use `literal`, it is the tier name. When you use `policy`, it is the expression you want to evaluate to determine if the user matches this tier or not (see security policies for syntax). Examples: `"gold"` , `"silver"` , `"value.matches('User-[a-zA-Z]+')"` Defaults to `""` ##### [`tier_value_as`](#tiers-tier_value_as "Click to copy link to tier_value_as") Determines how to parse the value found in the tier header. When `literal` is used, the exact value of the header is compared against the tier name. When `policy` is used, the value is used to evaluate a policy. When `*` is used, all values will match. Make sure to put the `*` as the last tier; otherwise the rest will be ignored. Possible values are: `"literal"` , `"policy"` , `"*"` Defaults to `"literal"` Schema: [https://www.krakend.io/schema/v2.13/qos/ratelimit/tiered.json](https://www.krakend.io/schema/v2.13/qos/ratelimit/tiered.json "JSON schema definition") As you can see, all tier definitions come with an associated `ratelimit` object and/or a `ratelimit_redis`. You can set a stateless rate limit and a stateful rate limit simultaneously. This can be used as a contention measure, so if Redis fails for whatever reason, you have a secondary rate limit that does not need network connectivity. When you enable both options, all rate limits act simultaneously, so do some math! ### Stateless tiered rate limit The stateless `ratelimit` object accepts the following properties: ##### Configuration for each \`ratelimit\` in the tier \* required fields Minimum configuration needs **any of**: `max_rate` , or `client_max_rate` ##### [`capacity`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#capacity "Click to copy link to capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **all users together** at **any given instant**. When the gateway starts, the bucket is full. As requests from users come, the remaining tokens in the bucket decrease. At the same time, the `max_rate` refills the bucket at the desired rate until its maximum capacity is reached. The default value for the `capacity` is the `max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `max_rate`. Defaults to `1` ##### [`cleanup_period`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#cleanup_period "Click to copy link to cleanup_period") string The cleanup period is how often the routine(s) in charge of optimizing the memory dedicated will go iterate all counters looking for outdated TTL and remove them. A low value keeps the memory slightly decreasing, but as a trade-off, it will increase the CPU dedicated to achieving this optimization. This is an advanced micro-optimization setting that should be used with caution. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1m"` ##### [`cleanup_threads`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#cleanup_threads "Click to copy link to cleanup_threads") integer These are the number of routines that search for and remove outdated rate limit counters. The more routine(s) you add, the faster the memory optimization is completed, but the more CPU it will consume. Generally speaking, a single thread is more than enough because the delete operation is very fast, even with a large number of counters. This is an advanced micro-optimization setting that you should use with caution. Defaults to `1` ##### [`client_capacity`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#client_capacity "Click to copy link to client_capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **each individual user** at **any given instant**. Works just as `capacity`, but instead of having one bucket for all users, keeps a counter for every connected client and endpoint, and refills from `client_max_rate` instead of `max_rate`. The client is recognized using the `strategy` field (an IP address, a token, a header, etc.). The default value for the `client_capacity` is the `client_max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `client_max_rate`. Defaults to `1` ##### [`client_max_rate`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#client_max_rate "Click to copy link to client_max_rate") number Number of tokens you add to the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) for each individual user (*user quota*) in the time interval you want (`every`). The remaining tokens in the bucket are the requests a specific user can do. It keeps a counter for every client and endpoint. Keep in mind that every KrakenD instance keeps its counters in memory for **every single client**. ##### [`every`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#every "Click to copy link to every") string Time period in which the maximum rates operate. For instance, if you set an `every` of `10m` and a rate of `5`, you are allowing 5 requests every ten minutes. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1s"` ##### [`key`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#key "Click to copy link to key") string Available when using `client_max_rate` and you have set a `strategy` equal to `header` or `param`. It makes no sense in other contexts. For `header` it is the header name containing the user identification (e.g., `Authorization` on tokens, or `X-Original-Forwarded-For` for IPs). When they contain a list of space-separated IPs, it will take the IP from the client that hit the first trusted proxy. For `param` it is the name of the placeholder used in the endpoint, like `id_user` for an endpoint `/user/{id_user}`. Examples: `"X-Tenant"` , `"Authorization"` , `"id_user"` ##### [`max_rate`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#max_rate "Click to copy link to max_rate") number Sets the maximum number of requests all users can do in the given time frame. Internally uses the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) algorithm. The absence of `max_rate` in the configuration or a `0` is the equivalent to no limitation. You can use decimals if needed. ##### [`num_shards`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#num_shards "Click to copy link to num_shards") integer All rate limit counters are stored in memory in groups (shards). All counters in the same shard share a mutex (which controls that one counter is modified at a time), and this helps with contention. Having, for instance, 2048 shards (default) and 1M users connected concurrently (same instant) means that each user will need to coordinate writes in their counter with an average of under 500 other users (1M/2048=489). Lowering the shards might increase contention and latency but free additional memory. This is an advanced micro-optimization setting that should be used with caution. Defaults to `2048` ##### [`strategy`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#strategy "Click to copy link to strategy") Available when using `client_max_rate`. Sets the strategy you will use to set client counters. Choose `ip` when the restrictions apply to the client’s IP address, or set it to `header` when there is a header that identifies a user uniquely. That header must be defined with the `key` entry. Possible values are: `"ip"` , `"header"` , `"param"` Schema: [https://www.krakend.io/schema/v2.13/qos/ratelimit/router.json](https://www.krakend.io/schema/v2.13/qos/ratelimit/router.json "JSON schema definition") #### Example: Stateless Tiered Rate Limit at the Service level The following configuration sets four different stateless tiers at the service level, which would apply to all endpoints defined in the configuration simultaneously: ```json { "version": 3, "host": [ "http://localhost:8080" ], "endpoints": [], "extra_config": { "qos/ratelimit/tiered": { "tier_key": "X-Plan", "tiers": [ { "tier_value": "admin", "tier_value_as": "literal", "ratelimit": { "client_max_rate": 100, "client_capacity": 100, "every": "1m", "max_rate": 10000, "strategy": "header", "key": "X-Account-Id" } }, { "tier_value": "user", "tier_value_as": "literal", "ratelimit": { "client_max_rate": 20, "client_capacity": 20, "every": "1m", "max_rate": 10000, "strategy": "header", "key": "X-Account-Id" } }, { "tier_value": "value.matches('Account-[a-zA-Z]+')", "tier_value_as": "policy", "ratelimit": { "client_max_rate": 20, "client_capacity": 20, "every": "1m", "strategy": "header", "key": "X-USER-ID" } }, { "tier_value": "", "tier_value_as": "*", "ratelimit": { "client_max_rate": 2, "client_capacity": 2, "every": "1m", "strategy": "ip" } } ] } } } ``` In the configuration above, we have defined the following behavior: - When a request comes with an `X-Plan` header with the value `admin`, each user can do 100 reqs per minute. The user uniqueness comes in the `X-Account-Id` header. In addition, all users of the admin tier can make up to 10000 requests together. - When a request comes with an `X-Plan` header with the value `user`, each user can do 20 reqs per minute. - When a request comes with an `X-Plan`, its value is matched against the policy `Account-[a-zA-Z]+` , e.g., if it contains `Account-abcdef`, then it rates to 20 per minute. - A final special tier `*` (which you must place at the end) matches the remaining cases and sets to 2 req/s based on their IP address. This would rate limit unknown accounts. ### Stateful Redis-backed tiered rate limit The stateful `ratelimit_redis` object accepts the following properties: ##### Configuration for each \`ratelimit\_redis\` in the tier \* required fields Minimum configuration needs **any of**: `connection_pool` + `max_rate` , or `connection_pool` + `client_max_rate` , or `connection_name` + `max_rate` , or `connection_name` + `client_max_rate` ##### [`capacity`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#capacity "Click to copy link to capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **all users together** at **any given instant**. When the gateway starts, the bucket is full. As requests from users come, the remaining tokens in the bucket decrease. At the same time, the `max_rate` refills the bucket at the desired rate until its maximum capacity is reached. The default value for the `capacity` is the `max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `max_rate`. Defaults to `1` ##### [`client_capacity`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#client_capacity "Click to copy link to client_capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **each individual user** at **any given instant**. Works just as `capacity`, but instead of having one bucket for all users, keeps a counter for every connected client and endpoint, and refills from `client_max_rate` instead of `max_rate`. The client is recognized using the `strategy` field (an IP address, a token, a header, etc.). The default value for the `client_capacity` is the `client_max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `client_max_rate`. Defaults to `1` ##### [`client_max_rate`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#client_max_rate "Click to copy link to client_max_rate") number Number of tokens you add to the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) for each individual user (*user quota*) in the time interval you want (`every`). The remaining tokens in the bucket are the requests a specific user can do. It keeps a counter for every client and endpoint. Keep in mind that every KrakenD instance keeps its counters in memory for **every single client**. ##### [`connection_name`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#connection_name "Click to copy link to connection_name") string The connection pool name or cluster name that is used by this ratelimit. The value must match what you configured in the [Redis Connection Pool](https://www.krakend.io/docs/enterprise/service-settings/redis-connection-pools/) ##### [`connection_pool`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#connection_pool "Click to copy link to connection_pool") string Deprecated The connection pool name that is used by this ratelimit. The value must match what you configured in the [Redis Connection Pool](https://www.krakend.io/docs/enterprise/service-settings/redis-connection-pools/) ##### [`every`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#every "Click to copy link to every") string Time period in which the maximum rates operate. For instance, if you set an `every` of `10m` and a rate of `5`, you are allowing 5 requests every ten minutes. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1s"` ##### [`key`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#key "Click to copy link to key") string Available when using `client_max_rate` and you have set a `strategy` equal to `header` or `param`. It makes no sense in other contexts. For `header` it is the header name containing the user identification (e.g., `Authorization` on tokens, or `X-Original-Forwarded-For` for IPs). When they contain a list of space-separated IPs, it will take the IP from the client that hit the first trusted proxy. For `param` it is the name of the placeholder used in the endpoint, like `id_user` for an endpoint `/user/{id_user}`. Examples: `"X-Tenant"` , `"Authorization"` , `"id_user"` ##### [`max_rate`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#max_rate "Click to copy link to max_rate") number Sets the maximum number of requests all users can do in the given time frame. Internally uses the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) algorithm. The absence of `max_rate` in the configuration or a `0` is the equivalent to no limitation. You can use decimals if needed. ##### [`on_failure_allow`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#on_failure_allow "Click to copy link to on_failure_allow") boolean Whether you want to allow a request to continue when the Redis connection is failing or not. The default behavior blocks the request if Redis is not responding correctly Defaults to `false` ##### [`strategy`](https://www.krakend.io/docs/enterprise/service-settings/tiered-rate-limit/#strategy "Click to copy link to strategy") Available when using `client_max_rate`. Sets the strategy you will use to set client counters. Choose `ip` when the restrictions apply to the client’s IP address, or set it to `header` when there is a header that identifies a user uniquely. That header must be defined with the `key` entry. Possible values are: `"ip"` , `"header"` , `"param"` Schema: [https://www.krakend.io/schema/v2.13/qos/ratelimit/redis.json](https://www.krakend.io/schema/v2.13/qos/ratelimit/redis.json "JSON schema definition") #### Example: Redis-based tiered rate limit ant the Service level As we have seen, two of the different variants of this Rate Limit are stateful and support Redis. The only difference is that instead of using the `ratelimit` entry, you use `ratelimit_redis`. But remember, you can use both at the same time if needed! Here’s an example configuration using Redis: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "extra_config": { "redis": { "connection_pools": [ { "name": "shared_instance", "host": "shared.redis.example.com" } ] }, "qos/ratelimit/tiered": { "tier_key": "X-Rate-Tier", "tiers": [ { "tier_value": "admin", "tier_value_as": "literal", "ratelimit_redis": { "connection_pool": "default", "on_failure_allow": true, "client_max_rate": 10, "client_capacity": 10, "max_rate": 10000, "every": "1s", "strategy": "header", "key": "X-Rate-Client-Id" } } ] } }, "endpoints": [] } ``` ## Tier evaluation When there are [multiple rate limits in the configuration](/docs/enterprise/throttling/) (tiered rate limit is just one of them), the `qos/ratelimit/tiered` evaluates in the first place. If the user has not reached the usage limit, the rest are checked, having a behavior of consistently applying the most restrictive rate limit. Speaking of the tiered rate limit alone, when the tiers evaluate, it’s done sequentially. The first tier matching its definition applies, and no additional tiers are checked. Be careful when using the special tier `*` that matches any request, and always set it in the **last position of the array**. Pay attention to similar attributes like `tier_key` and the inner `ratelimit` attribute `key`. The `tier_key` tells which header contains the **tier name** (or plan), while the `key` sets which element are we going to use to count hits to the user. ## Extracting the tier from a JWT token As the tiers work with a header, if you want to extract them from a JWT, you must use the `propagate_headers` attribute to specify which claim contains the tier. As JWT validation is only available at the endpoint level, the tier definition must go inside each endpoint. We recommend using Flexible Configuration to reduce the repeat code in your configuration. For instance, a JWT token containing a claim `plan`, and the identity of the user under the `sub` claim could be configured as this (in this example, we have an endpoint tiered rate limit): ```json { "endpoint": "/foo", "extra_config": { "auth/validator": { "propagate_claims": [ [ "plan", "X-Plan" ], [ "sub", "X-Account-Id" ] ] }, "qos/ratelimit/tiered": { "tier_key": "X-Plan", "tiers": [ { "tier_value": "admin", "tier_value_as": "literal", "ratelimit": { "client_max_rate": 100, "client_capacity": 100, "every": "1m", "max_rate": 10000, "strategy": "header", "key": "X-Account-Id" } } ] } } } ``` As you can see, the JWT validator converts de JWT claims to the headers `X-Plan` and `X-Account-Id` using `propagate_claims`. Then, the component `qos/ratelimit/tiered` uses the first propagated header `X-Plan` to know which tier needs to use, and the second `X-Account-Id` header to know which user is being rate limited. ## Using the API Key role as tier definition Similarly, [API Keys](/docs/enterprise/authentication/api-keys/) work with propagated headers too. You can use the role of an API key as the tier definition, both at the service and endpoint levels. To do so, you only need to use the `propagate_role` attribute in the API keys: ```json { "version": 3, "extra_config": { "auth/api-keys": { "strategy": "header", "identifier": "Authorization", "propagate_role": "X-Krakend-Role", "keys": [ { "key": "4d2c61e1-34c4-e96c-9456-15bd983c5019", "roles": [ "user" ], "@description": "ACME Inc." }, { "key": "58427514-be32-0b52-b7c6-d01fada30497", "roles": [ "admin", "user" ], "@description": "Administrators Inc." } ] }, "qos/ratelimit/tiered": { "tier_key": "X-Krakend-Role", "tiers": [ { "tier_value": "admin", "tier_value_as": "literal", "ratelimit": { "client_max_rate": 100, "client_capacity": 100, "every": "1m", "max_rate": 10000, "strategy": "header", "key": "Authorization" } }, { "tier_value": "", "tier_value_as": "*", "ratelimit": { "client_max_rate": 1, "client_capacity": 1, "every": "1m", "max_rate": 10, "strategy": "header", "key": "Authorization" } } ] } } } ``` Notice that the `key` used in the `ratelimit` of each plan matches the `identifier` of the API Key. Notice that there is also a special tier `*` for nonmatching tiers that is used as the default, but keep in mind that the API key component can pass as `tier_value` the string `ANY` when the endpoint does not require roles, so you might want to add a tier with this value as well. --- # [Rate Limiting Backends](https://www.krakend.io/docs/backends/rate-limit/) No matter what amount of activity the users generate at the router level, you can limit KrakenD’s connections to your backends. The configuration is similar to the [router’s rate limit](/docs/endpoints/rate-limit/), but it’s declared directly in the `backend` section instead of the `endpoint`. The limit applies **per defined backend entry** and does not consider the activity other backends generate. Each `backend` entry handles its counters and does not share them with different backends or endpoints. The proxy rate limit is defined in the `krakend.json` configuration file as follows: ```json { "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, "every": "1s", "capacity": 1 } } }] } ``` These are the parameters you can set: ##### Fields of Backend Ratelimit \* required fields ##### [`capacity`](https://www.krakend.io/docs/backends/rate-limit/#capacity "Click to copy link to capacity") * integer The capacity according to the [token bucket algorithm](https://www.krakend.io/docs/throttling/token-bucket/). Defines the maximum requests you can do in an instant (including the zero moment when you start the gateway), and can be larger or smaller than the `max_rate`. When unsure, use the same value of `max_rate`, so the maximum number of requests can be consumed at once. Defaults to `1` ##### [`every`](https://www.krakend.io/docs/backends/rate-limit/#every "Click to copy link to every") string Time period in which the counter works. For instance, if you set an `every` of `10m` and a `max_rate` of `5`, you are allowing 5 requests every ten minutes. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1s"` ##### [`max_rate`](https://www.krakend.io/docs/backends/rate-limit/#max_rate "Click to copy link to max_rate") * number Maximum requests per second you want to accept in this backend. Example: `0.5` Schema: [https://www.krakend.io/schema/v2.13/qos/ratelimit/proxy.json](https://www.krakend.io/schema/v2.13/qos/ratelimit/proxy.json "JSON schema definition") ## Comparison with router rate limit In a nutshell: - The [router rate limit](/docs/endpoints/rate-limit/) controls the requests users do to KrakenD. - The proxy rate limit controls KrakenD’s requests to your services. You don’t have to choose one or the other; you can mix the different types as they cover additional use cases. --- # [Stateless Cluster Throttling: Optimize API Performance](https://www.krakend.io/docs/throttling/cluster/) The stateless rate-limiting ([service Enterprise](/docs/enterprise/service-settings/service-rate-limit/) or [endpoint](/docs/endpoints/rate-limit/) types) is the recommended approach for almost all scenarios. As the API Gateway does not have any centralization, **the limits apply individually to each running instance of KrakenD**. Global rate limit If you prefer not to use a stateless rate limit, the KrakenD Enterprise edition has a [stateful Redis-backed rate limit](/docs/enterprise/throttling/global-rate-limit/) where counters are shared amongst all nodes. Working in a cluster implies applying the limits taking into account the deployment size. For instance, if you want to use a limit of 100 reqs/s in a specific endpoint, the configuration will need to consider the number of nodes in the cluster. Let’s say that you have a cluster deployed with three instances of KrakenD. If you write a `max_rate=100` in the configuration, your ecosystem is limiting to 300 reqs/s in total as three servers are receiving balanced traffic. When you have to do small math like this, through the [Flexible Configuration](/docs/configuration/flexible-config/), you might inject environment variables when starting KrakenD with the total number of machines you have. For instance: ```tpl { "endpoint": "/limited-endpoint", "extra_config": { "qos/ratelimit/router": { "max_rate": {{ env "NUM_PODS" | div 100 }} } } } ``` You can execute krakend like this: Term  ``` $FC_ENABLE=1 \ FC_OUT=compiled.json \ NUM_PODS=3 krakend check -c krakend.json ``` And you get in the compiled.json the following content: ```json { "endpoint": "/limited-endpoint", "extra_config": { "qos/ratelimit/router": { "max_rate": 33 } } } ``` --- # [Stateful endpoint rate limit (Redis backed) (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/) The **Stateful Endpoint Rate Limit based on Redis** centralizes the activity of all KrakenD instances rate limiting. It works identically to the [stateful service rate limit](/docs/enterprise/throttling/global-rate-limit/) but applies the limits to specific endpoints instead of the whole service. ![](/images/documentation/krakend-global-rate-limit.png "Global rate limit") You should always use the stateless rate limit unless an unavoidable business rule forces you to choose this one. The stateless rate limit does not need any database and does not generate network activity to manage the token bucket. From a functional perspective, both rate limits, stateless and stateful, offer the same features. The difference lies in the fact that in the stateless, each node counts on its traffic, while the stateful counts all traffic. But don’t be misled; with a stateless rate limit, you can achieve the same results without adding a network dependency, even when you scale up and down your infrastructure. ## Redis rate-limit endpoint configuration To configure Redis-backed rate limits on your endpoints, you must declare at least two namespaces. One with the Redis configuration at the service level (`redis` namespace), and another that sets the rate limit values in the desired endpoint’s `extra_config` with the namespace `qos/ratelimit/router/redis`. The following configuration enables 10 requests per minute to the `/foo` endpoint, based on the identity of the `sub` claim present in the JWT token: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "extra_config": { "redis": { "connection_pools": [ { "name": "shared_instance", "address": "redis:6379" } ] }, }, "endpoints": [ { "endpoint": "/foo", "extra_config": { "qos/ratelimit/router/redis": { "connection_name": "shared_instance", "on_failure_allow": false, "client_max_rate": 10, "client_capacity": 10, "every": "1m", "strategy": "jwt", "key": "sub" } } } ] } ``` The `redis` namespace allows you to set a [lot of Redis pool options](/docs/enterprise/service-settings/redis-connection-pools/). The two basic ones are the `name` and the `address` as shown above. Then, the namespace `qos/ratelimit/router/redis` defines how the endpoint Rate Limit will work in this endpoint. The properties you can set in this namespace are the same as you find in the [stateful service rate limit](/docs/enterprise/throttling/global-rate-limit/): ##### Fields of Redis-backed Rate Limit \* required fields Minimum configuration needs **any of**: `connection_pool` + `max_rate` , or `connection_pool` + `client_max_rate` , or `connection_name` + `max_rate` , or `connection_name` + `client_max_rate` ##### [`capacity`](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/#capacity "Click to copy link to capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **all users together** at **any given instant**. When the gateway starts, the bucket is full. As requests from users come, the remaining tokens in the bucket decrease. At the same time, the `max_rate` refills the bucket at the desired rate until its maximum capacity is reached. The default value for the `capacity` is the `max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `max_rate`. Defaults to `1` ##### [`client_capacity`](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/#client_capacity "Click to copy link to client_capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **each individual user** at **any given instant**. Works just as `capacity`, but instead of having one bucket for all users, keeps a counter for every connected client and endpoint, and refills from `client_max_rate` instead of `max_rate`. The client is recognized using the `strategy` field (an IP address, a token, a header, etc.). The default value for the `client_capacity` is the `client_max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `client_max_rate`. Defaults to `1` ##### [`client_max_rate`](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/#client_max_rate "Click to copy link to client_max_rate") number Number of tokens you add to the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) for each individual user (*user quota*) in the time interval you want (`every`). The remaining tokens in the bucket are the requests a specific user can do. It keeps a counter for every client and endpoint. Keep in mind that every KrakenD instance keeps its counters in memory for **every single client**. ##### [`connection_name`](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/#connection_name "Click to copy link to connection_name") string The connection pool name or cluster name that is used by this ratelimit. The value must match what you configured in the [Redis Connection Pool](https://www.krakend.io/docs/enterprise/service-settings/redis-connection-pools/) ##### [`connection_pool`](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/#connection_pool "Click to copy link to connection_pool") string Deprecated The connection pool name that is used by this ratelimit. The value must match what you configured in the [Redis Connection Pool](https://www.krakend.io/docs/enterprise/service-settings/redis-connection-pools/) ##### [`every`](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/#every "Click to copy link to every") string Time period in which the maximum rates operate. For instance, if you set an `every` of `10m` and a rate of `5`, you are allowing 5 requests every ten minutes. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1s"` ##### [`key`](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/#key "Click to copy link to key") string Available when using `client_max_rate` and you have set a `strategy` equal to `header` or `param`. It makes no sense in other contexts. For `header` it is the header name containing the user identification (e.g., `Authorization` on tokens, or `X-Original-Forwarded-For` for IPs). When they contain a list of space-separated IPs, it will take the IP from the client that hit the first trusted proxy. For `param` it is the name of the placeholder used in the endpoint, like `id_user` for an endpoint `/user/{id_user}`. Examples: `"X-Tenant"` , `"Authorization"` , `"id_user"` ##### [`max_rate`](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/#max_rate "Click to copy link to max_rate") number Sets the maximum number of requests all users can do in the given time frame. Internally uses the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) algorithm. The absence of `max_rate` in the configuration or a `0` is the equivalent to no limitation. You can use decimals if needed. ##### [`on_failure_allow`](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/#on_failure_allow "Click to copy link to on_failure_allow") boolean Whether you want to allow a request to continue when the Redis connection is failing or not. The default behavior blocks the request if Redis is not responding correctly Defaults to `false` ##### [`strategy`](https://www.krakend.io/docs/enterprise/throttling/endpoint-redis-rate-limit/#strategy "Click to copy link to strategy") Available when using `client_max_rate`. Sets the strategy you will use to set client counters. Choose `ip` when the restrictions apply to the client’s IP address, or set it to `header` when there is a header that identifies a user uniquely. That header must be defined with the `key` entry. Possible values are: `"ip"` , `"header"` , `"param"` Schema: [https://www.krakend.io/schema/v2.13/qos/ratelimit/redis.json](https://www.krakend.io/schema/v2.13/qos/ratelimit/redis.json "JSON schema definition") --- # [Stateful service rate limit (Redis backed) (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/) The Redis rate limit functionality enables a Redis database store to centralize all KrakenD rate limit counters. Instead of having each KrakenD node count its hits in memory, the counters are global and persist in the database. ![](/images/documentation/krakend-global-rate-limit.png "Global rate limit") ## Rate limit (stateless) vs. Redis-backed rate limit (stateful) It’s essential to understand the differences between these two antagonistic approaches, so let’s put an example. Let’s say you have four different KrakenD nodes running in a cluster and want to limit a specific group of users to **100 requests per second**. With the **stateless rate limit** (`qos/ratelimit/service`), every KrakenD node only knows about itself. Whatever is happening in the rest of the nodes is not visible to itself. Since you have four KrakenD boxes, you need to write a limit of 25 reqs/s in the configuration. When all nodes run simultaneously and balance equally, you get the average limit of 100 reqs/s. Users can see rejections when the system runs to almost the configured limit’s total capacity. Some nodes will already reach their limit, while others still have a margin. If you ever deploy a 5th machine with the same configuration, your total rate limit upgrades to 125 reqs/s. You are adding 25reqs/s more capacity with the new node. But if you remove instead one of the four nodes, your total rate limit would be 75 reqs/s. On the other hand, the **stateful rate limit** (`qos/ratelimit/service/redis`) uses a shared Redis store by all neighbors, and all nodes are affected and aware of other’s activity. Awareness comes from all KrakenD nodes reading and writing the counters on the central database, knowing the real number of hits on the whole platform. The limits are invariable if you add or remove nodes on the fly. From a business perspective, the Global rate limit might sound more attractive. In contrast, from an architectural point of view, the default rate limit is much better as it offers infinite scalability, no network dependency, and much higher throughput. Our recommendation (an engineer writing) is always to use the stateless rate limit. ## Configuration To configure Redis-backed rate limits, you must declare at least two namespaces. One with the Redis configuration and another that sets the rate limit values. The two namespaces could look like this: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "extra_config": { "redis": { "connection_pools": [ { "name": "shared_instance", "address": "redis:6379" } ] }, "qos/ratelimit/service/redis": { "connection_pool": "shared_instance", "on_failure_allow": false, "max_rate": 10, "capacity": 10, "client_max_rate": 10, "client_capacity": 10, "every": "1m", "strategy": "header", "key": "x-client-id" } } } ``` The `redis` namespace allows you to set a [lot of Redis pool options](/docs/enterprise/service-settings/redis-connection-pools/). The two basic ones are the `name` and the `address` as shown above. Then, the namespace `qos/ratelimit/service/redis` defines how the Service Rate Limit is going to work. The properties you can set in this namespace are: ##### Fields of Redis-backed Rate Limit \* required fields Minimum configuration needs **any of**: `connection_pool` + `max_rate` , or `connection_pool` + `client_max_rate` , or `connection_name` + `max_rate` , or `connection_name` + `client_max_rate` ##### [`capacity`](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/#capacity "Click to copy link to capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **all users together** at **any given instant**. When the gateway starts, the bucket is full. As requests from users come, the remaining tokens in the bucket decrease. At the same time, the `max_rate` refills the bucket at the desired rate until its maximum capacity is reached. The default value for the `capacity` is the `max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `max_rate`. Defaults to `1` ##### [`client_capacity`](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/#client_capacity "Click to copy link to client_capacity") integer Defines the maximum number of [tokens a bucket can hold](https://www.krakend.io/docs/throttling/token-bucket/), or said otherwise, how many requests will you accept from **each individual user** at **any given instant**. Works just as `capacity`, but instead of having one bucket for all users, keeps a counter for every connected client and endpoint, and refills from `client_max_rate` instead of `max_rate`. The client is recognized using the `strategy` field (an IP address, a token, a header, etc.). The default value for the `client_capacity` is the `client_max_rate` value expressed in seconds or 1 for smaller fractions. When unsure, use the same number as `client_max_rate`. Defaults to `1` ##### [`client_max_rate`](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/#client_max_rate "Click to copy link to client_max_rate") number Number of tokens you add to the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) for each individual user (*user quota*) in the time interval you want (`every`). The remaining tokens in the bucket are the requests a specific user can do. It keeps a counter for every client and endpoint. Keep in mind that every KrakenD instance keeps its counters in memory for **every single client**. ##### [`connection_name`](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/#connection_name "Click to copy link to connection_name") string The connection pool name or cluster name that is used by this ratelimit. The value must match what you configured in the [Redis Connection Pool](https://www.krakend.io/docs/enterprise/service-settings/redis-connection-pools/) ##### [`connection_pool`](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/#connection_pool "Click to copy link to connection_pool") string Deprecated The connection pool name that is used by this ratelimit. The value must match what you configured in the [Redis Connection Pool](https://www.krakend.io/docs/enterprise/service-settings/redis-connection-pools/) ##### [`every`](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/#every "Click to copy link to every") string Time period in which the maximum rates operate. For instance, if you set an `every` of `10m` and a rate of `5`, you are allowing 5 requests every ten minutes. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1s"` ##### [`key`](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/#key "Click to copy link to key") string Available when using `client_max_rate` and you have set a `strategy` equal to `header` or `param`. It makes no sense in other contexts. For `header` it is the header name containing the user identification (e.g., `Authorization` on tokens, or `X-Original-Forwarded-For` for IPs). When they contain a list of space-separated IPs, it will take the IP from the client that hit the first trusted proxy. For `param` it is the name of the placeholder used in the endpoint, like `id_user` for an endpoint `/user/{id_user}`. Examples: `"X-Tenant"` , `"Authorization"` , `"id_user"` ##### [`max_rate`](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/#max_rate "Click to copy link to max_rate") number Sets the maximum number of requests all users can do in the given time frame. Internally uses the [Token Bucket](https://www.krakend.io/docs/throttling/token-bucket/) algorithm. The absence of `max_rate` in the configuration or a `0` is the equivalent to no limitation. You can use decimals if needed. ##### [`on_failure_allow`](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/#on_failure_allow "Click to copy link to on_failure_allow") boolean Whether you want to allow a request to continue when the Redis connection is failing or not. The default behavior blocks the request if Redis is not responding correctly Defaults to `false` ##### [`strategy`](https://www.krakend.io/docs/enterprise/throttling/global-rate-limit/#strategy "Click to copy link to strategy") Available when using `client_max_rate`. Sets the strategy you will use to set client counters. Choose `ip` when the restrictions apply to the client’s IP address, or set it to `header` when there is a header that identifies a user uniquely. That header must be defined with the `key` entry. Possible values are: `"ip"` , `"header"` , `"param"` Schema: [https://www.krakend.io/schema/v2.13/qos/ratelimit/redis.json](https://www.krakend.io/schema/v2.13/qos/ratelimit/redis.json "JSON schema definition") ## Migrating from the old Redis plugin (legacy) Before EE v2.8, an external Redis plugin offered this functionality and is now considered **deprecated**. The native namespace `qos/ratelimit/service/redis` replaces this plugin and offers a better way of dealing with stateful functionality. To migrate, you need to remove the plugin configuration from your settings, and put the values in the new namespace. Here is a diff example to compare the previous and the new configurations: ```diff < "plugin": { < "pattern": ".so", < "folder": "/opt/krakend/plugins/" < }, < "extra_config": { < "plugin/http-server": { < "name": [ < "redis-ratelimit" < ], < "redis-ratelimit": { < "host": "redis:6379", < "tokenizer": "jwt", < "tokenizer_field": "sub", < "burst": 10, < "rate": 10, < "period": "60s" < } < } < } < } --- > "qos/ratelimit/service/redis": { > "redis_instance": "shared_instance", > "on_failure_allow": false, > "client_max_rate": 10, > "client_capacity": 10, > "every": "60s", > "strategy": "jwt", > "key": "sub" > } ``` The changes you need to do are: - The `plugin` entry can be removed entirely from the configuration **if you don’t have other plugins**. - Instead of the `plugin/http-server` entry in the service `extra_config`, you must add a `redis` namespace that declares the connection pools (now you can have many) - Instead of the `redis-ratelimit` field inside the plugin, you have to declare the new namespace `qos/ratelimit/service/redis` under the service `extra_config`, at the same level than the `redis` namespace. In all, the **new Redis configuration** should be similar to this: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "extra_config": { "redis": { "nodes": [ { "name": "shared_instance", "host": "redis:6379" } ] }, "qos/ratelimit/service/redis": { "redis_instance": "shared_instance", "on_failure_allow": false, "max_rate": 10, "capacity": 10, "client_max_rate": 10, "client_capacity": 10, "every": "60s", "strategy": "jwt", "key": "sub" } } } ``` To see the documentation of the old Redis plugin, browse the documentation for KrakenD Enterprise v2.7 or older. --- # [Bot Detection and Throttling Guide: Prevent API Abuse](https://www.krakend.io/docs/throttling/botdetector/) The **bot detector** module checks incoming connections to the gateway to determine if a bot made them, helping you detect and reject bots carrying out scraping, content theft, and form spam. Bots are detected by inspecting the `User-Agent` and comparing its value with a set of configuration rules provided by you. The bot detector module **does not set any initial rules**, meaning that is up to you to decide the best rules for your use case, and choose how restrictive or permissive you are with bots. As the bot detector module is flexible in its configuration, you can use it for other purposes than just discarding bots. For instance, you could set an allow rule for your mobile application `User-Agent` which would be allowed to interact with KrakenD and discard the rest of the traffic. Discarded traffic receives a `403 Forbidden` status code. ## Configuring bot rules The configuration rules of the bot detector have to be included inside the `extra_config`’s namespace `security/bot-detector` at the root level of your `krakend.json` file, or inside an endpoint. For instance: ```json { "extra_config": { "security/bot-detector": { "allow": ["MyAndroidClient/1.0", "Pingdom.com_bot_version_1.1"], "deny": ["a", "b"], "patterns": [ "(Pingdom.com_bot_version_).*", "(facebookexternalhit)/.*" ], "cache_size": 10000, "empty_user_agent_is_bot": true } } } ``` The available configuration options in the bot detector module are: ##### Fields of Bot detector \* required fields ##### [`allow`](https://www.krakend.io/docs/throttling/botdetector/#allow "Click to copy link to allow") array of strings An array with EXACT MATCHES of trusted user agents that can connect. Example: `["MyAndroidClient/1.0","Pingdom.com_bot_version_1.1"]` Defaults to `[]` ##### [`cache_size`](https://www.krakend.io/docs/throttling/botdetector/#cache_size "Click to copy link to cache_size") integer Size of the LRU cache that helps speed the bot detection. The size is the mumber of users agents that you want to keep in memory. Example: `1000` ##### [`deny`](https://www.krakend.io/docs/throttling/botdetector/#deny "Click to copy link to deny") array of strings An array with EXACT MATCHES of undesired bots, to reject immediately. Example: `["facebookexternalhit/1.1"]` Defaults to `[]` ##### [`empty_user_agent_is_bot`](https://www.krakend.io/docs/throttling/botdetector/#empty_user_agent_is_bot "Click to copy link to empty_user_agent_is_bot") boolean Whether to consider an empty user-agent a bot (and reject it) or not. Defaults to `false` ##### [`patterns`](https://www.krakend.io/docs/throttling/botdetector/#patterns "Click to copy link to patterns") array of strings An array with all the regular expressions that define bots. Matching bots are rejected. Example: `["GoogleBot.*","(facebookexternalhit)/.*"]` Defaults to `[]` Schema: [https://www.krakend.io/schema/v2.13/security/bot-detector.json](https://www.krakend.io/schema/v2.13/security/bot-detector.json "JSON schema definition") Notice that the `allow` and the `deny` do not expect regular expressions, but **literal strings**. The purpose of this design is to get the best performance as comparing a literal string is much faster than evaluating a regular expression. On the other hand, the `patterns` attribute expects regular expressions. The syntax is the same general syntax used by Perl, Python, and other languages. More precisely, it is the syntax accepted by [RE2](https://golang.org/s/re2syntax) The order of evaluation of the rules is sequential in this order: `allow` -> `deny` -> `patterns`. When a user agent matches in any of the former evaluations, the execution ends, and the connection is accepted (allow) or rejected (deny and patterns). ### Building your bot rules Fighting against spam, spiders, scrapping, theft, and bots is a problematic matter. There are different angles you can choose to combat it using the bot detection module. Maybe you want to have a [massive list](https://github.com/ua-parser/uap-core/blob/master/regexes.yaml) of regular expressions of bots that are troubling you, and caching enabled. Or perhaps you only require a single negative pattern that discards anything that you don’t know is legit. Whatever rules you decide to set in place, remember than allowing and denying are faster but are inflexible and require you to set the exact user-agent. On the other hand, regular expressions are very convenient, but the cost of evaluating them is higher in comparison. ### Caching Evaluating every user agent against a **substantial list of patterns** can be a time-consuming operation. Even when we are talking about a few milliseconds, you can enable caching by setting `cache_size` and avoid reprocessing User-Agents checked before. Every millisecond counts! The LRU caching system is in-memory and does not require running a separate set of servers, thus reducing the operation pain. There are neither cache expiration times, nor explicit cache evictions. When/if the cache is full, the least recently used (LRU) element is automatically replaced with the new one. An order of magnitude of megabytes should be enough to save the different User-Agent requests and combinations. Set in the `cache_size` an integer with the fixed size of the cache (number of elements to store), or `0` to disable caching. --- # [IP Filtering (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/throttling/ipfilter/) The IP filtering plugin allows you to restrict the traffic to your API gateway based on the IP address. It works in two different modes (allow or deny) where you define the list of IPs (CIDR blocks) that are authorized to use the API, or that are denied from using the API. The filtering applies globally to all endpoints, or to only specific endpoints **depending on the placement of the configuration**. When an IP is rejected, the consumer of the endpoint receives a **403 Forbidden** status code. When the functionality is at the endpoint level, the consumer receives a `500` unless you have enabled [returning the HTTP status code of a single backend](/docs/backends/detailed-errors/#return-the-http-status-code-of-a-single-backend). The basic configuration looks like this: ```json { "ip-filter": { "CIDR": [ "192.168.0.0/24", "172.17.2.56/32", "10.0.0.0/16", "127.0.0.1" ], "allow": true } } ``` The supported options of the plugin are: - `CIDR` *list*: The [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) blocks (list of IPs) you want to allow or deny. - `client_ip_headers` *list* (optional): A custom list of all headers that might contain the real IP of the client. The first matching IP in the list will be used. Default headers are (in order of checking): `X-Forwarded-For`, `X-Real-IP`, and `X-Appengine-Remote-Addr`. - `trusted_proxies` *list* (optional): A custom list of all the recognized machines/balancers that proxy the client to your application. This list is used to avoid spoofing when trying to get the real IP of the client. - `allow` *bool*: IP filtering mode. Defaults to `false` (deny mode). - `true`: **Allow mode**. Only the matching IPs are able to access the content. - `false`: **Deny mode**. All matching IPs are discarded. - In the rare case that the machine is unable to parse the given IP (couldn’t be parsed as an IPv4 or IPv6), in deny mode the request will pass, but the allow won’t. ## Filter IPs globally The following configuration is used when you want to enable a common set of IP filtering rules for any request to the gateway. **All endpoints will enable IP based access** with this configuration: ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "extra_config": { "plugin/http-server": { "name": ["ip-filter", "some-other-plugin-here" ], "ip-filter": { "allow": true, "CIDR": [ "192.168.0.0/24", "172.17.2.56/32", "10.0.0.0/16", "127.0.0.1" ] } } } } ``` When the plugin is loaded as a `plugin/http-server` the configuration affects all endpoints. ## Filter IPs by endpoint To filter IPs on specific endpoints, or to write additional rules to the global IP configuration (having a second check), then you have to place the configuration inside the endpoint’s `extra_config`. Custom location of the IP address When filtering endpoints, the IP address is taken from the `X-Forwarded-For` header which is automatically calculated by KrakenD. If this header couldn’t retrieve the client IP, you might want to set both `client_ip_headers` in the plugin configuration, and `input_headers` in the endpoint configuration with the custom headers you need. The following configuration is used to filter access to specific endpoints: ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "endpoints": [ { "endpoint": "/filtered-endpoint", "extra_config": { "plugin/req-resp-modifier": { "name": ["ip-filter" ], "ip-filter": { "allow": true, "CIDR": [ "192.168.0.0/24", "172.17.2.56/32", "10.0.0.0/16", "127.0.0.1" ] } } } }] } ``` When the plugin is loaded as a `plugin/req-resp-modifier` the configuration affects that specific endpoint only. ## Filtered IPs logging Whenever a request is denied by the IP filter plugin, the attempt is logged using an `INFO` level. Example: IP filtering log  ``` $▶ INFO [PLUGIN: ip-filter] blocking access to endpoint /foo from IP 1.2.3.4 ``` If you want to see these logs in production make sure to [enable logging](/docs/enterprise/logging/) with the `INFO` level. ## Advanced calculation of IPs There will be times when your PaaS provides the real IP in a specific custom header, or when you need to add a trusted proxy. Here is an example of the configuration (set your custom values): ```json { "ip-filter": { "CIDR": [ "192.168.0.0/24", "172.17.2.56/32", "10.0.0.0/16", "127.0.0.1" ], "client_ip_headers": [ "X-User-IP", "X-MyCompany-Client-IP" ], "trusted_proxies": [ "10.0.0.0/16" ], "allow": true } } ``` --- # [How API Traffic Throttling with Token Bucket algorithm works](https://www.krakend.io/docs/throttling/token-bucket/) The Token Bucket algorithm helps you to allow or deny requests depending on the levels of traffic you are having. The algorithm is used to offer functionalities like the **Spike Arrest** and the several **Rate Limiting** options. ## A quick analogy If you ever went to a traveling carnival, funfair, or amusement park to get into the attractions, you probably exchanged money for tokens/tickets at the ticket booth. The tokens help the carousel operator, bumper cars, or chance games stand to collect the payment faster and allow or deny access. No matter how much money you have, you can carry a fixed maximum number of tokens in your pocket or a bucket before they spill. Picture yourself now with a bucket full of these. The number of rides and games you can have at the fair is the number of tokens left in the bucket (assuming 1 token = 1 ride). The bigger your bucket or pocket, the more rides you can do before going to the ticket booth to refill again. Extrapolating to KrakenD, the rides are the API requests. Each token is a remaining gateway request you can do, and the bucket represents how many requests you are allowed to do before acquiring more tokens. ## Understanding the Token Bucket algorithm The Token Bucket algorithm ([Wikipedia definition](https://en.wikipedia.org/wiki/Token_bucket)) is based on an analogy similar to the one described above. KrakenD uses the bucket capacity to determine the number of requests it can serve. At the same time, it **fills the bucket with new tokens at a constant rate** while there is free space in it. Then, **users spend one token for each request**, and a token is removed from the bucket. ![](/images/documentation/krakend-token-bucket.png "Token Bucket algorithm") Users might spend the tokens faster than they are refilled. If the bucket gets empty, KrakenD rejects the requests until there is at least another token in the bucket. We call `capacity` (or **max burst**) the number of tokens you can put in the bucket and `max_rate` the speed at which you refill the bucket. The `max_rate` determines the **maximum rate users will have on average**. The `capacity` and the `max_rate` can be different. KrakenD adds a new token in the bucket every `1 ÷ max_rate` (for an `every` of `1s`). As each request is worth a token, you can serve as many requests as tokens remain in the bucket at every given time. The capacity (number of tokens) determines the maximum peak of requests you can absorb instantly. But remember that, on average, you can serve the number of requests in the `max_rate`. **When the bucket is empty**, the [Spike Arrest](/docs/throttling/spike-arrest/) policy takes place and requests rejected. ### Example scenario You want to limit users to **300 requests every minute**. A couple of ways to express this in the configuration could be: - `max_rate=300` and `every=1m`, or - `max_rate=5` and `every=1s` (300/60s=5) When you define a `max_rate=5` with `every=1s` (or its alternative above), KrakenD will refill the bucket at a speed of one token every `1s ÷ 5 = 0.2s`. So, every 0.2 seconds, the bucket receives a new token. If your `every` uses units different than seconds, internally, they are converted to seconds for this calculation. **On average** users can make five requests per second because if they push the system beyond the instant capacity, they need to wait 0.2 seconds to make another request, having the desired five every second. The `capacity` conditionates the effective number of requests you can do in a given instant as it holds the size of the bucket. Suppose you have set a `capacity` of `10`. Then, a user could make ten requests under a second during a fresh start or while the bucket is full. Even though the `max_rate` is just five per second, the user can make more requests because the bucket is not empty after five (it has five more). Has this user hacked your rate limit? No, you allowed the user to have a maximum burst of ten requests, but after the 10th request (emptied bucket), the gateway blocks the user and waits for the bucket to be refilled again at one token every 0.2 seconds. When you do your tests, you have to be aware that the capacity can allow the user to exceed your planned maximum rate in specific cases, and as it happens with money, you can spend more than you earn (credit), but not forever. --- # [Understanding Spike Arrest and Burst](https://www.krakend.io/docs/throttling/spike-arrest/) The **Spike Arrest** policy ensures a minimum time between different requests. KrakenD will enable Spike Arrest **after exhausting the burst capacity** of the rate-limiting features. ### Bursting control The bursting control is the policy that defines what to do when you reach the throttling capacity of the system within a second. When users consume content with rate-limiting enabled, the `capacity` of the rate limit defines the **bursting** they can have. Bursting makes users have a relatively higher number of requests for a short time. When this burst is exhausted, no additional requests are processed until the [Token Bucket algorithm](/docs/throttling/token-bucket/) credits the user again. ![Token Bucket image](/images/documentation/krakend-token-bucket.png) The bursting control is automatically set on [endpoint rate limiting](/docs/endpoints/rate-limit/) with a capacity equal to the rate limit, and is configurable on [backend rate limit](/docs/backends/rate-limit/). ### Spike Arrest The Spike Arrest policy defines the quickest time between two sequential requests when the users consume the maximum capacity. After an emptied bucket (capacity exhausted), the following requests are in Spike Arrest mode and will need to have a delay of at least `1 ÷ max_rate` to be processed again. Krakend will reject connections requesting content faster than this rate. Depending on the rate limit you implement, you might see rejected connections with status codes `503 Service Unavailable` or `429 Too Many Requests`. --- # [AI Gateway - API Gateway for LLMs (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/) KrakenD’s AI Gateway capabilities extend your existing API infrastructure with controls to securely route, govern, and optimize Artificial Intelligence workloads at scale. The functionality offers a straightforward integration without extra layers, products, or complexity. It brings the speed, flexibility, and clarity you are already familiar with. ## What is an AI Gateway? An AI Gateway is a specialized API Gateway designed to handle Artificial Intelligence workloads by providing secure, scalable, and manageable access to Large Language Models (LLMs) and other AI services. It acts as a control layer that routes AI-related traffic, enforces security and cost policies, and simplifies integration with multiple AI providers, helping developers manage AI services as part of their existing API ecosystem. ## Why do developers need AI control layers? As AI adoption grows, so do concerns about security, governance, cost, and complexity. Developers need a dedicated control layer to: - Protect sensitive AI data and enforce zero-trust security. - Control and monitor token usage to avoid overspending. - Manage multiple LLMs with unified, standardized configuration. - Enforce governance policies such as rate limiting, prompt validation, and compliance checks. - Simplify AI integration by abstracting vendor-specific APIs. An AI Gateway ensures AI services are integrated reliably, securely, and cost-effectively within a company’s API infrastructure. ## Overview of KrakenD capabilities for AI gateway To implement an AI Gateway with KrakenD, **you don’t need to install an additional product** as everything is already bundled in KrakenD to serve dually as an API Gateway and AI Gateway. To run AI gateway capabilities make sure your license [supports AI Gateway capabilities](/docs/enterprise/overview/license-file/#license-info). KrakenD meets multiple operational demands when AI calls are involved, enabling you to secure sensitive data, transform data, control costs, enforce governance policies, and simplify development with minimal overhead. Without reinventing your infrastructure, you can keep your stack clean and scale and effectively adopt AI traffic. The core functionality of KrakenD as an AI Gateway is classified under these four categories, each covering many use cases: 1. [Unified LLM interface](/docs/enterprise/ai-gateway/unified-llm-interface/) 2. [Security](/docs/enterprise/ai-gateway/security/) 3. [Cost Control](/docs/enterprise/ai-gateway/budget-control/) 4. [Governance](/docs/enterprise/ai-gateway/governance/) See each section for the functionalities included. ### Unified LLM interface Developers face complexity in integrating multiple LLM providers and managing proprietary APIs. KrakenD’s AI Gateway simplifies this by providing a unified configuration that abstracts vendor-specific differences for API users. With standard AI API specs, easy protocol compatibility, and built-in telemetry endpoints, development teams can build once and deploy AI applications everywhere with less friction and overhead. [Explore AI Gateway Unified LLM interface](/docs/enterprise/ai-gateway/unified-llm-interface/) ### AI Security Protecting sensitive AI data and controlling access is essential for trustworthy AI workloads. KrakenD’s AI Gateway integrates multiple layers of security at the edge to enforce **zero-trust AI operations**. From isolated authentication flows to data masking and exfiltration prevention, you can safeguard data traveling between clients and LLM providers without altering your existing API infrastructure. [Explore AI Gateway Security functionality](/docs/enterprise/ai-gateway/security/) ### Cost Control AI workloads can quickly generate unpredictable and excessive costs. The key to a successful adoption is being able to control expenses. KrakenD’s AI Gateway provides granular token usage monitoring and enforcement to keep your AI expenses transparent and within budget. [Explore AI Gateway Cost Control](/docs/enterprise/ai-gateway/budget-control/) ### Governance Strong governance policies must be in place to deploy AI responsibly at scale. KrakenD’s AI Gateway delivers comprehensive control via **multi-LLM routing**, prompt and response validation, rate limiting, and reusable prompt templates. These capabilities allow you to enforce compliance, security, and quality guardrails in line, reducing risk and standardizing AI interactions across teams or tenants. [Explore AI Governance](/docs/enterprise/ai-gateway/governance/) ### Quickstart with KrakenD AI Gateway Below is a simple example configuration snippet to enable AI Gateway capabilities in KrakenD. It includes just LLM routing, but as you can see above, there are many other components. ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "host": [ "localhost:8080" ], "echo_endpoint": true, "endpoints": [ { "endpoint": "/llm/openai", "method": "POST", "backend": [ { "host": [ "https://api.openai.com" ], "url_pattern": "/v1/chat/completions", "extra_config": { "modifier/martian": { "header.Modifier": { "scope": [ "request" ], "name": "Authorization", "value": "Bearer YOUR_OPENAI_API_KEY" } } } } ] } ] } ``` There are many other possibilities; look at the rest of the AI sections. --- # [AI Security (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/security/) Protecting sensitive AI data and controlling access is essential for trustworthy AI workloads. KrakenD’s AI Gateway integrates multiple layers of security at the edge to enforce zero-trust AI operations. From isolated authorization flows to data masking and exfiltration prevention, you can safeguard data traveling between clients and LLM providers without altering your existing API infrastructure. The following key features are explained in this document: - Isolated Authentication (JWT + API key separation) - API Key Injection (backend-only exposure) - Data Masking (request & response layers) - Exfiltration Prevention Patterns ## Isolated Authentication KrakenD separates the authentication flows between consumers and LLM on your AI endpoints. This way, you can implement different auth mechanisms and isolate consumers from vendor credentials. For instance: ![ai-gateway-isolated-auth.mmd diagram](/images/documentation/diagrams/ai-gateway-isolated-auth.mmd.svg) Although **most LLM models use API Key authentication**, you don’t need to adopt a less secure pattern when it comes to consumers, neither share your provider API key with them. Instead, you may want to integrate your user base through your existing Identity Provider and demand JWT tokens to use AI through the gateway. Then, you leave the API Key usage only between the gateway and the LLM without being exposed anywhere else. KrakenD allows you to separate the two sides with the configuration that best suits your needs. The example above would use [JWT Validation](/docs/authorization/jwt-validation/) at the endpoint level (what the consumer sees), and API Key protection at the backend level (what the gateway uses to talk to the LLM). See: - [JWT Validation](/docs/authorization/jwt-validation/) - API Key Protection (see below) ## API Key Protection Even if your API is internal and not open to the world, you might want to safeguard API keys required by LLM providers and not share secrets with the rest of the company. To do so, you can configure the gateway to **inject an Authorization header**; this API key remains in the configuration or an environment variable. This can be done using a [Martian modifier](/docs/backends/martian/#header-modifier), as depicted below: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "endpoints": [ { "endpoint": "/chatgpt", "output_encoding": "no-op", "method": "POST", "backend": [ { "host": [ "https://api.openai.com" ], "url_pattern": "/v1/chat/completions", "extra_config": { "modifier/martian": { "header.Modifier": { "scope": [ "request" ], "name": "Authorization", "value": "Bearer YOUR_OPENAI_API_KEY" } } } } ] } ] } ``` The example above would automatically add your OpenAI API key when calling the `/ChatGPT` endpoint on KrakenD. In this example, end-users would not be required to provide any kind of authentication (e.g., internal API), as the endpoint does not include JWT validation. You can test the snippet above, replacing `YOUR_OPENAI_API_KEY` with your valid API KEY and test. You can also use enable [Flexible Configuration](/docs/configuration/flexible-config/) and inject the API KEY from an env var using `"value": "Bearer {{ env "YOUR_OPENAI_API_KEY" }}"`. With this config, you can call ChatGPT: ``` curl http://localhost:8080/chatgpt \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-3.5-turbo", "messages": [ {"role": "system", "content": "Provide summarized answers and keep the language plain and simple"}, {"role": "user", "content": "Explain how photosynthesis works."} ], "temperature": 0.7 }' ``` If you have ChatGPT quota you should be able to see the response from ChatGPT without adding the authentication. For more options on how to set static headers on external requests, see the [Martian API Key modifier](/docs/backends/martian/#header-modifier) ## Data Masking & Data Loss Prevention Thanks to the [**request and response transformation components**](/docs/request-response-manipulation/) on KrakenD, you can easily apply data masking to protect sensitive information from being exposed in APIs by modifying the data before it leaves KrakenD. But also when you don’t want end-users to pass sensitive content to the LLM. There are two types of data masking: - **Dynamic data masking** (e.g., enriched on an LLM) - **Static data masking** (e.g., the gateway transforms the data itself based on patterns). Data masking is especially useful when front-end clients, external consumers, or logs shouldn’t see raw values like credit cards, SSNs, or personal identifiers. ### AI Static Response Masking In KrakenD, **data masking in the response** can be done using the [regexp modifier](/docs/enterprise/endpoints/content-replacer/#regular-expression-modifier) inside the `modifier/response-body` namespace, while in the Open Source edition you must employ Lua Scripts. This allows you to define masking rules directly in your gateway configuration. To accomplish this: - You define a series of regex-based rules targeting the field containing the potentially sensitive content - When the response passes through KrakenD, matching values are transformed - The original sensitive values never leave the gateway. Example: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "host": [ "localhost:8080" ], "echo_endpoint": true, "endpoints": [ { "endpoint": "/mask", "method": "GET", "backend": [ { "url_pattern": "/__echo/123456789", "extra_config": { "modifier/response-body": { "modifiers": [ { "regexp": { "@comment": "Example of masking SSN or credit numbers in the response body", "field": "req_uri_details.path", "find": "([\\d+\\-\\s]*)(\\d{4})", "replace": "XXX-$2" } } ] } } } ] } ] } ``` This is a simple example with a generic regular expression, but you can stack as many regexp patterns as needed by adding more modifiers to the array. The `field` attribute specifies which element of the response the replacement takes place in. As you can see, the `find` looks for any number of digits, hyphens, and spaces and replaces them with `XXX-` and the last four digits. ### AI Static Prompt Masking You can also apply **static data masking in the request** before it reaches the backend or the LLM using the [Body generator](/docs/enterprise/backends/body-generator/). Suppose your users are sending a payload that could contain something sensitive, and you would like to apply a regexp before it passes to the LLM. For instance: Term  ``` $curl -H'Content-Type: application/json' \ -d '{"message": "123456789 is a number that should never be sent"}' \ http://localhost:8080/req-mask ``` Then, you can create the template of the payload as follows: ```gotmpl {{- $message := index .req_body "message" -}} {{- $message = regexReplaceAll "([\\d+\\-\\s]*)(\\d{4})" $message "XXX-${2}" -}} {{/* Request JSON to the LLM: */}} { "message": {{ $message | toJson }} } ``` And include it in the configuration: ```json { "endpoint": "/req-mask", "method": "POST", "backend": [ { "url_pattern": "/__echo/test", "host": ["http://localhost:8080"], "extra_config": { "modifier/request-body-generator": { "path": "body_mask.tmpl", "content_type": "application/json", "debug": true } } } ] } ``` The payload the LLM would receive under these conditions would be ```json { "message": "XXX-6789 is a number that should never be sent." } ``` ### LLM-based Request and Response Transformer A more sophisticated way of masking incoming or outgoing data is to delegate the request or the response to an LLM that will manipulate the request or the response using more sophisticated mechanisms. To do this, you have to configure a [sequential proxy](/docs/endpoints/sequential-proxy/) where you: 1. Add the LLM in the first place to modify a request 2. Add the LLM in the second place (after querying your backend) to modify the response. ## Exfiltration Prevention Exfiltration prevention in KrakenD can be approached by implementing a combination of configuration rules and optional custom plugins that restrict, sanitize, or monitor outbound data to ensure that no unauthorized or sensitive data leaves your perimeter via the API gateway. As with data masking, exfiltration prevention can be static or dynamic. Here are some indications to avoid exfiltration using **static patterns**: - Use an [allow list](/docs/backends/data-manipulation/#allow) for APIs that should return a limited set of attributes - Use [Security Policies](/docs/enterprise/security-policies/) to check necessary roles and conditions to deliver content - Use [Regex Masking or Redaction](/docs/enterprise/ai-gateway/security/#data-masking--data-loss-prevention) on fields that must not be exfiltrated in full. - Use custom logic in [request and response modifier plugins](/docs/extending/plugin-modifiers/) - Instead of allowing open LLM queries, use [prompt templates](/docs/enterprise/ai-gateway/governance/#prompt-templates) For **dynamic operation**, you can delegate the output to an LLM that will refine the response. To do that, you can add a [sequential proxy](/docs/endpoints/sequential-proxy/) step as the second backend, as explained in the chapter above. --- # [AI Token Cost Control & Quotas (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/budget-control/) AI workloads can quickly generate unpredictable and excessive costs. KrakenD’s AI Gateway provides granular token usage monitoring and enforcement to keep your AI expenses transparent and within budget. Features like token quotas, budget alerts, prompt caching, and intelligent routing enable you to optimize requests and avoid surprise bills while maintaining performance and scalability. ## Token Quota and Budget Enforcement KrakenD Enterprise includes a powerful persistent quota system that’s perfect for managing token-based usage quotas in LLM applications, designer for controlling cost, enforcing subscription tiers, and preventing overuse. The quota system allows you to limit usage per user, client, or endpoint to prevent runaway costs. **See the [Quota component](/docs/enterprise/governance/quota/) for full details.** Here’s a sample of the configuration (see the documentation for all necessary blocks): ```json { "governance/quota": { "quota_name": "public_plans", "on_unmatched_tier_allow": false, "weight_key": "credits_consumed", "weight_strategy": "body", "tier_key": "X-Level", "disable_quota_headers": false, "tiers": [ { "rule_name": "rule_gold", "tier_value": "gold", "tier_value_as": "literal", "strategy": "header", "key": "X-User-Id" }, { "comment": "Special case * that catches any requests not falling into one of the tiers above", "rule_name": "rule_bronze", "tier_value_as": "*", "strategy": "ip" } ] } } ``` ## AI Metrics and reporting Through [OpenTelemetry](/docs/enterprise/telemetry/opentelemetry/) you can follow all the activity of the gateway, including connections to LLMs. If you want to follow internals like the models used, providers, etc. we recommend you to add tags to telemetry so you have a complete detail on what is going on. In addition, while there is no API available to generate reporting yet, you can follow real-time token consumption if you connect to the internal Redis database that tracks usage. --- # [AI Governance (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/governance/) KrakenD AI Governance empowers organizations to deploy large language models responsibly by enforcing compliance, security, and operational guardrails inline with AI traffic. Leverage granular controls on prompts, responses, usage, and reuse of prompt templates to standardize AI across teams, tenants, and projects. ## Prompt Policy Enforcement Prompt policies enforce constraints on input prompts by pattern matching, contextual validation, or checking request metadata to prevent abusive or sensitive content from being processed downstream. These policies can reject or modify incoming requests with custom error messages, offering a powerful line of defense integrated directly within the API Gateway. What prompt policies can do: - Enforce rules to detect and block abusive, sensitive, or off-policy prompt content - Apply pattern matching and data sanitizaion on prompt inputs - Reject or modify requests that fail validation with custom error messages Components that you can use are: - [JSON Schema Validator](/docs/endpoints/json-schema/) to make sure that payload passed adheres to your business rules, including the usage of regexp patterns or complex data structures. - [Request Body Modifier](/docs/enterprise/backends/response-body-generator/) to filter or craft the body sent before passing it to the LLM - [Limit the request size](/docs/enterprise/endpoints/maximum-request-size/#limit-the-maximum-request-size-to-an-endpoint) - [Use Prompt Templates](/docs/enterprise/ai-gateway/unified-llm-interface/#vendor-transformation) and generate the prompt instead of requesting it to the user. ## Response Guardrails Ensure responses from LLMs meet compliance and security standards by inspecting and modifying outputs inline. Response guardrails prevent revealing sensitive data or unsafe content in AI outputs. Features: - Validate response content against policies (e.g., disallow certain words or patterns) - Mask or redact sensitive information automatically - Modify responses to conform with brand or legal guidelines - Reject or flag non-compliant responses for audit Policies block or allow responses based on content rules. Redaction uses regex to mask sensitive data patterns, such as social security numbers. Proper response validation reduces compliance risks while maintaining user safety. ## Rate Limiting per tenant/team/project KrakenD AI Gateway applies fine-grained rate limiting controls to manage AI usage and prevent abuse or unexpected cost spikes. Limits can be defined per tenant, team, or project to enforce usage budgets aligned with organizational policies. Capabilities - Define concurrency and request rate caps scoped to tenant or project identifiers - Enforce limits based on API keys, JWT claims, or custom headers - Support burst capacity and smooth rate limiting behavior - Integrate with monitoring and alerting systems for visibility ## Prompt Validation Templates To promote consistency and reuse, KrakenD governance supports prompt validation templates. These templates standardize prompt structures and validation rules across teams and projects, simplifying management and ensuring compliance. Benefits: - Reuse common prompt patterns and validation logic without duplicating configs - Enforce a uniform prompt design to improve downstream AI model behavior - Enable rapid onboarding of teams with ready-made prompt templates --- # [Unified LLM Interface (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/unified-llm-interface/) The Unified LLM Interface of KrakenD allows you to interact with one or more LLMs, removing the complexity of dealing with LLMs for the end user. It allows you to set the grounds to communicate with LLMs, authenticate, and treat requests and responses back and forth. The unified LLM interface helps you to: - **Route to the right LLM** - **Abstract the request interface** - **Abstract the response interface** ## Routing to the right LLM KrakenD’s LLM Routing and AI Proxy feature enables the distribution of AI requests across one or multiple Large Language Model. It supports various routing strategies including direct proxying, conditional routing based on request headers or policies, JWT claim-based routing, and path-based routing. Read more about [LLM Routing](/docs/enterprise/ai-gateway/llm-routing/) ## Abstracting the request and response interfaces The namespace `ai/llm` makes communication with several AI providers simpler as each vendor will require a different payload and communication style. The `ai/llm` namespace is able to pre-generate **Prompt Templates** for a series of vendors: - [Google Gemini](/docs/enterprise/ai-gateway/gemini/) - [OpenAI / ChatGPT](/docs/enterprise/ai-gateway/openai/) - [Mistral](/docs/enterprise/ai-gateway/mistral/) - [Anthropic](/docs/enterprise/ai-gateway/anthropic/) - [Bedrock](/docs/enterprise/ai-gateway/bedrock/) - [Other vendors](/docs/enterprise/ai-gateway/other-vendors/) When talking to the LLM through KrakenD, the user can concentrate in the query rather than in the whole payload needed to interact with the vendor API. The gateway takes care of formatting the API request and response, and allows you to treat different LLMs using the same format, so switching a provider is an easy task. ### Prompt Templates Prompt Templates define how the system talks to each individual LLM, respecting their format and payload characteristics, allowing the user to define only the prompt. When you talk to an LLM you need to send a body in a format that is different for each vendor. We call Prompt Templates the skeleton used to build the payload you will sned to the LLM. Prompt Templates are predefined templates that contain the variables the user needs to pass. Instead of allowing the end-user to construct the full-body that the LLM will receive (and opening the door to injecting malicious system directives), we ask just for the prompt. This is not only writing a text that you send to the LLM, but actually have the opportunity to insert place holders, values that came in the request body, headers, parameters, etc. or even apply scripting over it. The Prompt Templates offer more control on the types of requests sent to the AI and allow you to specify system and user input to ensure the final output. See each vendor documentation. --- # [MCP Gateway (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/mcp-gateway/) The Model Context Protocol (MCP) Gateway in KrakenD enables transparent communication between an **MCP agent** and a **third-party MCP server** through a `no-op` endpoint. This setup allows KrakenD to forward messages with minimal processing while enriching the communication path with additional layers and functionality such as: - Adding [authorization](/docs/authorization/) and other security options - Add [observability](/docs/telemetry/) - [Throttle](/docs/throttling/) the usage of the MCP - Add [CORS](/docs/service-settings/cors/) - Add [IP filtering](/docs/enterprise/throttling/ipfilter/) - Add [security policies](/docs/enterprise/security-policies/) or [CEL expressions](/docs/endpoints/common-expression-language-cel/) with business logic - Move the passing of headers to the gateway and make the client unaware with [Martian](/docs/backends/martian/) - Limit the [payload size](/docs/enterprise/endpoints/maximum-request-size/) of the requests - And a long etcetera, check the documentation menu. Use KrakenD’s MCP Gateway to **connect MCP agents to external MCP servers** that lack sufficient Enterprise-ready features, leveraging KrakenD as a secure, observable, and manageable gateway. This feature relays MCP agent content directly to the MCP server while enabling configurable middlewares to enhance protocol flow control and monitoring. KrakenD can also be the [MCP Server](/docs/enterprise/ai-gateway/mcp-server/) if needed Example use cases: - Secure MCP Agent Communication: Inject authentication and authorization to restrict MCP agent interactions with third-party MCP servers. - Rate Limiting MCP Traffic: Protect MCP servers from overload by applying rate limits at the gateway. - Observability and Logging: Add tracing and metrics on MCP message exchanges to improve operational insight. - Enterprise MCP Server: For deployments needing an in-house MCP server, KrakenD Enterprise includes the MCP Server feature that can be integrated or proxied using this gateway. ## MCP Gateway configuration To configure an endpoint in KrakenD acting as an MCP gateway to forward raw MCP messages, you only need a configuration like this: ```json { "endpoints": [ { "endpoint": "/mcp", "output_encoding": "no-op", "input_headers": [ "HeaderNeededByYourMCP" ], "method": "POST", "backend": [ { "url_pattern": "/mcp", "host": [ "https://third-party-mcp-server.local" ] } ] }, { "endpoint": "/mcp", "output_encoding": "no-op", "input_headers": [ "HeaderNeededByYourMCP" ], "method": "GET", "@comment": "The high timeout avoids repeating connections on SSE", "timeout": "5m", "backend": [ { "url_pattern": "/mcp", "host": [ "https://third-party-mcp-server.local" ] } ] }, { "endpoint": "/mcp", "output_encoding": "no-op", "input_headers": [ "HeaderNeededByYourMCP" ], "method": "DELETE", "backend": [ { "url_pattern": "/mcp", "host": [ "https://third-party-mcp-server.local" ] } ] } ] } ``` Line-by-Line: - `method`: Three endpoints, as we want to allow MCP communication through `GET`, `POST` and `DELETE`. Being the `POST` the most important. - `output_encoding`: The `no-op` encoding allows transparent MCP communication. This is a must. - `input_headers`: Depending on the MCP technology, you might need forwarding headers to the MCP server. Or you can add [Martian](/docs/backends/martian/) and inject them at the gateway level if they have constant values and you don’t want the MCP agent to know. - `endpoint`: `/mcp` defines the KrakenD endpoint exposed to MCP agents, whatever route you want this to be. - `host`: Remote MCP server address to forward MCP messages. Supported MCP Server types The MCP server connected to KrakenD must be HTTP-based, like JSONRPC or SSE. Stdio communication is out of scope for this functionality. If you plan to use SSE, make sure to add also a `timeout` with a value large enough to not close the session on the `GET` method only. From here, you can add Authorization, Telemetry, Rate Limiting, or any other KrakenD component that is compatible with no-op. KrakenD’s MCP Gateway feature enables infrastructure teams to transparently connect MCP agents to third-party MCP servers while adding vital control layers such as authorization, observability, and rate limiting. The no-op proxy behavior ensures the integrity of the MCP protocol, enabling integration and operational management. For advanced MCP deployments, KrakenD Enterprise offers a native [MCP Server](/docs/enterprise/ai-gateway/mcp-server/) option that complements or replaces third-party MCP resources. --- # [MCP Server (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/mcp-server/) The MCP Server feature **exposes any of your existing use cases as MCP tools** so you can allow to **AI MCP Agents to access any of the systems you have already**, or create new ones easily, whether it is based on legacy services and data or not. With the MCP Server of KrakenD, you instantly add MCP to all your stack regardless of their native MCP compatibility or not. This unlocks effortless AI-driven integrations by bridging any service with MCP through declarative configuration. ![ai-gateway-mcp-server.mmd diagram](/images/documentation/diagrams/ai-gateway-mcp-server.mmd.svg) As KrakenD can expose through MCP any kind of service, you can start offering **endless use cases**, and is a game changer to start publishing AI services inside enterprise architectures with no effort. The key concepts of the MCP server are: - **Build on-the-fly MCP servers**: Declare and run MCP servers that fetch and send data from and to multiple places. - **Protocol translation**: Offer MCP, consume anything. E.g., connect to a SOAP service and offer it to Claude via MCP. - **KrakenD ecosystem compatibility**: MCP servers are first-class citizens and enjoy authorization, throttling, data modification, validation, caching, monitoring, workflows and everything else on KrakenD. - **Declarative simplicity**: Minimal config additions get powerful AI-optimized APIs in front of any service. - **Multi-tool support**: Expose multiple AI tools with individual input/output JSON schemas and workflows per MCP server, including mediatypes like audio and images. - **Stateless or stateful operations**: Choose behavior to align with backend semantics, **SSE available** too. ## MCP Server configuration overview To start serving MCP on KrakenD, two main configuration sections define MCP Servers: 1. **MCP server declaration** (root `extra_config` with `ai/mcp`) 2. **MCP entrypoint configuration** (endpoint’s `extra_config` with `ai/mcp`) ## MCP Server configuration The `ai/mcp` namespace at the root level contains all the configuration related to MCP. In this section you declare the list of servers available to agents, and plug them to specific services. Here’s a real world configuration example you can test: ```json { "$schema": "https://krakend.io/schema/v2.12/krakend.json", "version": 3, "extra_config": { "ai/mcp": { "servers": [ { "instructions": "Provides the current state of city webcams", "name": "webcam-status", "title": "Webcam tools", "version": "1.0.1", "tools": [ { "name": "rovaniemi-webcam", "title": "Rovaniemi webcam", "description": "Get the latest image from the Rovaniemi webcam", "type": "image", "workflow": { "endpoint": "/__mcp/rovaniemi-webcam", "output_encoding": "no-op", "backend": [ { "url_pattern": "/vi/Cp4RRAEgpeU/0.jpg", "host": [ "https://img.youtube.com" ] } ] } } ] } ] } }, "endpoints": [ { "endpoint": "/mcp", "method": "POST", "timeout": "10s", "backend": [ { "url_pattern": "/ignore", "host": [ "ignore" ] } ], "extra_config": { "ai/mcp": { "server_name": "webcam-status" } } } ] } ``` The configuration above will allow you an interaction like this: ![Example of MCP tool call from Claude Desktop](/images/documentation/ai/mcp-tool-example.png) The list of available fields is: ##### Fields of MCP functionality \* required fields ##### [`servers`](https://www.krakend.io/docs/enterprise/ai-gateway/mcp-server/#servers "Click to copy link to servers") * array of objects The array of MCP servers available for linking to endpoints. Each object represents a different MCP server. The entry is only the definition of the server. You must create an endpoint that serves as the entrypoint to each server. Each item of servers accepts the following properties: ##### [`instructions`](#servers-instructions "Click to copy link to instructions") string The instructions are key for the AI client to understand how to use this server. Defaults to `""` ##### [`json_response`](#servers-json_response "Click to copy link to json_response") boolean Set to true if you want the response to be in JSON format. If set to false, the response will be in SSE format (text/event-stream). Defaults to `false` ##### [`name`](#servers-name "Click to copy link to name") * string A unique name for MCP server configuration. This name is an identifier used in the logs or when a title is not provided. Example: `"stories-tracker"` ##### [`ping_period`](#servers-ping_period "Click to copy link to ping_period") string Krakend will ping all connected clients from time to time to determine if the session must be kept alive. This duration strings sets the ping period KrakenD will do to its connected clients. When you don’t set a value, there is no ping process and KrakenD is unaware of disconnected clients, so it won’t report live notifications. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"0s"` ##### [`stateless`](#servers-stateless "Click to copy link to stateless") boolean A stateless server does not validate the Mcp-Session-Id header, and uses a temporary session with default initialization parameters. Any server->client request is rejected immediately as there’s no way for the client to respond. Server->Client notifications may reach the client if they are made in the context of an incoming request Defaults to `false` ##### [`title`](#servers-title "Click to copy link to title") * string The description of the purpose of this MCP Server. Provide a meaningful description to help AI understand its use case. Example: `"Story tracker tools"` ##### [`tools`](#servers-tools "Click to copy link to tools") array of objects The Model Context Protocol (MCP) allows servers to expose tools ([RFC](https://modelcontextprotocol.io/specification/2025-06-18/server/tools)) that can be invoked by language models. Tools enable models to interact with internal/external systems. Each tool is uniquely identified by a name and includes metadata describing its schema. Each item of tools accepts the following properties: ##### [`description`](#tools-description "Click to copy link to description") * string The description of the purpose of this MCP tool. Provide a meaningful description to help AI understand its use case. Example: `"Get a list of open stories in tracker matching a search query"` ##### [`input_headers`](#tools-input_headers "Click to copy link to input_headers") array of strings Defines the list of all headers allowed to reach the backend when passed. By default, KrakenD won’t pass any header from the client to the backend. This list is **case-insensitive**. You can declare headers in lowercase, uppercase, or mixed. An entry `["Cookie"]` forwards all cookies, and a single star element `["*"]` as value forwards everything to the backend (**it’s safer to avoid this option**), including cookies. See [headers forwarding](https://www.krakend.io/docs/endpoints/parameter-forwarding/#headers-forwarding) Defaults to `[]` ##### [`input_schema`](#tools-input_schema "Click to copy link to input_schema") object The input JSON schema to validate the input parameters sent by the AI client. The schema is **essential** in most clients to understand how to pass parameters to the MCP server. The schema must be a valid [2020-12 draft JSON Schema](https://json-schema.org/understanding-json-schema/) object. ##### [`name`](#tools-name "Click to copy link to name") * string A unique name for MCP tool configuration. This name is an identifier used in the logs or when a title is not provided. Example: `"stories-list"` ##### [`output_schema`](#tools-output_schema "Click to copy link to output_schema") object The input JSON schema to validate the output parameters sent back to the AI client. The schema must be a valid [2020-12 draft JSON Schema](https://json-schema.org/understanding-json-schema/) object. ##### [`return_error_msg`](#tools-return_error_msg "Click to copy link to return_error_msg") boolean Set to true if you want the error message to be returned to the AI client. If set to false, the AI client will only receive a generic error message. Defaults to `false` ##### [`title`](#tools-title "Click to copy link to title") * string The description of the purpose of this MCP tool. Provide a meaningful text to help AI understand its use case. Example: `"Story list"` ##### [`type`](#tools-type "Click to copy link to type") The type of the response media. Possible values are: `"text"` , `"audio"` , `"image"` Defaults to `"text"` ##### [`workflow`](#tools-workflow "Click to copy link to workflow") * object This is the action/pipeline performed by the tool. While you can add a single action to the tool, because actions can be chained and have complex logic, we have called this a workflow, whether is connecting to a single remote service, many, aggreggating, or any other functionality supported by KrakenD. See the [Workflow syntax](https://www.krakend.io/docs/enterprise/endpoints/workflows/) ##### [`version`](#servers-version "Click to copy link to version") * string The version of the logic behind your MCP Server implementation. This version makes sense to your business and is not linked to anything else. Example: `"0.2.1"` Schema: [https://www.krakend.io/schema/v2.13/ai/mcp.json](https://www.krakend.io/schema/v2.13/ai/mcp.json "JSON schema definition") ## MCP Entrypoint configuration Finally, you need to provide a door to use the MCP servers you have defined. Endpoints serve as entrypoints, and you declare a route (most vendors use `/mcp`), and then plug it to the server you have defined above. Make sure to reuse the exact name you wrote in the server definition. For example: ```json { "version": 3, "endpoints": [ { "endpoint": "/mcp", "method": "POST", "timeout": "10s", "backend": [ { "url_pattern": "/ignore", "host": [ "ignore" ] } ], "extra_config": { "ai/mcp": { "server_name": "webcam-status" } } }, { "endpoint": "/mcp", "method": "DELETE", "@comment": "Allows the client to mark the session as deleted to free up memory", "backend": [ { "url_pattern": "/ignore", "host": [ "ignore" ] } ], "extra_config": { "ai/mcp": { "server_name": "webcam-status" } } } ] } ``` ##### Fields of MCP endpoint \* required fields ##### [`server_name`](https://www.krakend.io/docs/enterprise/ai-gateway/mcp-server/#server_name "Click to copy link to server_name") * The MCP server you want to attach to this endpoint. When you add this namespace, the endpoint becomes the MCP server entry point URL. The name used must match the name in the `ai/mcp` configuration in the root level. Example: `"stories-tracker"` Schema: [https://www.krakend.io/schema/v2.13/ai/mcp\_endpoint.json](https://www.krakend.io/schema/v2.13/ai/mcp_endpoint.json "JSON schema definition") You can write any value on the `host` and `url_pattern` of your backend, as once you add the `ai/mcp` these become irrelevant (the endpoint connects to the MCP server you just created). On the `timeout` make sure to use a value that is suitable for the services you are exposing through MCP. --- # [LLM Routing (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/llm-routing/) KrakenD’s AI Proxy and LLM routing feature enables you to distribute AI requests across one or multiple Large Language Model providers or instances. LLM Routing on KrakenD supports both single-provider and multi-provider. You can configure endpoints that can connect to a specific LLM model, or based on policies, change the model on the fly, or they even make simultaneous requests to different providers to aggregate their responses. To implement single or multi-LLM routing in KrakenD, like dynamically selecting between different Large Language Model (LLM) providers like OpenAI, Mistral, Anthropic, or custom models, there are several clean, scalable strategies depending on how you want to choose the provider: - Just proxy to the AI vendor - Conditional routing - Header-based routing - JWT claim-based routing - Path-based routing - Other non-standard routing logic ## AI Proxy Not a recommended option, but in case you want to couple your application to the LLM vendor, and let the user to use directly the LLM vendor API with no transformation or adaptation on KrakenD, you can treat the provider as a regular `no-op` backend with no specific AI configuration. In addition to the proxying, you might want to hide the authentication part on KrakenD, and for that you can use [Martian headers](/docs/enterprise/backends/martian/). The following example is a direct consumption of OpenAI API through KrakenD with no modification other than doing the auto-authentication (caution, this endpoint would be public as there is no auth validation): ```json { "endpoint": "/chat", "output_encoding": "no-op", "method": "POST", "backend": [ { "host": [ "https://api.openai.com" ], "url_pattern": "/v1/chat/completions", "encoding": "no-op", "extra_config": { "modifier/martian": { "header.Modifier": { "scope": [ "request" ], "name": "Authorization", "value": "Bearer YOUR_OPENI_API_KEY" } } } } ] } ``` ## Conditional Routing The [Conditional Routing](/docs/enterprise/backends/conditional/) allows you to set simple or complex policies defining which backend should the gateway hit and fallback to an alternative when policies do not evaluate to true. The idea is that you define multiple backends on a single endpoint, and set the policies that will satisfy the usage of a specific backend. If your policies are not mutually exclusive, you can connect to multiple LLMs too. The **optional** `fallback` option allows you to connect to a default backend when the rest of the conditions failed. Here’s a practical example of conditional routing, that based on a request header `X-Model` decides whether to get content from Gemini, OpenAI, or fallback to Anthropic for any other case: ```json { "endpoint": "/llm", "input_headers": [ "X-Model" ], "backend": [ { "host": [ "https://generativelanguage.googleapis.com" ], "url_pattern": "/v1beta/models/gemini-2.5-flash:generateContent", "method": "POST", "extra_config": { "backend/conditional": { "strategy": "header", "name": "X-Model", "value": "gemini" }, "ai/llm": { "gemini": { "v1beta": { "credentials": "XXX_YYY_ZZZ", "debug": true, "variables": { "candidates": 3 } } } } } }, { "host": [ "https://api.openai.com" ], "url_pattern": "/v1/responses", "method": "POST", "extra_config": { "backend/conditional": { "strategy": "header", "name": "X-Model", "value": "openai" }, "ai/llm": { "openai": { "v1": { "credentials": "xx-yy-zz", "debug": false, "variables": { "model": "gpt-5-nano" } } } } } }, { "url_pattern": "/v1/messages", "host": [ "https://api.anthropic.com" ], "extra_config": { "backend/conditional": { "strategy": "fallback" }, "ai/llm": { "anthropic": { "v1": { "credentials": "xxxxx", "debug": false, "variables": { "model": "claude-3-7-sonnet-latest" } } } } } } ] } ``` For more information on how to do conditions, including writing custom policies (the example only checks literals on headers), see the [Conditional Routing documentation](/docs/enterprise/backends/conditional/) With the example above (and setting your credentials in the config) you could interact with the three different models like this: Routing to the right LLM  ``` $curl -XPOST -H'X-Model' --json '{"instructions": "Act as a 1000 dollar consultant", "input": "Tell me a consultant joke"}' http://localhost:8080/llm ``` ### Header-based LLM routing While this can be done with the Conditional Routing too, you can use [Dynamic Routing](/docs/enterprise/endpoints/dynamic-routing/) as another approach to allow end-users set a specific header with the desired model to use, and from here, the gateway chooses the route. For instance, say you want to integrate with two providers and let the consumer of the API Gateway decide which one to use based on a header: ![ai-gateway-proxy-header.mmd diagram](/images/documentation/diagrams/ai-gateway-proxy-header.mmd.svg) The consumer would send a request like this: ``` POST /multi-llm X-LLM: openai ``` And the gateway uses the header’s value to route to the appropriate backend. ### JWT claim-based LLM routing Similarly to header routing, you can use data in claims to route to the proper LLM. For instance, when you issue a JWT token, you could add a claim that specifies what LLM to use based on role, business policies, etc. As opposed to header-based routing, this method is transparent to the user, who does not have any control over the model used, because it is enforced by a policy in the Identity Provider. It would work with the same configuration you use in the header-based routing, only that you use the JWT instead: ```json { "url_pattern": "/_llm/{jwt.preferred_ai_model}" } ``` Another more sophisticated way of JWT routing, would be using a policy. As the policies do not have direct access to JWT data, you need to propagate the desired claims as headers and then apply the policy. For instance, notice the `propagate_claims` that is later used on the `backend/conditional`’s policy: ```json { "endpoint": "/jwt-routing-policy", "method": "POST", "extra_config": { "auth/validator": { "alg": "RS256", "jwk_url": "https://some-domain.example.com/.well-known/jwks.json", "cache": true, "@comment": "Take the claim ai_model from the JWT and send it as header.", "propagate_claims": [ [ "ai_model", "X-Ai-Model" ], [ "ai_enable", "X-Ai-Enable" ] ] } }, "input_headers": [ "X-Ai-Model" ], "backend": [ { "method": "POST", "host": ["https://api.openai.com"], "url_pattern": "/v1/responses", "extra_config": { "backend/conditional": { "strategy": "policy", "value": "req_header('X-Ai-Model') == 'openai' && req_header('X-Ai-Enable') == 'yes'" }, "ai/llm": { "openai": { "credentials": "xxxx", "version": "v1", "debug": false, "variables": { "model": "gpt-5-nano" } } } } }, { "url_pattern": "/condition-B-non-AI", "host": ["http://localhost:8080"], "method": "POST", "extra_config": { "backend/conditional": { "strategy": "fallback" } } } ] } ``` ### Path-based LLM routing This type of routing is delegated to the end-user, and is the simplest way of routing because you directly declare the endpoints you want to use, e.g.: - `/llm/openai` - `/llm/mistral` Then the user calls one endpoint or the other, and the API offers one endpoint per provider. --- # [OpenAI Integration (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/openai/) The OpenAI interface allows KrakenD to use [OpenAI’s API](https://platform.openai.com/docs/overview) **without writing custom integration code**, enabling intelligent automation, content generation, or any LLM-powered use case within your existing API infrastructure. This component abstracts you from the OpenAI API usage allowing the consumer to concentrate on the prompt only, as for each request to an endpoint, KrakenD will create the **OpenAI request** with all the necessary elements in their API, and will return a **unified response**, so if you use other vendors you have a consitent use of LLM models. In other words, the user sends the content, like “tell me a joke!”, and then KrakenD builds the API payload necessary to talk to OpenAI. This OpenAI interface configures a backend within KrakenD that transparently forwards REST requests to OpenAI’s API endpoints. It manages authentication, versioning, and payload formatting using its custom templating system. This way, you can easily call OpenAI models without writing custom integration code. A simple configuration looks like this: ```json { "endpoint": "/openai", "method": "POST", "backend": [ { "host": ["https://api.openai.com"], "url_pattern": "/v1/responses", "method": "POST", "extra_config": { "ai/llm": { "openai": { "v1": { "credentials": "xx-yy-zz", "debug": false, "variables": { "model": "gpt-5-nano" } } } } } } ] } ``` To interact with the LLM, the user can send in the request: - `instructions` (optional): If you want to add a system prompt - `contents`: The content you want to send to the template Like this: Using the endpoint  ``` $curl -XPOST --json '{"instructions": "Act as a 1000 dollar consultant", "contents": "Tell me a consultant joke"}' http://localhost:8080/openapi ``` ## Configuration of OpenAI The configuration of OpenAI requires you to add under your backend `extra_config` the `ai/llm` namespace with the `openai` vendor. ##### Fields of OpenAI integration \* required fields ##### [`v1`](https://www.krakend.io/docs/enterprise/ai-gateway/openai/#v1 "Click to copy link to v1") All settings depend on a specific version, as the vendor might change the API over time. ##### [`credentials`](#v1-credentials "Click to copy link to credentials") * string Your OpenAI API key. You can set it as an environment variable for better security. Example: `"sk-xxxx"` ##### [`debug`](#v1-debug "Click to copy link to debug") boolean Enables the debug mode to log activity for troubleshooting. Do not set this value to true in production as it may log sensitive data. Defaults to `false` ##### [`input_template`](#v1-input_template "Click to copy link to input_template") string A path to a custom [Go template](https://pkg.go.dev/text/template) that sets the payload format sent to OpenAI. You don’t need to set this value unless you want to override the default template making use of all the `variables` listed in this configuration. ##### [`output_template`](#v1-output_template "Click to copy link to output_template") string A path to a custom [Go template](https://pkg.go.dev/text/template) that sets how the response from OpenAI is transformed before being sent to the client. The default template extracts the text from the first choice returned by OpenAI so in most cases you don’t need to set a custom output template. ##### [`variables`](#v1-variables "Click to copy link to variables") * object The variables specific to the OpenAI usage that are used to construct the payload. ##### [`extra_payload`](#variables-extra_payload "Click to copy link to extra_payload") object A map of additional payload attributes you want to use in your custom `input_template` (this payload is not used in the default template). The attributes set here are accessible in your custom template as `{{ .variables.extra_payload.yourchosenkey }}`. This option helps adding rare customization and future attributes. ##### [`max_output_tokens`](#variables-max_output_tokens "Click to copy link to max_output_tokens") integer An upper bound for the number of tokens that can be generated for a response, including visible output tokens and reasoning tokens. Setting this value to `0` does not set any limit. ##### [`model`](#variables-model "Click to copy link to model") * string The name of the OpenAI model you want to use. The value you provide is passed as is to OpenAI and KrakenD does not prove if the model is currently accepted by the vendor. Check the available models on OpenAI documentation. Examples: `"gpt-5-nano"` , `"gpt-4"` ##### [`temperature`](#variables-temperature "Click to copy link to temperature") number What sampling temperature to use, between `0` and `2`. Higher values like `0.8` will make the output more random, while lower values like `0.2` will make it more focused and deterministic. ##### [`top_p`](#variables-top_p "Click to copy link to top_p") number The nucleus sampling, where the model considers the results of the tokens with top\_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. ##### [`truncation`](#variables-truncation "Click to copy link to truncation") The strategy to use when truncating messages to fit within the model’s context length (, the model will truncate the response to fit the context window by dropping items from the beginning of the conversation. Possible values are: `"auto"` , `"disabled"` Defaults to `"disabled"` Schema: [https://www.krakend.io/schema/v2.13/ai/openai.json](https://www.krakend.io/schema/v2.13/ai/openai.json "JSON schema definition") ## Customizing the payload sent and received from OpenAI As it happens with all LLM interfaces of KrakenD, you can completely replace the request and the response so you have a custom interaction with the LLM. While the default template should allow you to accomplish any day to day job, you might need to extend it using your own template. You may override the input and output Go templates by specifying: - `input_template`: Path to a custom template controlling how the request data is formatted before sending to OpenAI. - `output_template`: Path to a custom template to transform and extract the desired pieces from OpenAI’s response. See below how to change this interaction ### Default input\_template for v1 When you don’t set any `input_template`, KrakenD will create the JSON payload sent to OpenAI using the following template: ```gotmpl { "model": {{ .variables.model | toJson }}, {{ $max_output_tokens := .variables.max_output_tokens }}{{ if ge $max_output_tokens 0 }}"max_output_tokens": {{ $max_output_tokens }},{{ end }} {{ $temperature := .variables.temperature }}{{ if ge $temperature 0.0 }}"temperature": {{ $temperature }},{{ end }} {{ $top_p := .variables.top_p }}{{ if ge $top_p 0.0 }}"top_p": {{ $top_p }},{{ end }} "stream": false, "truncation": "disabled", {{ if hasKey .req_body "instructions" }}"instructions": {{ .req_body.instructions | toJson }},{{ end }} "input": {{ .req_body.contents | toJson }} } ``` Remember you can access your own `variables` declared in the configuration using `{{ .variables.xxx }}`. ### Default output\_template for v1 When you don’t declare an `output_template`, the response from the AI is transformed to have the following format: ```gotmpl { "ai_gateway_response": [ {{ if gt (len .resp_body.output) 0 }} { "contents": [ {{ range $output := .resp_body.output }} {{ if hasKey $output "content" }} {{ range $index, $part := $output.content }} {{ if $index }},{{ end }} {{ $part.text | toJson }} {{ end }} {{ end }} {{ end }} ] } {{ end }} ], "usage": "{{ .resp_body.usage.total_tokens }}" } ``` --- # [Google Gemini integration (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/gemini/) The Gemini interface allows KrakenD to use [Gemini’s API](https://ai.google.dev/gemini-api/docs) **without writing custom integration code**, enabling intelligent automation, content generation, or any LLM-powered use case within your existing API infrastructure. This component abstracts you from the Gemini API usage allowing the consumer to concentrate on the prompt only, as for each request to an endpoint, KrakenD will create the **Gemini request** with all the necessary elements in their API, and will return a **unified response**, so if you use other vendors you have a consitent use of LLM models. In other words, the user sends the content, like “tell me a joke!”, and then KrakenD builds the API payload necessary to talk to Gemini. This Gemini interface configures a backend within KrakenD that transparently forwards REST requests to Gemini’s API endpoints. It manages authentication, versioning, and payload formatting using its custom templating system. This way, you can easily call Gemini models without writing custom integration code. A simple configuration looks like this: ```json { "endpoint": "/gemini", "method": "POST", "backend": [ { "host": [ "https://generativelanguage.googleapis.com" ], "url_pattern": "/v1beta/models/gemini-2.5-flash:generateContent", "method": "POST", "extra_config": { "ai/llm": { "gemini": { "v1beta": { "credentials": "XXX_YYY_ZZZ", "debug": true, "variables": { "candidates": 3 } } } } } } ] } ``` To interact with the LLM, the user can send in the request: - `instructions` (optional): If you want to add a system prompt - `contents`: The content you want to send to the template Like this: Using the endpoint  ``` $curl -XPOST --json '{"instructions": "Act as a 1000 dollar consultant", "contents": "Tell me a consultant joke"}' http://localhost:8080/gemini ``` ## Configuration of Gemini The configuration of Gemini requires you to add under your backend `extra_config` the `ai/llm` namespace with the `Gemini` vendor. ##### Fields of Google Gemini integration \* required fields ##### [`v1beta`](https://www.krakend.io/docs/enterprise/ai-gateway/gemini/#v1beta "Click to copy link to v1beta") object All settings depend on a specific version, as the vendor might change the API over time. ##### [`credentials`](#v1beta-credentials "Click to copy link to credentials") * string Your Google Gemini API key. You can set it as an environment variable for better security. ##### [`debug`](#v1beta-debug "Click to copy link to debug") boolean Enables the debug mode to log activity for troubleshooting. Do not set this value to true in production as it may log sensitive data. Defaults to `false` ##### [`input_template`](#v1beta-input_template "Click to copy link to input_template") string A path to a custom [Go template](https://pkg.go.dev/text/template) that sets the payload format sent to Google Gemini. You don’t need to set this value unless you want to override the default template making use of all the `variables` listed in this configuration. ##### [`output_template`](#v1beta-output_template "Click to copy link to output_template") string A path to a custom [Go template](https://pkg.go.dev/text/template) that sets how the response from Google Gemini is transformed before being sent to the client. The default template extracts the text from the first choice returned by Google Gemini so in most cases you don’t need to set a custom output template. ##### [`variables`](#v1beta-variables "Click to copy link to variables") object The variables specific to the Google Gemini usage that are used to construct the payload. ##### [`candidate_count`](#variables-candidate_count "Click to copy link to candidate_count") integer An integer value that specifies how many different completions (responses) the model should generate for a single input prompt. This can be useful for exploring multiple variations of the output. Defaults to `1` ##### [`extra_payload`](#variables-extra_payload "Click to copy link to extra_payload") object A map of additional payload attributes you want to use in your custom `input_template` (this payload is not used in the default template). The attributes set here are accessible in your custom template as `{{ .variables.extra_payload.yourchosenkey }}`. This option helps adding rare customization and future attributes. ##### [`max_output_tokens`](#variables-max_output_tokens "Click to copy link to max_output_tokens") integer Maximum number of tokens that can be generated in the response. A token is approximately four characters. 100 tokens correspond to roughly 60-80 words. ##### [`stop_sequences`](#variables-stop_sequences "Click to copy link to stop_sequences") array of strings An array of sequences where the model will stop generating further tokens if found. This can be useful to control the length and content of the output. ##### [`temperature`](#variables-temperature "Click to copy link to temperature") number The temperature is used for sampling during response generation, which occurs when topP and topK are applied. Temperature controls the degree of randomness in token selection. Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. ##### [`top_k`](#variables-top_k "Click to copy link to top_k") integer Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model’s vocabulary (also called greedy decoding), while a top-K of 3 means that the next token is selected from among the three most probable tokens by using temperature. ##### [`top_p`](#variables-top_p "Click to copy link to top_p") number Top-P changes how the model selects tokens for output. Tokens are selected from the most probable to least probable until the sum of their probabilities equals the top-P value. For example, if tokens A, B, and C have a probability of 0.3, 0.2, and 0.1 and the top-P value is 0.5, then the model will select either A or B as the next token by using temperature and excludes C as a candidate. Schema: [https://www.krakend.io/schema/v2.13/ai/gemini.json](https://www.krakend.io/schema/v2.13/ai/gemini.json "JSON schema definition") ## Customizing the payload sent and received from Gemini As it happens with all LLM interfaces of KrakenD, you can completely replace the request and the response so you have a custom interaction with the LLM. While the default template should allow you to accomplish any day to day job, you might need to extend it using your own template. You may override the input and output Go templates by specifying: - `input_template`: Path to a custom template controlling how the request data is formatted before sending to Gemini. - `output_template`: Path to a custom template to transform and extract the desired pieces from Gemini’s response. See below how to change this interaction ### Default input\_template for Gemini v1beta When you don’t set any `input_template`, KrakenD will create the JSON payload sent to Gemini using the following template: ```gotmpl { "generationConfig": { {{ $max_tokens := .variables.max_tokens }}{{ if ge $max_tokens 0 }}"maxOutputTokens": {{ $max_tokens }},{{ end }} {{ $temperature := .variables.temperature }}{{ if ge $temperature 0.0 }}"temperature": {{ $temperature }},{{ end }} {{ $top_p := .variables.top_p }}{{ if ge $top_p 0.0 }}"topP": {{ $top_p }},{{ end }} {{ $top_k := .variables.top_k }}{{ if ge $top_k 0 }}"topK": {{ $top_k }},{{ end }} "candidateCount": {{ .variables.candidate_count | toJson }}, "stopSequences": {{ .variables.stop_sequences | toJson }} }, {{- if hasKey .req_body "instructions" }} "system_instruction": { "parts": [ { "text": {{ .req_body.instructions | toJson }} } ] }, {{ end }} "contents": [ { "parts": [ { "text": {{ .req_body.contents | toJson }} } ] } ] } ``` Remember you can access your own `variables` declared in the configuration using `{{ .variables.xxx }}`. ### Default output\_template for Gemini v1beta When you don’t declare an `output_template`, the response from the AI is transformed to have the following format: ```gotmpl { "ai_gateway_response": [ {{ if gt (len .resp_body.candidates) 0 }} { "contents": [ {{ range $ci, $candidate := .resp_body.candidates }} {{ range $index, $part := $candidate.content.parts }} {{ if $ci }},{{ end }} {{ $part.text | toJson }} {{ end }} {{ end }} ] } {{ end }} ], "usage": "{{ .resp_body.usageMetadata.totalTokenCount }}" } ``` --- # [Mistral Integration (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/mistral/) The Mistral integration enables KrakenD to talk to Mistral’s AI services, allowing you to embed powerful language model capabilities into your API workflows without custom coding. Use this interface when you want to connect Mistral for tasks like intelligent automation, conversational AI, content generation, or other LLM-driven functionalities inside your existing API infrastructure. This component abstracts the complexities of communicating with [Mistral’s AI API](https://docs.mistral.ai/api/). When an API request hits your KrakenD endpoint configured with the Mistral interface, KrakenD automatically constructs the necessary Mistral API payload, handles authentication, and processes the responses uniformly. This consistent integration also allows if needed to switch between different LLM vendors without changing client-facing interfaces. In essence, the user sends the textual content or instructions, and KrakenD manages the complete interaction with Mistral behind the scenes. ## Configuration of the Mistral integration To enable the Mistral interface, you add a backend configuration in KrakenD with the `extra_config` key under the `ai/llm` namespace. Example: ```json { "endpoint": "/mistral", "method": "POST", "backend": [ { "host": [ "https://api.mistral.ai" ], "url_pattern": "/v1/chat/completions", "method": "POST", "extra_config": { "ai/llm": { "mistral": { "v1": { "credentials": "xxxxxxxxx", "debug": false, "variables": { "model": "mistral-small-latest" } } } } } } ] } ``` To interact with the LLM, the user can send in the request: - `instructions` (optional): If you want to add a system prompt - `contents`: The content you want to send to the template Like this: Using the endpoint  ``` $curl -XPOST --json '{"instructions": "Act as a 1000 dollar consultant", "contents": "Tell me a consultant joke"}' http://localhost:8080/mistral ``` The configuration options are: ##### Fields of Mistral integration \* required fields ##### [`v1`](https://www.krakend.io/docs/enterprise/ai-gateway/mistral/#v1 "Click to copy link to v1") All settings depend on a specific version, as the vendor might change the API over time. ##### [`credentials`](#v1-credentials "Click to copy link to credentials") * string Your Mistral API key. You can set it as an environment variable for better security. ##### [`debug`](#v1-debug "Click to copy link to debug") boolean Enables the debug mode to log activity for troubleshooting. Do not set this value to true in production as it may log sensitive data. Defaults to `false` ##### [`input_template`](#v1-input_template "Click to copy link to input_template") string A path to a custom [Go template](https://pkg.go.dev/text/template) that sets the payload format sent to Mistral. You don’t need to set this value unless you want to override the default template making use of all the `variables` listed in this configuration. ##### [`output_template`](#v1-output_template "Click to copy link to output_template") string A path to a custom [Go template](https://pkg.go.dev/text/template) that sets how the response from Mistral is transformed before being sent to the client. The default template extracts the text from the first choice returned by Mistral so in most cases you don’t need to set a custom output template. ##### [`variables`](#v1-variables "Click to copy link to variables") * object The variables specific to the Mistral usage that are used to construct the payload. ##### [`extra_payload`](#variables-extra_payload "Click to copy link to extra_payload") object A map of additional payload attributes you want to use in your custom `input_template` (this payload is not used in the default template). The attributes set here are accessible in your custom template as `{{ .variables.extra_payload.yourchosenkey }}`. This option helps adding rare customization and future attributes. ##### [`max_tokens`](#variables-max_tokens "Click to copy link to max_tokens") integer An upper bound for the number of tokens that can be generated for a response, including visible output tokens and reasoning tokens. ##### [`model`](#variables-model "Click to copy link to model") * string The name of the Mistral model you want to use. The value you provide is passed as is to Mistral and KrakenD does not prove if the model is currently accepted by the vendor. Check the available models on Mistral documentation. Examples: `"mistral-small-latest"` , `"codestral-latest"` ##### [`n`](#variables-n "Click to copy link to n") integer An integer value that specifies how many different completions (responses) the model should generate for a single input prompt. This can be useful for exploring multiple variations of the output. Defaults to `1` ##### [`random_seed`](#variables-random_seed "Click to copy link to random_seed") integer An integer value to seed the random number generator used by the model. Setting a specific seed can help produce reproducible results across different requests. ##### [`safe_prompt`](#variables-safe_prompt "Click to copy link to safe_prompt") boolean A boolean flag to enable or disable Mistral’s safe prompt feature, which helps filter out inappropriate or harmful content from the model’s responses. By default, this feature is enabled to ensure safer interactions. Defaults to `false` ##### [`stop`](#variables-stop "Click to copy link to stop") array of strings An array of sequences where the model will stop generating further tokens if found. This can be useful to control the length and content of the output. ##### [`temperature`](#variables-temperature "Click to copy link to temperature") number What sampling temperature to use, recommended between 0.0 and 0.7. Higher values like 0.7 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. Change this or top\_p but not both. ##### [`top_p`](#variables-top_p "Click to copy link to top_p") number A float value between 0 and 1 that controls the nucleus sampling for text generation. It represents the cumulative probability threshold for token selection, where only the most probable tokens that add up to this threshold are considered. A higher value (closer to 1) allows for more diverse outputs, while a lower value (closer to 0) makes the output more focused and deterministic. Change this or temperature but not both. Schema: [https://www.krakend.io/schema/v2.13/ai/mistral.json](https://www.krakend.io/schema/v2.13/ai/mistral.json "JSON schema definition") This design keeps user input clean and focused only on content, while KrakenD handles building the full API request. ## Customizing the payload sent and received from Mistral As it happens with all LLM interfaces of KrakenD, you can completely replace the request and the response so you have a custom interaction with the LLM. While the default template should allow you to accomplish any day to day job, you might need to extend it using your own template. You may override the input and output Go templates by specifying: - `input_template`: Path to a custom template controlling how the request data is formatted before sending to Mistral. - `output_template`: Path to a custom template to transform and extract the desired pieces from Mistral’s response. See below how to change this interaction ### Default input\_template for Mistral v1 When you don’t set any `input_template`, KrakenD will create the JSON payload sent to Mistral using the following template: ```gotmpl { "model": {{ .variables.model | toJson }}, {{ $temperature := .variables.temperature }}{{ if ge $temperature 0.0 }}"temperature": {{ $temperature }},{{ end }} {{ $top_p := .variables.top_p }}{{ if ge $top_p 0.0 }}"top_p": {{ $top_p }},{{ end }} {{ $max_tokens := .variables.max_tokens }}{{ if ge $max_tokens 0 }}"max_tokens": {{ $max_tokens }},{{ end }} {{ $random_seed := .variables.random_seed }}{{ if ge $random_seed 0 }}"random_seed": {{ $random_seed }},{{ end }} "n": {{ .variables.n }}, "stream": false, "stop": {{ .variables.stop | toJson }}, "safe_prompt": {{ .variables.safe_prompt }}, "messages": [ {{- if hasKey .req_body "instructions" }} { "role": "system", "content": {{ .req_body.instructions | toJson }} }, {{- end }} { "role": "user", "content": {{ .req_body.contents | toJson }} } ] } ``` ### Default output\_template for Mistral v1 When you don’t declare an `output_template`, the response from the AI is transformed to have the following format: ```gotmpl { "ai_gateway_response": [ {{ if gt (len .resp_body.choices) 0 }} { "contents": [ {{- range $index, $choice := .resp_body.choices }} {{- if $index }},{{ end }} {{ $choice.message.content | toJson }} {{- end }} ] } {{ end }} ], "usage": "{{ .resp_body.usage.total_tokens }}" } ``` If you are not confortable receiveing the response in the `ai_gateway_response` field, you can add to the backend a mapping attribute to use another name, without changing the template: ```json { "url_pattern": "...", "mapping": { "ai_gateway_response": "my_response" } } ``` --- # [Anthropic integration (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/anthropic/) The Anthropic interface allows KrakenD to use [Anthropic’s API (Claude)](https://docs.claude.com/en/home) **without writing custom integration code**, enabling intelligent automation, content generation, or any LLM-powered use case within your existing API infrastructure. This component abstracts you from the Anthropic API usage allowing the consumer to concentrate on the prompt only, as for each request to an endpoint, KrakenD will create the **Anthropic request** with all the necessary elements in their API, and will return a **unified response**, so if you use other vendors you have a consitent use of LLM models. In other words, the user sends the content, like “tell me a joke!”, and then KrakenD builds the API payload necessary to talk to Anthropic. This Anthropic interface configures a backend within KrakenD that transparently forwards REST requests to Anthropic’s API endpoints. It manages authentication, versioning, and payload formatting using its custom templating system. This way, you can easily call Anthropic models without writing custom integration code. A simple configuration looks like this: ```json { "endpoint": "/anthropic", "method": "POST", "backend": [ { "url_pattern": "/v1/messages", "host": [ "https://api.anthropic.com" ], "extra_config": { "ai/llm": { "anthropic": { "v1": { "credentials": "xxxxx", "debug": false, "variables": { "model": "claude-opus-4-1-20250805" } } } } } } ] } ``` To interact with the LLM, the user can send in the request: - `instructions` (optional): If you want to add a system prompt - `contents`: The content you want to send to the template Like this: Using the endpoint  ``` $curl -XPOST --json '{"instructions": "Act as a 1000 dollar consultant", "contents": "Tell me a consultant joke"}' http://localhost:8080/anthropic ``` ## Configuration of Anthropic The configuration of Anthropic requires you to add under your backend `extra_config` the `ai/llm` namespace with the `anthropic` vendor. ##### Fields of Anthropic integration \* required fields ##### [`v1`](https://www.krakend.io/docs/enterprise/ai-gateway/anthropic/#v1 "Click to copy link to v1") object All settings depend on a specific version, as the vendor might change the API over time. ##### [`credentials`](#v1-credentials "Click to copy link to credentials") * string Your Anthropic API key. You can set it as an environment variable for better security. ##### [`debug`](#v1-debug "Click to copy link to debug") boolean Enables the debug mode to log activity for troubleshooting. Do not set this value to true in production as it may log sensitive data. Defaults to `false` ##### [`input_template`](#v1-input_template "Click to copy link to input_template") string A path to a custom [Go template](https://pkg.go.dev/text/template) that sets the payload format sent to Anthropic. You don’t need to set this value unless you want to override the default template making use of all the `variables` listed in this configuration. ##### [`output_template`](#v1-output_template "Click to copy link to output_template") string A path to a custom [Go template](https://pkg.go.dev/text/template) that sets how the response from Anthropic is transformed before being sent to the client. The default template extracts the text from the first choice returned by Anthropic so in most cases you don’t need to set a custom output template. ##### [`variables`](#v1-variables "Click to copy link to variables") * object The variables specific to the Anthropic usage that are used to construct the payload. ##### [`extra_payload`](#variables-extra_payload "Click to copy link to extra_payload") object A map of additional payload attributes you want to use in your custom `input_template` (this payload is not used in the default template). The attributes set here are accessible in your custom template as `{{ .variables.extra_payload.yourchosenkey }}`. This option helps adding rare customization and future attributes. ##### [`max_tokens`](#variables-max_tokens "Click to copy link to max_tokens") integer Maximum number of tokens that can be generated in the response. A token is approximately four characters. 100 tokens correspond to roughly 60-80 words. Defaults to `1024` ##### [`model`](#variables-model "Click to copy link to model") * string The name of the Anthropic model you want to use. Examples: `"claude-opus-4-1-20250805"` , `"claude-sonnet-4-20250514"` , `"claude-3-7-sonnet-latest"` , `"claude-3-5-haiku-latest"` ##### [`stop_sequences`](#variables-stop_sequences "Click to copy link to stop_sequences") array of strings An array of sequences where the model will stop generating further tokens if found. This can be useful to control the length and content of the output. ##### [`temperature`](#variables-temperature "Click to copy link to temperature") number The temperature is used for sampling during response generation, which occurs when topP and topK are applied. Temperature controls the degree of randomness in token selection. Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. ##### [`top_k`](#variables-top_k "Click to copy link to top_k") integer Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model’s vocabulary (also called greedy decoding), while a top-K of 3 means that the next token is selected from among the three most probable tokens by using temperature. ##### [`top_p`](#variables-top_p "Click to copy link to top_p") number Top-P changes how the model selects tokens for output. Tokens are selected from the most probable to least probable until the sum of their probabilities equals the top-P value. For example, if tokens A, B, and C have a probability of 0.3, 0.2, and 0.1 and the top-P value is 0.5, then the model will select either A or B as the next token by using temperature and excludes C as a candidate. Schema: [https://www.krakend.io/schema/v2.13/ai/anthropic.json](https://www.krakend.io/schema/v2.13/ai/anthropic.json "JSON schema definition") ## Customizing the payload sent and received from Anthropic As it happens with all LLM interfaces of KrakenD, you can completely replace the request and the response so you have a custom interaction with the LLM. While the default template should allow you to accomplish any day to day job, you might need to extend it using your own template. You may override the input and output Go templates by specifying: - `input_template`: Path to a custom template controlling how the request data is formatted before sending to Anthropic. - `output_template`: Path to a custom template to transform and extract the desired pieces from Anthropic’s response. See below how to change this interaction ### Default input\_template for Anthropic v1 When you don’t set any `input_template`, KrakenD will create the JSON payload sent to Anthropic using the following template: ```gotmpl { "model": {{ .variables.model | toJson }}, "max_tokens": {{ .variables.max_tokens }}, "stream": false, {{ $temperature := .variables.temperature }}{{ if ge $temperature 0.0 }}"temperature": {{ $temperature }},{{ end }} {{ $top_p := .variables.top_p }}{{ if ge $top_p 0.0 }}"top_p": {{ $top_p }},{{ end }} {{ $top_k := .variables.top_k }}{{ if ge $top_k 0 }}"top_k": {{ $top_k }},{{ end }} "stop_sequences": {{ .variables.stop_sequences | toJson }}, {{- if hasKey .req_body "instructions" }} "system": [ { "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m" }, "text": {{ .req_body.instructions | toJson }} } ], {{- end }} "messages": [ { "role": "user", "content": {{ .req_body.contents | toJson }} } ] } ``` Remember you can access your own `variables` declared in the configuration using `{{ .variables.xxx }}`. ### Default output\_template for Anthropic v1 When you don’t declare an `output_template`, the response from the AI is transformed to have the following format: ```gotmpl { "ai_gateway_response": [ { "contents": [ {{- range $index, $part := .resp_body.content }} {{- if $index }},{{ end }} {{ $part.text | toJson }} {{- end }} ] } ], "usage": "{{ add .resp_body.usage.output_tokens .resp_body.usage.input_tokens }} } ``` As you can see in the response template, you get the total number of tokens consumed aggregated (input and output) --- # [Amazon Web Services Bedrock integration (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/bedrock/) The Bedrock interface allows KrakenD to use [Bedrock’s Converse API](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-call.html) **without writing custom integration code**, enabling intelligent automation, content generation, or any LLM-powered use case within your existing API infrastructure. This component abstracts you from the Bedrock API, allowing the consumer to focus solely on the prompt. For each request to an endpoint, KrakenD will create the **Bedrock request** with all the necessary elements in its API and return a **unified response**, so if you use other vendors, you have a consistent use of LLM models. In other words, the user sends content, like “tell me a joke!”, and KrakenD builds the API payload needed to talk to Bedrock. This Bedrock interface configures a backend within KrakenD that transparently forwards REST requests to Bedrock’s API endpoints. It manages authentication, versioning, and payload formatting using its custom templating system. This way, you can easily call Bedrock models without writing custom integration code. A simple configuration looks like this: ```json { "endpoint": "/bedrock", "method": "POST", "backend": [ { "host": [ "https://bedrock-runtime.eu-west-1.amazonaws.com" ], "url_pattern": "/model/eu.meta.llama3-2-1b-instruct-v1:0/converse", "method": "POST", "extra_config": { "ai/llm": { "bedrock": { "v1": { "credentials": "bedrock-api-key-xxx", "debug": true, "variables": { "max_tokens": 1500 } } } } } } ] } ``` To interact with the LLM, the user can send in the request: - `instructions` (optional): If you want to add a system prompt - `contents`: The content you want to send to the template Like this: Using the endpoint  ``` $curl -XPOST --json '{"instructions": "Act as a 1000 dollar consultant", "contents": "Tell me a consultant joke"}' http://localhost:8080/bedrock ``` ## Configuration of Bedrock The Bedrock configuration requires you to add the `ai/llm` namespace under your backend’ extra\_config’, with the `Bedrock` vendor. ##### Fields of Bedrock integration \* required fields ##### [`v1`](https://www.krakend.io/docs/enterprise/ai-gateway/bedrock/#v1 "Click to copy link to v1") All settings depend on a specific version, as the vendor might change the API over time. ##### [`credentials`](#v1-credentials "Click to copy link to credentials") * string Your Bedrock API key. You can set it as an environment variable for better security. ##### [`debug`](#v1-debug "Click to copy link to debug") boolean Enables the debug mode to log activity for troubleshooting. Do not set this value to true in production as it may log sensitive data. Defaults to `false` ##### [`input_template`](#v1-input_template "Click to copy link to input_template") string A path to a custom [Go template](https://pkg.go.dev/text/template) that sets the payload format sent to Bedrock. You don’t need to set this value unless you want to override the default template making use of all the `variables` listed in this configuration. ##### [`output_template`](#v1-output_template "Click to copy link to output_template") string A path to a custom [Go template](https://pkg.go.dev/text/template) that sets how the response from Bedrock is transformed before being sent to the client. The default template extracts the text from the first choice returned by Bedrock so in most cases you don’t need to set a custom output template. ##### [`variables`](#v1-variables "Click to copy link to variables") * object The variables specific to the Bedrock usage that are used to construct the payload. ##### [`extra_payload`](#variables-extra_payload "Click to copy link to extra_payload") object A map of additional payload attributes you want to use in your custom `input_template` (this payload is not used in the default template). The attributes set here are accessible in your custom template as `{{ .variables.extra_payload.yourchosenkey }}`. This option helps adding rare customization and future attributes. ##### [`max_tokens`](#variables-max_tokens "Click to copy link to max_tokens") integer An upper bound for the number of tokens that can be generated for a response, including visible output tokens and reasoning tokens. ##### [`stop_sequences`](#variables-stop_sequences "Click to copy link to stop_sequences") array of strings An array of sequences where the model will stop generating further tokens if found. This can be useful to control the length and content of the output. ##### [`temperature`](#variables-temperature "Click to copy link to temperature") number What sampling temperature to use, recommended between 0.0 and 0.7. Higher values like 0.7 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. Change this or top\_p but not both. ##### [`top_k`](#variables-top_k "Click to copy link to top_k") integer Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model’s vocabulary (also called greedy decoding), while a top-K of 3 means that the next token is selected from among the three most probable tokens by using temperature. ##### [`top_p`](#variables-top_p "Click to copy link to top_p") number A float value between 0 and 1 that controls the nucleus sampling for text generation. It represents the cumulative probability threshold for token selection, where only the most probable tokens that add up to this threshold are considered. A higher value (closer to 1) allows for more diverse outputs, while a lower value (closer to 0) makes the output more focused and deterministic. Change this or temperature but not both. Schema: [https://www.krakend.io/schema/v2.13/ai/bedrock.json](https://www.krakend.io/schema/v2.13/ai/bedrock.json "JSON schema definition") ## Customizing the payload sent and received from Bedrock As with all KrakenD LLM interfaces, you can completely replace the request and response to have a custom interaction with the LLM. While the default template should allow you to accomplish any day-to-day job, you might need to extend it using your own template. You may override the input and output Go templates by specifying: - `input_template`: Path to a custom template controlling how the request data is formatted before sending to Bedrock. - `output_template`: Path to a custom template to transform and extract the desired pieces from Bedrock’s response. See below how to change this interaction. ### Default input\_template for Bedrock v1 When you don’t set any `input_template`, KrakenD will create the JSON payload sent to Bedrock using the following template: ```gotmpl { "inferenceConfig": { {{ $max_tokens := .variables.max_tokens }}{{ if ge $max_tokens 0 }}"maxTokens": {{ $max_tokens }},{{ end }} {{ $temperature := .variables.temperature }}{{ if ge $temperature 0.0 }}"temperature": {{ $temperature }},{{ end }} {{ $top_p := .variables.top_p }}{{ if ge $top_p 0.0 }}"topP": {{ $top_p }},{{ end }} {{ $top_k := .variables.top_k }}{{ if ge $top_k 0 }}"topK": {{ $top_k }},{{ end }} "stopSequences": {{ .variables.stop_sequences | toJson }} }, {{- if hasKey .req_body "instructions" }} "system": [ { "text": {{ .req_body.instructions | toJson }} } ], {{ end }} "messages": [ { "content": [ { "text": {{ .req_body.contents | toJson }} } ], "role": "user" } ] } ``` Remember, you can access your own `variables` declared in the configuration using `{{ .variables.xxx }}`. ### Default output\_template for Bedrock v1 When you don’t declare an `output_template`, the response from the AI is transformed to have the following format: ```gotmpl { "ai_gateway_response": [ {{ if gt (len .resp_body.output.message.content) 0 }} { "contents": [ {{ range $ci, $content := .resp_body.output.message.content }} {{ if $ci }},{{ end }} {{ $content.text | toJson }} {{ end }} ] } {{ end }} ], "usage": "{{ .resp_body.usage.totalTokens }}" } ``` --- # [Connecting to other AI vendors (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/other-vendors/) If you need to connect to an AI vendor that is not listed in the pre-defined list of abstracted interfaces, you can still perform the abstraction by providing a template. On KrakenD you can implement completely different models while you keep the user away from this process. The way to adapt the payload sent to each LLM, you need to pass the right request template to each LLM. This is achieved with the [request body generator](/docs/enterprise/backends/body-generator/) which takes care of this job. Examples of vendor templates, for a user payload containing a `{"content": ""}` would be: ## Configuring additional AI vendors Generally speaking, any AI integration is going to need: 1. A specific payload format for the API endpoint of the LLM ([request body generator](/docs/enterprise/backends/body-generator/)) 2. Some authentication mechanism ([Martian modifier](/docs/enterprise/backends/martian/)) See the following example configuration to understand how to do both: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "endpoints": [ { "endpoint": "/_llm/custom-llm", "backend": [ { "host": [ "https://llm.example.com" ], "url_pattern": "/llm/endpoint", "extra_config": { "modifier/request-body-generator": { "path": "/vendor.tmpl", "content_type": "application/json", "debug": true }, "modifier/martian": { "header.Modifier": { "scope": [ "request" ], "name": "Authorization", "value": "Bearer YOUR_OPENI_API_KEY" } } } } ] } ] } ``` Notice that the `modifier/request-body-generator` is calling a template `vendor.tmpl` that would contain the JSON format each vendor would expect. The following are a few examples of this `vendor.tmpl` (check each vendor documentation to put the right format) ### Deepseek request template This is an example of the `deepseek.tmpl` you would include in the `modifier/request-body-generator`: ```gotmpl { "model": "deepseek-chat", "messages": [ { "role": "user", "content": {{ .req_body.content | toJson }} } ] } ``` This template expects the user to call the endpoint with a JSON content containing a `content` property. ### LLaMa / Ollama request template Another example with a LLaMa LLM; ```gotmpl { "model": "meta-llama-3-70b-instruct", "messages": [ { "role": "user", "content": {{ .req_body.content | toJson }} } ] } ``` You could also change `"model": "llama3"` for **Ollama**. ### Cohere request template Another example with Cohere: ```gotmpl { "model": "command-r-plus", "chat_history": [], "message": {{ .req_body.content | toJson }}, "temperature": 0.5 } ``` --- # [AI usage monitoring (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/ai-gateway/usage-monitoring/) Monitoring AI backends in KrakenD does not require special treatment. AI services are integrated and handled just like any other backend in your API Gateway. Your existing monitoring tools and practices, such as [OpenTelemetry](/docs/telemetry/opentelemetry/), remain fully applicable. AI backends, regardless of their complexity, respond to API calls just like any other backend service. KrakenD treats AI integrations as regular backends. This simplifies observability and operational consistency by applying proven monitoring frameworks without specialized or separate tooling. ## OpenTelemetry integration for AI backends Use OpenTelemetry to monitor your API gateway traffic, including AI backend calls. The following example enables Jaeger traces and Prometheus metrics: ```json { "version": 3, "extra_config": { "telemetry/opentelemetry": { "service_name": "krakend_prometheus_service", "metric_reporting_period": 1, "trace_sample_rate": 1, "exporters": { "prometheus": [ { "name": "local_prometheus", "port": 9090, "process_metrics": true, "go_metrics": true } ], "otlp": [ { "disable_metrics": false, "disable_traces": false, "host": "localhost", "name": "debug_jaeger", "port": 64317, "use_http": false } ] }, "layers": { "global": { "disable_metrics": false, "disable_propagation": false, "disable_traces": false, "report_headers": true }, "proxy": { "disable_metrics": false, "disable_traces": false, "report_headers": true }, "backend": { "metrics": { "detailed_connection": true, "disable_stage": false, "read_payload": false, "round_trip": false }, "traces": { "detailed_connection": false, "disable_stage": false, "read_payload": false, "report_headers": false, "round_trip": false } } } } } } ``` --- # [API Governance using Quota (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/governance/quota/) The Quota feature allows teams to enforce quota limits by tier, enabling API monetization strategies such as freemium plans, usage-based tiers, and differentiated service levels, but also helps you **contain the expenses** when using external APIs or AI providers. The Quota system is equally powerful in **egress and ingress scenarios**: - On the **egress side**, organizations can enforce internal consumption controls. For instance, when KrakenD acts as an **AI Gateway** proxying to LLM models or metered third-party APIs. By applying quotas per team, product, etc, you can **prevent runaway spend**, cap daily/weekly/monthly/yearly usage, or restrict access to premium services, keeping your operational budget under control. - On the **ingress side**, when exposing public APIs, quotas become the foundation of **monetization models**. You can define consumption tiers (e.g., Free, Pro, Enterprise), enforce usage ceilings based on subscription level, and enable freemium or trial plans with precision. This protects your backend infrastructure from abuse and creates opportunities to align API usage with business value, enabling pay-per-use, overage billing, and developer self-service models, all driven by configuration. Quotas use **persistence backed by Redis**, which survives deployments and restarts, and serves as a central point for tracking activity. ## Quotas vs Rate Limiting There are [8 types of rate-limiting](/docs/enterprise/throttling/#rate-limiting), but here we are talking about something close but not the same. It is important to understand that although quotas and rate-limits seem similar, they serve different purposes. Traditional [throttling and rate-limiting](/docs/enterprise/throttling/) in KrakenD (like the [service](/docs/enterprise/service-settings/service-rate-limit/), [endpoint](/docs/enterprise/endpoints/rate-limit/), [tiered](/docs/enterprise/service-settings/tiered-rate-limit/), or [proxy](/docs/enterprise/backends/rate-limit/) rate-limits) operate in-memory per-node, and they are **stateless and fast**. Difference between Quotas and Rate Limits The purpose of a rate limit is to **prevent abuse** because it monitors a short period (like a second or minute). In contrast, the purpose of a quota is more closely related to **usage control** as it monitors a longer period (a day, month, etc.). A rate limit will cut traffic when there are many connections per second, while the quota might cut you when you spend your monthly plan. They might be used together and are complementary. ### Architectural differences If you want to limit the API’s usage alone, a stateless rate-limiting is the best design architectural pattern you can choose. But if you need more, a rate limit has the following **business limitations**: - You don’t have a long-term global state. - Limit exhaustion does not survive service restarts or redeployments. - Complicated monetization or **contractual enforcement**. - It is not designed to track usage over long periods (but close to a second). In contrast, the persistent **quota system**: - Shares state across all KrakenD nodes via Redis, making all nodes aware of the global counting in a cluster. - Supports long-term with low-use definitions, e.g., 1000 calls/month (in contrast to 10 calls/second). - Allows custom weighting of requests (e.g., based on LLM token cost, or API cost). - Enables parallel multi-interval policies (hourly, daily, monthly, yearly), counting all at once. - It is the foundation for API monetization, freemium models, and service-level enforcement. It’s not that one is better than the other; they serve very different goals. ## Quota Configuration The quota system requires at least three configuration blocks: 1. A `redis` entry with the [**connection details**](/docs/enterprise/service-settings/redis-connection-pools/) at the `extra_config` of the service level. The minimum supported Redis version is `7.4` (as quotas use the `HEXPIRE` command). 2. A `governance/processors` entry that defines the **global declaration of quota processors** that are responsible for keeping track of counters and rejecting requests. 3. A `governance/quota` entry that **attaches a processor and enforces the quota**. You can attach this namespace to the service (root of the configuration), or inside endpoints and backends. The differences and nuances are explained below. ### 1. Redis connection details As quotas are stateful, they require storage. The counters are kept in a shared `redis` configuration that you need to place at the root of the configuration. Here’s an example configuration: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "extra_config": { "redis": { "connection_pools": [ { "name": "shared_redis_pool", "address": "192.168.1.45:6379" } ] } } } ``` Redis connection pools and clusters are fully explained in the [Redis Connection Pool](/docs/enterprise/service-settings/redis-connection-pools/) section. Visit the link for more parameters and customization. What is important here is that the `name` you choose here, which is internal for KrakenD and can be anything human-readable for you, is the one you use later on when defining the processor. ### 2. Global declaration of quota processors The second thing you need at the service level is to define a **processor of quota**. The `governance/processors` property is an object under the global `extra_config` that defines the available processors and rulesets you will have. You can declare multiple quotas (e.g., a quota for internal LLM usage and another for your customers), and they can be connected to different Redis pools. Each quota defines multiple `rules` to enforce; you can see them as your “plans”, like “gold”, “silver”, “bronze”, etc. Each rule or plan can have multiple limits, because you might want to set limitations per hour, day, month, etc. The processors take care of **bookkeeping hits and denying access** when a threshold is met. Still, they don’t know anything about requests or how to identify a user, which will be the job of our last component, the `governance/quota`. The gateway can keep multiple processors working simultaneously, even when they are of the same type. See the following example: ```json { "version": 3, "extra_config": { "governance/processors": { "quotas": [ { "name": "public_plans", "connection_name": "shared_redis_pool", "hash_keys": true, "on_failure_allow": false, "rejecter_cache": { "N": 10000000, "P": 1e-8, "hash_name": "optimal" }, "rules": [ { "name": "gold", "limits": [ { "amount": 10, "unit": "hour" }, { "amount": 200, "unit": "day" } ] }, { "name": "bronze", "limits": [ { "amount": 5, "unit": "hour" }, { "amount": 100, "unit": "day" } ] } ] } ] } } } ``` The configuration above defines a processor that will connect to a Redis service defined as `shared_redis_pool`, and will prefix all keys with `public_plans`. There is one rule for the gold plan and another for the bronze plan, which is limited to half of the requests. In addition, it has a `rejecter_cache` where a local memory cache keeps track of rejections by Redis, so it is not queried that often, and a known overuser is kicked without needing to query Redis and avoid the network roundtrip. At this point, the quotas are not in place yet; they are only declared, and we need to attach them to specific places. The list of possible properties to declare quotas is: ##### Fields of Governance processors \* required fields ##### [`quotas`](https://www.krakend.io/docs/enterprise/governance/quota/#quotas "Click to copy link to quotas") * array of objects The list of quota processors available for attachment. You can have multiple processors with different configurations. Each item of quotas accepts the following properties: ##### [`connection_name`](#quotas-connection_name "Click to copy link to connection_name") * string The name of the [Redis connection](https://www.krakend.io/docs/enterprise/service-settings/redis-connection-pools/) to use, it must exist under the `redis` namespace at the service level and written exactly as declared. ##### [`deny_queue_flush_interval`](#quotas-deny_queue_flush_interval "Click to copy link to deny_queue_flush_interval") string When you have a `rejecter_cache`, the time interval to write the events stored in the buffer in the bloom filter. This is the maximum time that can elapse before the events are written to the bloom filter. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"1s"` ##### [`deny_queue_flush_threshold`](#quotas-deny_queue_flush_threshold "Click to copy link to deny_queue_flush_threshold") integer When you have a `rejecter_cache`, the maximum number of events in the buffer that will force a write to the bloom filter event when the flush interval has not kicked in yet. Defaults to `10` ##### [`deny_queue_size`](#quotas-deny_queue_size "Click to copy link to deny_queue_size") integer When you have a `rejecter_cache`, the size of the buffer (number of events stored) to write in the bloomfilter. It defaults to the number of cores on the machine. This is the maximum number of events that can be stored in memory before being written to the bloom filter. You should not set this value unless you are seeing increased latencies on very high-concurrency scenarios; ask support for help. ##### [`hash_keys`](#quotas-hash_keys "Click to copy link to hash_keys") boolean Whether to hash the keys used for quota consumption. If you have PII (Personal Identifiable Information) in the keys (like an email), enable this option to `true` to avoid Redis containing clear text keys with PII. This is a setting for privacy, enabling it may affect performance because of the extra hashing, and makes data exploration difficult. Defaults to `false` ##### [`name`](#quotas-name "Click to copy link to name") * string Name of the quota. The exact name you type here is the one you need to reference when you attach a quota under the `governance/quota` namespace, and is also part of the key name on the persistence layer. Examples: `"public_api"` , `"LLM"` ##### [`on_failure_allow`](#quotas-on_failure_allow "Click to copy link to on_failure_allow") boolean What to do with the user request if Redis is down. When `true`, allows continuing to perform requests even when Redis is unreachable, but the quota won’t be counted. When `false`, the request is rejected and the user receives a 500 error. This is a fail-safe option, but it may lead to quota overconsumption. ##### [`on_failure_backoff_strategy`](#quotas-on_failure_backoff_strategy "Click to copy link to on_failure_backoff_strategy") The backoff strategy to use when Redis is unreachable. The default is `exponential`, which means that the time between retries will increase exponentially. The other option is `linear`, which means that the time between retries will be constant. Possible values are: `"linear"` , `"exponential"` Defaults to `"exponential"` ##### [`on_failure_max_retries`](#quotas-on_failure_max_retries "Click to copy link to on_failure_max_retries") integer Maximum number of retries to Redis when it is unreachable. Once the retries are exhausted, the processor is no longer usable and the quota stops working until the Redis connection is restored and the service restarted. The users will be able to consume content depending on the `on_failure_allow` option. A zero value means no retries. Defaults to `0` ##### [`rejecter_cache`](#quotas-rejecter_cache "Click to copy link to rejecter_cache") object The bloom filter configuration that you use to cache rejections. The bloom filter is used to store the events that are rejected by the quota processor. This is useful to avoid rejecting the same event multiple times. ##### [`N`](#rejecter_cache-N "Click to copy link to N") * integer The maximum `N`umber of elements you want to keep in the bloom filter. Tens of millions work fine on machines with low resources. Example: `10000000` ##### [`P`](#rejecter_cache-P "Click to copy link to P") * number The `P`robability of returning a false positive. E.g.,`1e-7` for one false positive every 10 million different tokens. The values `N` and `P` determine the size of the resulting bloom filter to fulfill your expectations. E.g: 0.0000001 See: [https://www.krakend.io/docs/authorization/revoking-tokens/](https://www.krakend.io/docs/authorization/revoking-tokens/) Example: `1e-7` ##### [`cleanup_interval`](#rejecter_cache-cleanup_interval "Click to copy link to cleanup_interval") string The time interval to clean up the bloom filter. This is the maximum time that can elapse before the bloom filter is cleaned up. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"30m"` ##### [`hash_name`](#rejecter_cache-hash_name "Click to copy link to hash_name") Either `optimal` (recommended) or `default`. The `optimal` consumes less CPU but has less entropy when generating the hash, although the loss is negligible. See: [https://www.krakend.io/docs/authorization/revoking-tokens/](https://www.krakend.io/docs/authorization/revoking-tokens/) Possible values are: `"optimal"` , `"default"` Defaults to `"optimal"` ##### [`rules`](#quotas-rules "Click to copy link to rules") * array of objects The rules to use for the quota processor. Each item of rules accepts the following properties: ##### [`limits`](#rules-limits "Click to copy link to limits") * array of objects Show properties The limits for the rule. The limits are defined as an array of objects, each object containing an amount and a unit. Example: `[{"amount":10,"unit":"hour"},{"amount":250,"unit":"day"}]` Each item of limits accepts the following properties: ##### [`amount`](#limits-amount "Click to copy link to amount") * number The amount of the limit. This is the maximum number of requests that can be made in the given time unit. ##### [`unit`](#limits-unit "Click to copy link to unit") * The time unit for the limit. Possible values are: `"second"` , `"minute"` , `"hour"` , `"day"` , `"week"` , `"month"` , `"year"` ##### [`name`](#rules-name "Click to copy link to name") * string The name of the rule. This is the name that will be used to identify the rule in the logs and metrics. It is also the plan name. Examples: `"gold"` , `"silver"` Schema: [https://www.krakend.io/schema/v2.13/governance/processors.json](https://www.krakend.io/schema/v2.13/governance/processors.json "JSON schema definition") ### 3. Attach a quota Once the quotas are declared at a global level, it’s time to attach them. The `governance/quota` namespace is the quota enforcer that you can add to the service globally, on endpoints, or backends. As you can see below, it reuses the `quota_name` and the `rule_name` you declared previously, but also adds bits of behaviour, like how to count requests and what to do with unknown requests. Here is an example of the full configuration with steps 1 to 3: ```json { "extra_config": { "redis": { "connection_pools": [ { "name": "shared_redis_pool", "address": "192.168.1.45:6379" } ] }, "governance/processors": { "quotas": [ { "name": "public_plans", "connection_name": "shared_redis_pool", "hash_keys": true, "on_failure_allow": false, "rejecter_cache": { "N": 10000000, "P": 1e-8, "hash_name": "optimal" }, "rules": [ { "name": "rule_gold", "limits": [ { "amount": 10, "unit": "hour" }, { "amount": 200, "unit": "day" } ] }, { "name": "rule_bronze", "limits": [ { "amount": 5, "unit": "hour" }, { "amount": 100, "unit": "day" } ] } ] } ] }, "governance/quota": { "quota_name": "public_plans", "on_unmatched_tier_allow": false, "weight_key": "credits_consumed", "weight_strategy": "body", "tier_key": "X-Level", "disable_quota_headers": false, "tiers": [ { "rule_name": "rule_gold", "tier_value": "gold", "tier_value_as": "literal", "strategy": "header", "key": "X-User-Id" }, { "comment": "Special case * that catches any requests not falling into one of the tiers above", "rule_name": "rule_bronze", "tier_value_as": "*", "strategy": "ip" } ] } } } ``` This example is for the service level, but you can put the `governance/quota` namespace in an endpoint or in a `backend` as well. You will probably want to add a `governance/quota` in the following scope when: - `service`: You don’t need to identify tiers based on JWT and want a single configuration for all the endpoints, no exceptions. At the service level, **everything is inspected for quota**, even a single `/__health` request. - `endpoint`: In most cases, you want to add **ingress quota** to your API contract. Use [Flexible Configuration](/docs/enterprise/configuration/flexible-config/) to avoid repeating code on every endpoint needing quota. - `backend`: In cases where you want to put a quota between the gateway and upstream services or LLM (**egress quota**) Notice that the concepts of ingress and egress are for illustration, but they are open to interpretation. Having an external user limited to consuming an external LLM through KrakenD could be both ingress and egress. If a referenced processor or rule is missing, the config fails, and affected endpoints return `500` status codes. The attributes you can see here are: ##### Fields of Attachment of a quota \* required fields ##### [`disable_quota_headers`](https://www.krakend.io/docs/enterprise/governance/quota/#disable_quota_headers "Click to copy link to disable_quota_headers") boolean When set to `true`, the quota headers `X-Quota-Limit`, `X-Quota-Remaining`, and `Retry-After` will not be added to the response. This is useful when you want to hide the quota information from the client. Defaults to `false` ##### [`on_unmatched_tier_allow`](https://www.krakend.io/docs/enterprise/governance/quota/#on_unmatched_tier_allow "Click to copy link to on_unmatched_tier_allow") boolean When a tier cannot be infered from the request, whether to allow the request to continue or not. In case a request does not match any of the tiers, the request will be rejected with a 400 error unless you set this to `true`. Defaults to `false` ##### [`quota_name`](https://www.krakend.io/docs/enterprise/governance/quota/#quota_name "Click to copy link to quota_name") * string Name of the quota you want to reuse, written exactly as declared under the `processors` list. Example: `"my_quota"` ##### [`tier_key`](https://www.krakend.io/docs/enterprise/governance/quota/#tier_key "Click to copy link to tier_key") * string Header used to determine the tier. Use `tier_value` and `tier_value_as` on each tier to determine how to match the value. Examples: `"X-User-Tier"` , `"X-User-ID"` ##### [`tiers`](https://www.krakend.io/docs/enterprise/governance/quota/#tiers "Click to copy link to tiers") * array of objects List of tiers to match against the request. The first tier that matches will be used to determine the quota to consume. Each item of tiers accepts the following properties: ##### [`key`](#tiers-key "Click to copy link to key") string The key (e.g., header name, IP, claim name) that contains the identity of the caller, like the user ID of who is doing the request. The key must be present in the request. ##### [`rule_name`](#tiers-rule_name "Click to copy link to rule_name") * string Tier limit defined in the global `processor`. It must be within the defined `limits` of the quota\_name processor. If it’s not found, the system will complain at startup and affected endpoints will be degraded with a 500 error ##### [`strategy`](#tiers-strategy "Click to copy link to strategy") Where to find the key containing the identity of the caller. Use `header` for headers, `ip` for the IP address of the caller, and `param` for an endpoint {parameter}. Possible values are: `"header"` , `"ip"` , `"param"` Defaults to `"header"` ##### [`tier_value`](#tiers-tier_value "Click to copy link to tier_value") string Literal value or CEL expression to match. ##### [`tier_value_as`](#tiers-tier_value_as "Click to copy link to tier_value_as") How to treat the value. In most cases the tier value contains the plan name, like `gold`, so you will choose `literal`. But you can also set in the value a security policy (CEL) that will evaluate to resolve the tier policy accordingly, or put an asterisk `*` to always match and use as your last and default tier. Possible values are: `"literal"` , `"policy"` , `"*"` Defaults to `"literal"` ##### [`weight_key`](https://www.krakend.io/docs/enterprise/governance/quota/#weight_key "Click to copy link to weight_key") string Instead of incrementing the quota counter by one unit, use the value provided in a field or header with its dynamic value. For instance, an LLM can return how many tokens it consumed, and you can use that value to increment the quota counter. The value must be a parseable number, and the field or header must be present in the backend response. The `weight_key` is only used in the `endpoint` and `backend` scopes, and it is ignored in the service level. ##### [`weight_strategy`](https://www.krakend.io/docs/enterprise/governance/quota/#weight_strategy "Click to copy link to weight_strategy") Where to find the key containing the counter value to increment. Use `body` for any type of encoding different than `no-op` and `header` for `no-op`. Possible values are: `"body"` , `"header"` Defaults to `"body"` Schema: [https://www.krakend.io/schema/v2.13/governance/quota.json](https://www.krakend.io/schema/v2.13/governance/quota.json "JSON schema definition") ## Redis keys format The storage of counters in Redis uses a [*Redis hash* type](https://redis.io/docs/latest/develop/data-types/hashes/) that stores an attribute for each dimension (current day, hour, month…). The key name follows the format `quota_name:tier_value_as:tier_value:key`. If the setting `hash_keys` is set, the Redis key ending`...:key` is hashed. This will prevent the tracked key from being stored clearly in the database if it contains personal information (such as an email). The Redis Hash will contain one property per dimension, using a letter plus a number. The letters and possible ranges are: - `hX` Hour (where `X` is in the range 0-23) - `dX` Day (`X` in the range 1-31) - `wX` Week (range 1-53) - `mX` Month (range 1-12) - `yX` Current year (four digits) For instance, if a request comes at `13:25` on 2025/01/04, the dimensions that could be computed are `h13`, `d4`, `w1`, `m1`, and `y2025`. Here’s an example interacting with Redis for a quota named `public_plans` identified by a header that contains the `literal` value `gold`, when accessed by the user ID `1234`: ``` # redis-cli 127.0.0.1:6379> keys public_plans* 1) "public_plans:literal:gold:1234" 127.0.0.1:6379> hkeys public_plans:literal:gold:1234 1) "h13" 2) "d4" 127.0.0.1:6379> hget public_plans:literal:gold:1234 d4 "125" ``` From the example above, we can deduce that user 1234 made 125 calls on the fourth day at the 13th hour. ## How much quota is left? When you place a `governance/quota` at the service or endpoint levels, **clients receive usage headers** unless you set the flag `disable_quota_headers` to `true` or use the `weight_key` property. These headers are: being: - `Retry-After`: This header is set only when the limit is surpassed and the clients receive `429` status codes. It contains the number of seconds until the next quota refill. It is a standard header implemented in browsers for retrying. - `X-Quota-Limit: "hour";n=10`: When the request is successful, it contains the total quota limit the user has within a time window (e.g., `hour`, `day`, etc.), and an `n=` which is the number of total hits permitted. - `X-Quota-Remaining: "hour";n=9`: Similarly, the number of remaining hits in the time window, the quota left. Clients might receive **multiple entries** of the `X-Quota-` headers, as you can set quotas that work in different time windows. ``` curl -i -H 'X-Level: MyPlan' http://localhost:8080/test HTTP/1.1 200 OK X-Quota-Limit: "hour";n=50 X-Quota-Limit: "day";n=250 X-Quota-Limit: "week";n=1000 X-Quota-Limit: "month";n=10000 X-Quota-Limit: "year";n=100000 X-Quota-Remaining: "hour";n=0 X-Quota-Remaining: "day";n=200 X-Quota-Remaining: "week";n=950 X-Quota-Remaining: "month";n=950 X-Quota-Remaining: "year";n=997050 Date: Fri, 4 May 2025 08:57:55 GMT Content-Length: 250 ``` The user will be able to make 200 more requests today, 950 more in the week, etc. although not in the current hour because they exhausted the hourly limit and this was the last one (remaining = 0). On the other side, users with an exceeding quota will see a response like this: ``` curl -i -H 'X-Level: MyPlan' http://localhost:8080/test HTTP/1.1 429 Too Many Requests Retry-After: 5 Date: Fri, 4 May 2025 08:59:55 GMT Content-Length: 0 ``` The example above tells the user that there won’t be more quota for the next 5 seconds (when the hourly limit will be refiled). ## Quota over usage edge case There is an edge case when you use the `weight_key` (you increment the usage counter based on the response), where any user with remaining quota credits could spend more than the configured amount. The weight number in the response might be higher than the total amount of quota left. In that case, the gateway returns the service response to the user. Although they won’t be able to make more requests until the next refill, you must be aware that consuming above the quota in this scenario is possible. Practical example: You have configured a weekly quota of 1000 LLM tokens. A user has already spent 999 tokens and sends a new request (still within the limits), and the LLM spends 50 more tokens. The response is returned to the user, but the total spent tokens in that week is 1049 tokens. You must remember that KrakenD **cannot predict the weight**, so as long as there is a remaining quota, it will allow users to make requests. ## Example use case of a monetization plan Suppose you want to establish the following plans: ``` Gold users: 250 req/day Bronze users: 100 req/day Anonymous users: 10 req/day ``` Gold and Bronze users are known to you, and they set a header name `X-User-ID` (that could be [propagated by a JWT token](/docs/authorization/jwt-validation/#propagate-jwt-claims-as-request-headers)) containing their identifier. Anonymous users on the other side, use the API without an identificator and you will limit based on the IP address they are using. This idea is expressed with the following configuration `tiers` for the `governance/quota`: ```json { "governance/quota": { "quota_name": "public_plans", "on_unmatched_tier_allow": false, "tier_key": "X-Plan", "tiers": [ { "rule_name": "rule_gold", "tier_value": "gold", "tier_value_as": "literal", "strategy": "header", "key": "X-User-ID" }, { "rule_name": "rule_bronze", "tier_value": "bronze", "tier_value_as": "literal", "strategy": "header", "key": "X-User-ID" }, { "rule_name": "rule_anonymous", "tier_value_as": "*", "strategy": "ip" } ] } } ``` --- # [Moesif integration for API Analytics and Monetization (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/governance/moesif/) [Moesif](https://www.moesif.com/) is our API Analytics and Monetization partner that helps you understand and monetize API usage with a robust analytics and billing platform. While KrakenD focuses on organizing and protecting your content through the API gateway, Moesif provides governance and monetization dashboards that allow you to rule the activity on the API. When the Moesif integration is enabled on KrakenD, the activity of your API is asynchronously pushed to Moesif, where you can configure your **API governance** and **API Monetization rules**. KrakenD is in real-time synchronized with Moesif and applies your governance rules as set in the Moesif platform, preventing users from violating them. KrakenD also provides Moesif with identification mechanisms so you can quickly pinpoint which organizations are linked to the activity you see in the dashboards. ## How does it work ![moesif-async-flow.mmd diagram](/images/documentation/diagrams/moesif-async-flow.mmd.svg) When the integration is enabled, every pair of requests and responses is stored as an event in an internal in-memory queue that serves as a buffer. Then, a timer periodically takes the number of events you have set in the configuration from the queue and pushes them to the Moesif API. **The events are pushed asynchronously to the Moesif API, and the request and response flow to the end user is unaffected.** Once the data is pushed to Moesif, from Moesif itself, you can: - **Analyze Customer API Usage**: Understand API usage by customer, product, revenue, and more in real time. - **Ensure Customer Success**: Get notified of changes in API and customer usage. Identify self-serve customers ready for the sales team. - **Charge for APIs Effortlessly**: Connect to a payment gateway and set up usage-based billing rules on API calls, gross dollar volume, and more. Issue invoices directly from Stripe, Recurly, Chargebee, or trigger webhooks. - **Enforce Subscription Quotas**: Make your users subscribe and accept terms automatically. Allow access to freemium and trial accounts. - **Guide Developers at Scale**: Automatically guide external developers on using your platform with behavioral emails, like they have exceeded the quota or any other rules you want to enforce. Streaming incompatible The activation of this component disables [streaming](/docs/endpoints/no-op/) on all endpoints (and the full response uses buffering instead). ## Monetization and API Analytics configuration To push data to your Moesif account, the configuration you need is straightforward, as you only need to add your Application ID and a few additional settings to map your users and companies correctly. Here is an example configuration: ```json { "version": 3, "extra_config": { "telemetry/moesif": { "@comment": "Push user activity to Moesif based on the information contained in JWT, every second, in batches of 1000 events", "application_id": "yourapplicationid", "user_id_headers": [ "Authorization" ], "user_id_jwt_claim": "sub", "identify_company": { "jwt_claim": "company_id" }, "debug": false, "log_body": false, "event_queue_size": 1000000, "batch_size": 1000, "timer_wake_up_seconds": 1 } } } ``` The list of all the supported options is as follows: ##### Fields of Moesif integration \* required fields ##### [`application_id`](https://www.krakend.io/docs/enterprise/governance/moesif/#application_id "Click to copy link to application_id") * string The Collector Application ID is used to send events, actions, users, and companies to Moesif’s Collector API. Moesif provides it under the ‘API Keys’ section. ##### [`batch_size`](https://www.krakend.io/docs/enterprise/governance/moesif/#batch_size "Click to copy link to batch_size") integer Number of events you will send on every batch reporting asynchronously to Moesif. For high throughput you will need to increase this value. Defaults to `200` ##### [`debug`](https://www.krakend.io/docs/enterprise/governance/moesif/#debug "Click to copy link to debug") boolean Set to true when configuring Moesif for the first time while in development, to see the activity in the logs. Set to false in production. Defaults to `false` ##### [`event_queue_size`](https://www.krakend.io/docs/enterprise/governance/moesif/#event_queue_size "Click to copy link to event_queue_size") integer Sends the number of events you can hold in-memory to send them asynchronously to Moesif. If the throughput of your API generates more events than the size of the queue, the exceeding events will be discarded and not reported. Defaults to `1000000` ##### [`identify_company`](https://www.krakend.io/docs/enterprise/governance/moesif/#identify_company "Click to copy link to identify_company") object It sets which strategy you want to use to identify the company. Identifying the company helps you efficiently govern your API. Choose the system you wish to apply (**declare only one property**). The claim value you access must be of type string. You can access nested structured using the dot `.` separator. When using dots, literals with an exact match containing the dot are checked first. ##### [`header`](#identify_company-header "Click to copy link to header") string The company is identified using a header. Provide the header name. Example: `"X-Tenant"` ##### [`jwt_claim`](#identify_company-jwt_claim "Click to copy link to jwt_claim") string The company is stored in a claim inside the JWT. The claim must return a string. Example: `"company_id"` ##### [`query_string`](#identify_company-query_string "Click to copy link to query_string") string The company is always passed inside a query string when calling any URL. Provide the query string name. Example: `"company"` ##### [`log_body`](https://www.krakend.io/docs/enterprise/governance/moesif/#log_body "Click to copy link to log_body") boolean Send the body of all endpoints and requests to Moesif. Defaults to `true` ##### [`metadata`](https://www.krakend.io/docs/enterprise/governance/moesif/#metadata "Click to copy link to metadata") object A free-form object that allows you to push custom metadata along with events. The custom metadata appears in Moesif under a key `krakend`, you can use nesting if needed. Example: `{"instance_id":"1234abcd","region":"us-west-1"}` ##### [`request_body_masks`](https://www.krakend.io/docs/enterprise/governance/moesif/#request_body_masks "Click to copy link to request_body_masks") array of strings The list of fields in the request body that you want to mask before sending them to Moesif. You can set `log_body` to `false` to prevent any body being sent. Example: `["password","credit_card"]` ##### [`request_header_masks`](https://www.krakend.io/docs/enterprise/governance/moesif/#request_header_masks "Click to copy link to request_header_masks") array of strings The list of request headers that you want to mask their values before sending them to Moesif. Example: `["Authorization"]` ##### [`response_body_masks`](https://www.krakend.io/docs/enterprise/governance/moesif/#response_body_masks "Click to copy link to response_body_masks") array of strings The list of fields in the response body that you want to mask before sending them to Moesif. You can set `log_body` to `false` to prevent any body being sent. Example: `["password","credit_card"]` ##### [`response_header_masks`](https://www.krakend.io/docs/enterprise/governance/moesif/#response_header_masks "Click to copy link to response_header_masks") array of strings The list of response headers that you want to mask their values before sending them to Moesif. Example: `["Cookie"]` ##### [`should_skip`](https://www.krakend.io/docs/enterprise/governance/moesif/#should_skip "Click to copy link to should_skip") string Defines an expression expressed as [Security Policy](https://www.krakend.io/docs/enterprise/security-policies/) that avoids reporting to Moesif when the result of the evaluation is `true`. Example: `"( req_method=='GET' || req_path.startsWith('/bar/')) && hasHeader('X-Something')"` ##### [`timer_wake_up_seconds`](https://www.krakend.io/docs/enterprise/governance/moesif/#timer_wake_up_seconds "Click to copy link to timer_wake_up_seconds") integer Specifies how often a background thread runs to send events to Moesif. Value in seconds. Defaults to `2` ##### [`user_id_headers`](https://www.krakend.io/docs/enterprise/governance/moesif/#user_id_headers "Click to copy link to user_id_headers") * array of strings Defines the list of possible headers that can identify a user uniquely. When the header is `Authorization`, it automatically extracts the username if it contains an `Authorization: Basic` value with no additional configuration. If, on the other hand, you use tokens and pass an `Authorization: Bearer`, it will extract the user ID from the JWT claim defined under `user_id_jwt_claim`. If there are multiple headers in the list, all of them are tested in the given order, and the first existing header in the list is used to extract the user ID (successfully or not). Example: `["X-User-ID","Authorization"]` ##### [`user_id_jwt_claim`](https://www.krakend.io/docs/enterprise/governance/moesif/#user_id_jwt_claim "Click to copy link to user_id_jwt_claim") string When using JWT tokens, it defines which claim contains the user ID. The claim value you access must be of type string. You can access nested structured using the dot `.` separator. When using dots, literals with an exact match containing the dot are checked first. Examples: `"sub"` , `"user.id"` Defaults to `"sub"` Schema: [https://www.krakend.io/schema/v2.13/telemetry/moesif.json](https://www.krakend.io/schema/v2.13/telemetry/moesif.json "JSON schema definition") You have the freedom to configure the size of the queue, the wake-up timer, and the batch size to optimize resources and gain efficiency. When you enable API Analytics and Monetization, the gateway must hold information in transit until it’s pushed to the Moesif API in memory. Depending on the cardinality of your endpoints, the size of the requests and responses, etc., the memory and CPU consumption will increase accordingly. --- # [Telemetry and Monitoring](https://www.krakend.io/docs/telemetry/) KrakenD’s **no lock-in philosophy** emphasizes flexibility and interoperability, ensuring technology-specific dependencies don’t constrain you. Giving you choices for observability and networking tools is key to success in a distributed microservices architecture. Our [OpenTelemetry](/docs/telemetry/opentelemetry/) integration and its previous predecessors [OpenCensus](/docs/telemetry/opencensus/), and the [Metrics API](/docs/telemetry/extended-metrics/), are part of this effort. Our components provide: - Ways to export data to detect root causes of problems. - Monitoring and details of the different distributed transactions. - Performance and latency optimization in the systems of your choice. - The flexibility to use the monitoring system that you have chosen, and not one that you are locked-in **OpenTelemetry** presents a unified, open-source framework for collecting and managing telemetry data across distributed systems, such as traces and metrics. It offers **vendor neutrality**, **simplifies instrumentation**, and **avoids lock-in** with specific monitoring platforms. You can choose between **over twenty different providers**. [Add OTEL to your configuration](/docs/telemetry/opentelemetry/). **OpenCensus** is the previous component which has provided reliable service for over six years for traces and metrics, and now its development is frozen in favour of OpenTelemetry. You can still use [OpenCensus telemetry](/docs/telemetry/opencensus/), although we recommend you to plan a transition to OpenTelemetry. The **Metrics API** and its native exporter to InfluxDB are in a similar situation. It was our richest exporter of metrics data until the OpenTelemetry release, and while it still works, its development has also frozen. [See the Metrics API](/docs/telemetry/extended-metrics/) If starting with a new project, choose an OpenTelemetry integration for metrics and traces. ## OpenTelemetry integrations As OpenTelemetry is **an open standard**, any provider adopting it using the wire protocol will automatically be compatible with KrakenD. More than 50 vendors natively support it. So the question of *does KrakenD support provider X?* can be answered with another question: *Does your provider offer an OpenTelemetry integration?*. As providers and software makers make an ongoing effort to adopt OpenTelemetry, you can find an extensive list of systems, SaaS or on-premise, that are on this path, and more are coming (see [vendors who natively support OpenTelemetry](https://opentelemetry.io/ecosystem/vendors/)). If you work with KrakenD and a piece of software that is not in the list below, please add it! ### Self-hosted systems using OTEL When you want to have complete control of your metrics and traces, this is a list of software you can install in your infrastructure: - **Prometheus**: An open-source system monitoring and alerting toolkit. - **Jaeger**: An open-source, self-hosted solution for distributed tracing. - **Elastic APM**: Part of the Elastic Stack, can be self-hosted for full control over data and infrastructure. - **Grafana Tempo**: Integrates with Grafana can be self-hosted for tracing data. ### SaaS systems using OTEL If you want to use a third-party SaaS, here is a list of a few APM systems that vary in their specific offerings, such as AI capabilities, ease of integration, visualization tools, and support for different programming languages and frameworks: - **New Relic**: Offers comprehensive monitoring with native support for OpenTelemetry. - **Datadog**: Provides extensive analytics and monitoring, supporting OpenTelemetry protocols. - **Dynatrace**: Known for AI-powered analytics and robust OpenTelemetry integration. - **Splunk APM (formerly SignalFx)**: Offers real-time analytics and visualization compatible with OpenTelemetry. - **AppDynamics (Cisco)**: Supports OpenTelemetry, providing performance analysis and proactive alerting. - **Elastic APM**: Part of the Elastic Stack integrates well with OpenTelemetry. - **Instana**: Offers automated APM for microservices with OpenTelemetry support. - **Google Cloud’s operations suite (formerly Stackdriver)**: Provides an integrated suite for monitoring, logging, and diagnostics, compatible with OpenTelemetry. - **AWS X-Ray**: Supports OpenTelemetry for applications running in AWS environments. - **Azure Monitor**: Fully compatible with OpenTelemetry for monitoring applications on Azure. - **Jaeger**: An open-source platform for distributed tracing that supports OpenTelemetry. - **Lightstep**: Delivers detailed insights and is compatible with the OpenTelemetry protocol. - **Honeycomb.io**: Emphasizes understanding production systems and supports OpenTelemetry. - **Sumo Logic**: Offers cloud-native solutions with OpenTelemetry integration. - **SolarWinds AppOptics**: Combines APM features with cloud monitoring, supporting OpenTelemetry. - **LogicMonitor**: Known for its automated monitoring solutions that are compatible with OpenTelemetry. - **Scout APM**: A developer-centric monitoring tool that supports OpenTelemetry. - **Rollbar**: Focuses on real-time error tracking and debugging with OpenTelemetry support. - **Wavefront by VMware**: A streaming analytics platform integrating with OpenTelemetry. The adoption of OpenTelemetry by these platforms indicates a strong industry shift towards standardized, open-source observability solutions. **You should test the ones you need on KrakenD** and ensure they deliver what you seek; from KrakenD, we are unfamiliar with every vendor out there. [Configure OpenTelemetry](/docs/telemetry/opentelemetry/ "Configure OpenTelemetry") --- # [Telemetry and Monitoring through OpenTelemetry](https://www.krakend.io/docs/telemetry/opentelemetry/) OpenTelemetry (for short **OTEL**) offers a comprehensive, unified, and **vendor-neutral** approach to collecting and managing telemetry data, providing enhanced observability and deeper insights into application performance and behavior. It’s particularly beneficial in complex, distributed, and cloud-native environments. OpenTelemetry captures detailed, contextual information about the operation of your applications. This includes not only **metrics** but also **tracing data** that shows the full lifecycle of requests as they flow through your systems, providing insights into performance bottlenecks, latency issues, and error diagnostics. This OpenTelemetry **does not** support pushing logs. See [logging](/docs/logging/), [GELF](/docs/logging/graylog-gelf/), or [Logstash](/docs/logging/logstash/) instead. It supports auto-instrumentation and can be integrated seamlessly into cloud-native deployments, making it easier to monitor these dynamic environments. Stability note on OpenTelemetry KrakenD has traditionally offered part of its telemetry integration through the [OpenCensus integration](/docs/telemetry/opencensus/), which has provided a reliable service for over six years. We are transitioning to the more modern and robust OpenTelemetry framework, and the OpenCensus integration does not receive further updates. While the underlying protocol specification of OpenTelemetry is stable, you’ll find [mixed stability statuses](https://opentelemetry.io/docs/specs/status/) in the components lifecycle. While we cannot predict what changes there will be as the technology evolves, KrakenD will always do its best to maintain compatibility between versions. More information about the underlying exporter can be found [here](https://opentelemetry.io/docs/languages/go/exporters/). ## Collecting metrics and traces The `telemetry/opentelemetry` component in KrakenD collects the activity generated for the enabled layers and pushes or exposes the data for pulling. There are two ways of publishing metrics: - **OpenTelemetry protocol (OTLP)** - push - **Prometheus** - pull You can use both simultaneously if needed, and even multiple instances of each. When you add OpenTelemetry in the configuration, you will have [different metrics available](/docs/telemetry/opentelemetry-layers-metrics/). ## Prometheus exporter (pull) Choose the `prometheus` exporter when you want KrakenD to **expose a new port offering a `/metrics` endpoint**. So, an external Prometheus job can connect to a URL like `http://krakend:9090/metrics` and retrieve all the data. ![Prometheus connecting to KrakenD and fetching metrics](/images/documentation/diagrams/opentelemetry-prometheus.mmd.svg) [See how to configure Prometheus](/docs/telemetry/prometheus/) ## OTLP exporter (push) Choose the `otlp` exporter when you want to **push the metrics to a local or remote collector** or directly to a SaaS or storage system that supports native OTLP (there is a [large number of supported providers](/docs/telemetry/#opentelemetry-integrations)). The following diagram represents this idea: ![KrakenD to collector, collector to backend](/images/documentation/diagrams/opentelemetry-otlp.mmd.svg) The `host` where your collector lives can also point to an external load balancer between KrakenD and multiple collectors if needed: ![KrakenD to load balanced collectors, collectors to backend](/images/documentation/diagrams/opentelemetry-otlp-lb.mmd.svg) Enterprise users can push directly to external storage passing auth credentials using the [`telemetry/opentelemetry-security` component](/docs/enterprise/telemetry/opentelemetry-security/), so the collector is not needed anymore: ![opentelemetry-otlp-auth.mmd diagram](/images/documentation/diagrams/opentelemetry-otlp-auth.mmd.svg) This strategy saves a lot of time during the setup of KrakenD. ## OpenTelemetry Configuration To enable OpenTelemetry, you will need a Prometheus or an OTEL Collector (or both) and add the `telemetry/opentelemetry` namespace at the top level of your configuration. The configuration of the `telemetry/opentelemetry` namespace is very extensive, but the two key entries are: - `exporters`, defining the different technologies you will use - `layers`, the amount of data you want to report In addition the environment variable `OTEL_PROPAGATORS` (not a configuration option) can be set to modify the propagators. It accepts a comma separated list with the values `tracecontext`, `baggage`, `b3` and `b3multi`. For instance you can set `OTEL_PROPAGATORS="b3,baggage"`. The entire configuration is as follows: ##### Fields of OpenTelemetry \* required fields ##### [`deploy_env`](https://www.krakend.io/docs/telemetry/opentelemetry/#deploy_env "Click to copy link to deploy_env") string The environment you are deploying, this can be useful for deployment tracking. The string can have any value that makes sense to you to identify the running environment. Examples: `"development"` , `"testing"` , `"staging"` , `"production"` Defaults to `""` ##### [`exporters`](https://www.krakend.io/docs/telemetry/opentelemetry/#exporters "Click to copy link to exporters") * object The places where you will send telemetry data. You can declare multiple exporters even when they are of the same type. For instance, when you have a self-hosted Grafana and would like to migrate to its cloud version and check the double reporting during the transition. There are two families of exporters: `otlp` or `prometheus`. ##### [`otlp`](#exporters-otlp "Click to copy link to otlp") array The list of OTLP exporters you want to use. Set at least one object to push metrics and traces to an external collector using OTLP. Each item of otlp accepts the following properties: ##### [`custom_metric_reporting_period`](#otlp-custom_metric_reporting_period "Click to copy link to custom_metric_reporting_period") integer Whether you want to override the global `metric_reporting_period` attribute set for all exporters or not. Value in seconds. A missing attribute, or set to `0` means using whatever value was used in `metric_reporting_period` at the global level. Defaults to `0` ##### [`disable_metrics`](#otlp-disable_metrics "Click to copy link to disable_metrics") boolean Disable metrics in this exporter (leaving only traces if any). It won’t report any metrics when the flag is `true`. Defaults to `false` ##### [`disable_traces`](#otlp-disable_traces "Click to copy link to disable_traces") boolean Disable traces in this exporter (leaving only metrics if any). It won’t report any metrics when the flag is `true`. Defaults to `false` ##### [`host`](#otlp-host "Click to copy link to host") * string The host where you want to push the data. It can be a sidecar or a remote collector. ##### [`name`](#otlp-name "Click to copy link to name") * string A unique name to identify this exporter. Examples: `"local_prometheus"` , `"remote_grafana"` ##### [`port`](#otlp-port "Click to copy link to port") integer A custom port to send the data. The port defaults to 4317 for gRPC unless you enable `use_http`, which defaults to 4318. Defaults to `4317` ##### [`use_http`](#otlp-use_http "Click to copy link to use_http") boolean Whether this exporter uses HTTP instead of gRPC. ##### [`prometheus`](#exporters-prometheus "Click to copy link to prometheus") array Set here at least the settings for one Prometheus exporter. Each exporter will start a local port that offers metrics to be pulled from KrakenD. Each item of prometheus accepts the following properties: ##### [`custom_metric_reporting_period`](#prometheus-custom_metric_reporting_period "Click to copy link to custom_metric_reporting_period") integer Whether you want to override the global `metric_reporting_period` attribute set for all exporters or not. Value in seconds. A missing attribute, or set to `0` means using whatever value was used in `metric_reporting_period` at the global level. Defaults to `0` ##### [`disable_metrics`](#prometheus-disable_metrics "Click to copy link to disable_metrics") boolean Leave this exporter declared but disabled (useful in development). It won’t report any metrics when the flag is `true`. Defaults to `false` ##### [`go_metrics`](#prometheus-go_metrics "Click to copy link to go_metrics") boolean Whether you want fine-grained details of Go language metrics or not. ##### [`listen_ip`](#prometheus-listen_ip "Click to copy link to listen_ip") string The IP address that KrakenD listens to in IPv4 or IPv6. You can, for instance, expose the Prometheus metrics only in a private IP address. An empty string, or no declaration means listening on all interfaces. The inclusion of `::` is intended for IPv6 format only (**this is not the port**). Examples of valid addresses are `192.0.2.1` (IPv4), `2001:db8::68` (IPv6). The values `::` and `0.0.0.0` listen to all addresses, which are valid for IPv4 and IPv6 simultaneously. Examples: `"172.12.1.1"` , `"::1"` Defaults to `"0.0.0.0"` ##### [`name`](#prometheus-name "Click to copy link to name") * string A unique name to identify this exporter. Examples: `"local_prometheus"` , `"remote_grafana"` ##### [`port`](#prometheus-port "Click to copy link to port") integer The port in KrakenD where Prometheus will connect to. Defaults to `9090` ##### [`process_metrics`](#prometheus-process_metrics "Click to copy link to process_metrics") boolean Whether this exporter shows detailed metrics about the running process like CPU or memory usage or not. ##### [`histograms`](https://www.krakend.io/docs/telemetry/opentelemetry/#histograms "Click to copy link to histograms") object Use an histogram bucket configuration different from the defaults to define the detail of histogram metrics (decrease or increase their size). You don’t need to set this attribute unless you want full control of the histogram definition. ##### [`size_buckets`](#histograms-size_buckets "Click to copy link to size_buckets") array of numbers The size of the buckets in bytes you want to use for the histograms. Defaults to `[128,256,512,1024,4096,8192,16384,32768,65536,262144,524288,1048576,4194304,16777216,67108864]` ##### [`time_buckets`](#histograms-time_buckets "Click to copy link to time_buckets") array of numbers The duration of buckets in seconds you want to use for the histograms. Defaults to `[0.01,0.02,0.05,0.075,0.1,0.125,0.15,0.175,0.2,0.25,0.3,0.35,0.5,0.75,1,1.5,2,3.5,5,10]` ##### [`layers`](https://www.krakend.io/docs/telemetry/opentelemetry/#layers "Click to copy link to layers") object A request and response flow passes through three different layers. This attribute lets you specify what data you want to export in each layer. All layers are enabled by default unless you declare this section. ##### [`backend`](#layers-backend "Click to copy link to backend") object Reports the activity between KrakenD and each of your backend services. This is the more granular layer. ##### [`metrics`](#backend-metrics "Click to copy link to metrics") object Show properties ##### [`detailed_connection`](#metrics-detailed_connection "Click to copy link to detailed_connection") boolean Whether you want to enable detailed metrics for the HTTP connection phase or not. Includes times to connect, DNS querying, and the TLS handshake. Defaults to `false` ##### [`disable_stage`](#metrics-disable_stage "Click to copy link to disable_stage") boolean Whether to turn off the metrics or not. Setting this to `true` means stop reporting any data. Defaults to `false` ##### [`read_payload`](#metrics-read_payload "Click to copy link to read_payload") boolean Whether you want to enable metrics for the response reading payload or not (HTTP connection not taken into account). Defaults to `false` ##### [`round_trip`](#metrics-round_trip "Click to copy link to round_trip") boolean Whether you want to enable metrics for the actual HTTP request for the backend or not (manipulation not taken into account). This is the time your backend needs to produce a result. Defaults to `false` ##### [`static_attributes`](#metrics-static_attributes "Click to copy link to static_attributes") array of objects A list of tags or labels you want to associate with these metrics. Example: `[{"key":"my_metric_attr","value":"my_metric_val"}]` Each item of static\_attributes accepts the following properties: ##### [`key`](#static_attributes-key "Click to copy link to key") * string The key, tag, or label name you want to use. ##### [`value`](#static_attributes-value "Click to copy link to value") * string The static value you want to assign to this key. ##### [`traces`](#backend-traces "Click to copy link to traces") object Show properties ##### [`detailed_connection`](#traces-detailed_connection "Click to copy link to detailed_connection") boolean Whether you want to add detailed trace attributes for the HTTP connection phase or not. Includes times to connect, DNS querying, and the TLS handshake. Defaults to `false` ##### [`disable_stage`](#traces-disable_stage "Click to copy link to disable_stage") boolean Whether to turn off the traces or not. Setting this to `true` means stop reporting any data. Defaults to `false` ##### [`read_payload`](#traces-read_payload "Click to copy link to read_payload") boolean Whether you want to add trace attributes for the response reading payload or not (HTTP connection not taken into account). Defaults to `false` ##### [`report_headers`](#traces-report_headers "Click to copy link to report_headers") boolean Whether you want to report the final headers that reached the backend. Defaults to `false` ##### [`round_trip`](#traces-round_trip "Click to copy link to round_trip") boolean Whether you want to add trace attributes for the actual HTTP request for the backend or not (manipulation not taken into account). This is the time your backend needs to produce a result. Defaults to `false` ##### [`skip_headers`](#traces-skip_headers "Click to copy link to skip_headers") array of strings A list of headers you want to skip when reporting the headers that reached the backend. This is useful to avoid reporting sensitive data. Example: `["X-Backend-Secret","X-All-Secret"]` ##### [`static_attributes`](#traces-static_attributes "Click to copy link to static_attributes") array of objects A list of tags or labels you want to associate to these traces. Example: `[{"key":"my_trace_attr","value":"my_trace_val"}]` Each item of static\_attributes accepts the following properties: ##### [`key`](#static_attributes-key "Click to copy link to key") * string The key, tag, or label name you want to use. ##### [`value`](#static_attributes-value "Click to copy link to value") * string The static value you want to assign to this key. ##### [`global`](#layers-global "Click to copy link to global") object Reports the activity between end-users and KrakenD ##### [`disable_metrics`](#global-disable_metrics "Click to copy link to disable_metrics") boolean Whether you want to disable all metrics happening in the global layer or not. Defaults to `false` ##### [`disable_propagation`](#global-disable_propagation "Click to copy link to disable_propagation") boolean Whether you want to ignore previous propagation headers to KrakenD. When the flag is set to `true`, spans from a previous layer will never be linked to the KrakenD trace. Defaults to `false` ##### [`disable_traces`](#global-disable_traces "Click to copy link to disable_traces") boolean Whether you want to disable all traces happening in the global layer or not. Defaults to `false` ##### [`metrics_static_attributes`](#global-metrics_static_attributes "Click to copy link to metrics_static_attributes") array Show properties Static attributes you want to pass for metrics. Each item of metrics\_static\_attributes accepts the following properties: ##### [`key`](#metrics_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#metrics_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send ##### [`report_headers`](#global-report_headers "Click to copy link to report_headers") boolean Whether you want to send all headers that the consumer passed in the request or not. Defaults to `false` ##### [`semantic_convention`](#global-semantic_convention "Click to copy link to semantic_convention") The semantic convention naming you want to use. The default is an empty string which uses the original naming convention prior to 1.27. For the semantic convention of 1.27 and higher, use `1.27` Possible values are: `""` , `"1.27"` ##### [`skip_headers`](#global-skip_headers "Click to copy link to skip_headers") array of strings A list of headers you want to skip when reporting the headers from the request. This is useful to avoid reporting sensitive data. Example: `["X-Request-Api-Key","X-Signature"]` ##### [`traces_static_attributes`](#global-traces_static_attributes "Click to copy link to traces_static_attributes") array Show properties Static attributes you want to pass for traces. Each item of traces\_static\_attributes accepts the following properties: ##### [`key`](#traces_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#traces_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send ##### [`proxy`](#layers-proxy "Click to copy link to proxy") object Reports the activity at the beginning of the proxy layer, including spawning the required requests to multiple backends, merging, endpoint transformation and any other internals of the proxy between the request processing and the backend communication ##### [`disable_metrics`](#proxy-disable_metrics "Click to copy link to disable_metrics") boolean Whether you want to disable all metrics happening in the proxy layer or not. Defaults to `false` ##### [`disable_traces`](#proxy-disable_traces "Click to copy link to disable_traces") boolean Whether you want to disable all traces happening in the proxy layer or not. Defaults to `false` ##### [`metrics_static_attributes`](#proxy-metrics_static_attributes "Click to copy link to metrics_static_attributes") array Show properties Static attributes you want to pass for metrics. Each item of metrics\_static\_attributes accepts the following properties: ##### [`key`](#metrics_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#metrics_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send ##### [`report_headers`](#proxy-report_headers "Click to copy link to report_headers") boolean Whether you want to report all headers that passed from the request to the proxy layer (`input_headers` policy in the endpoint plus KrakenD’s headers). Defaults to `false` ##### [`skip_headers`](#proxy-skip_headers "Click to copy link to skip_headers") array of strings A list of headers you want to skip when reporting headers passed to the proxy layer. This is useful to avoid reporting sensitive data. Example: `["X-Sensitive-Data","X-Signature"]` ##### [`traces_static_attributes`](#proxy-traces_static_attributes "Click to copy link to traces_static_attributes") array Show properties Static attributes you want to pass for traces. Each item of traces\_static\_attributes accepts the following properties: ##### [`key`](#traces_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#traces_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send ##### [`metric_reporting_period`](https://www.krakend.io/docs/telemetry/opentelemetry/#metric_reporting_period "Click to copy link to metric_reporting_period") integer How often you want to report and flush the metrics in seconds. This setting is only used by `otlp` exporters. Defaults to `30` ##### [`service_name`](https://www.krakend.io/docs/telemetry/opentelemetry/#service_name "Click to copy link to service_name") string A friendly name identifying metrics reported by this installation. When unset, it uses the `name` attribute in the root level of the configuration. ##### [`service_version`](https://www.krakend.io/docs/telemetry/opentelemetry/#service_version "Click to copy link to service_version") string The version you are deploying, this can be useful for deployment tracking. ##### [`skip_paths`](https://www.krakend.io/docs/telemetry/opentelemetry/#skip_paths "Click to copy link to skip_paths") array of strings The paths you don’t want to report. Use the literal value used in the `endpoint` definition, including any `{placeholders}`. In the `global` layer, this attribute works only on metrics, because traces are initiated before there is an endpoint to match against. If you do not want any path skipped, just add an array with an empty string `[""]`. Example: `["/foo/{bar}"]` Defaults to `["/__health","/__debug/","/__echo/","/__stats/"]` ##### [`trace_sample_rate`](https://www.krakend.io/docs/telemetry/opentelemetry/#trace_sample_rate "Click to copy link to trace_sample_rate") number The sample rate for traces defines the percentage of reported traces. This option is key to reduce the amount of data generated (and resource usage), while you still can debug and troubleshoot issues. For instance, a number of `0.25` will report a 25% of the traces seen in the system. Example: `0.25` Defaults to `1` Schema: [https://www.krakend.io/schema/v2.13/telemetry/opentelemetry.json](https://www.krakend.io/schema/v2.13/telemetry/opentelemetry.json "JSON schema definition") Here’s an example with a Grafana Tempo and a Prometheus. ```json { "version": 3, "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "extra_config": { "telemetry/opentelemetry": { "service_name": "krakend_middle_service", "service_version": "commit-sha-ACBDE1234", "deploy_env": "production", "exporters": { "prometheus": [ { "name": "my_prometheus", "port": 9092, "listen_ip": "::1", "process_metrics": false, "go_metrics": false } ], "otlp": [ { "name": "local_tempo", "host": "localhost", "port": 4317, "use_http": false } ] }, "layers": { "global": { "disable_metrics": false, "disable_traces": false, "disable_propagation": false }, "proxy": { "disable_metrics": false, "disable_traces": false }, "backend": { "metrics": { "disable_stage": false, "round_trip": true, "read_payload": true, "detailed_connection": true, "static_attributes": [ { "key": "my_metric_attr", "value": "my_middle_metric" } ] }, "traces": { "disable_stage": false, "round_trip": true, "read_payload": true, "detailed_connection": true, "static_attributes": [ { "key": "my_metric_attr", "value": "my_middle_metric" } ] } } }, "skip_paths": [ "/foo/{bar}" ] } } } ``` ## Examples of integrations - Push metrics to [InfluxDB](/docs/telemetry/influxdb/) - Pull metrics from [Prometheus](/docs/telemetry/prometheus/) - Push metrics to [Datadog](/docs/telemetry/datadog/) - Push metrics to [Zipkin](/docs/telemetry/zipkin/) - Push metrics to [Jaeger](/docs/telemetry/jaeger/) - Push metrics to [Azure Monitor](/docs/telemetry/azure/) --- # [Understanding OpenTelemetry layers and metrics](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/) You can add several `exporters` to your [OpenTelemetry configuration](/docs/telemetry/opentelemetry/#opentelemetry-configuration) (the more, the hungrier the gateway will be), and KrakenD will send data to all the declared exporters and layers by default. While `exporters` define **where** you will have the metrics, the `layers` define **which** metrics you want to have. The layers contain the traces and metrics for a subset of the execution flow. These are the layers you can use: ![Diagram showing global, proxy, and backend sequence](/images/documentation/diagrams/opentelemetry-layers.mmd.svg) - `global`: The global layer contains everything that KrakenD saw in and out. It includes the total timings for a request hitting the service until it is delivered to the client. - `proxy`: The proxy layer starts acting after processing the HTTP request and comprehends the internal work of dealing with the endpoint, including spawning multiple requests to the backends, aggregating their results, and manipulating the final response. - `backend`: The backend layer monitors the activity of a single backend request and response with any additional components in its level. It contains mainly the timings between KrakenD and your service. It is the richest layer of all. **It is vital to see that `backend` is a subset of `proxy`, and `proxy` is a subset of `global`** . Do not generate metrics for the layers you will not use. CPU and memory consumption As you will see below, the amount of telemetry data you can send for each request is massive. The more layers and details you add, the more resources KrakenD will consume and the more storage you will need to save metrics and traces. Choose carefully a balance between observability power and resource consumption to save money! In production, **you should only enable a small sample rate of the traces** (e.g., 5%). The data generated in telemetry is usually more extensive than the responses of the services you expose. Once you configure the OpenTelemetry component, KrakenD will start reporting metrics when there is activity. The section below describes different metrics in each layer, briefly explaining their meaning. **The presence of the metrics and the traces described below depend on the [OpenTelemetry configuration](/docs/telemetry/opentelemetry/#opentelemetry-configuration).** ## How to access telemetry data Once the OpenTelemetry configuration is in place, KrakenD will start to push metrics and traces as specified to the providers of your choice. The telemetry data is treated differently by each of the providers, and you might find slight differences depending on what you use. For instance, most OTEL integrations will use a dot in the metric name as a separator, while Prometheus will use an underscore instead. In addition, for metrics that are an histogram, providers will add a suffix to the metric. For instance, on Prometheus you will find a metric documented as `http_server_duration` but in Prometheus **will add automatic suffixes** as follow: - `http_server_duration_bucket`: Counts how many observations fall into each range. - `http_server_duration_sum`: Tracks the total sum of all recorded values. - `http_server_duration_count`: Counts the total number of recorded observations. This is a sample of what you will find: ### Metrics of the `sample` layer Description of what the metric is about [`metric.name` `metric_name` datatype](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-example-metric.name "Permalink to metric.name") The description of the metric goes here. Notice the metric format above for OTEL or Prometheus. → [`dimension.name`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-example-metric.name-dimension.name "Permalink to dimension.name") type Description of the dimension and what it represents. Below you’ll find all possible metrics and traces ## Data exposed in the global layer The `global` layer reveals all timings in and out of KrakenD. If you have custom server plugins, their timings are also included here. Enable this layer when you want to see the complete picture of the gateway. You will have metrics, traces, or both available depending on the exporter you use. ### Metrics of the `global` layer Present when you have an exporter able to collect metrics and the `global` attribute configured inside `layers` [`http.server.duration` `http_server_duration` float](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-http.server.duration "Permalink to http.server.duration") Histogram of the time in seconds that it takes to produce the response → [`http.response.status_code`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-http.server.duration-http.response.status_code "Permalink to http.response.status_code") integer Status code of the produced response → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-http.server.duration-http.route "Permalink to http.route") string The matched endpoint path → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-http.server.duration-http.request.method "Permalink to http.request.method") string The method sent by the user (GET, POST, etc.) [`http.server.response.size` `http_server_response_size` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-http.server.response.size "Permalink to http.server.response.size") Histogram of the size of the body produced for the response in bytes → [`http.response.status_code`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-http.server.response.size-http.response.status_code "Permalink to http.response.status_code") integer Status code of the produced response → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-http.server.response.size-http.route "Permalink to http.route") string The matched endpoint path → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-http.server.response.size-http.request.method "Permalink to http.request.method") string The method sent by the user (GET, POST, etc.) ### Traces of the `global` layer Present when you have an exporter able to collect traces and the `global` attribute configured inside `layers`. In case an error happens, the span will record the error and set the span status to error (value = 1), in case of success the span status is set to ok (value = 2) as per the OTEL definition. [`/foo/123` string](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-%2ffoo%2f123 "Permalink to /foo/123") A span is created with the name of the requested path and is depicted as `/foo/123` as an example. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-%2ffoo%2f123-krakend.stage "Permalink to krakend.stage") string Contains the value `global`. It’s meant to filter traces of this stage. → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-%2ffoo%2f123-http.route "Permalink to http.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. In case no matching is made, for example in 404 results, it would be empty. → [`http.response.body.size`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-%2ffoo%2f123-http.response.body.size "Permalink to http.response.body.size") integer Histogram of the size in bytes of the body produced for the response. → [`http.response.status_code`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-%2ffoo%2f123-http.response.status_code "Permalink to http.response.status_code") integer Status code of the produced response → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-%2ffoo%2f123-http.request.method "Permalink to http.request.method") string The method sent by the user (GET, POST, etc.) → [`http.request.header.xxx`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-%2ffoo%2f123-http.request.header.xxx "Permalink to http.request.header.xxx") array When the `report_headers` is set in the configuration, you will have one extra span for each incoming header (`xxx` represents here any header name in lower case). → [`http.connection.hijacked`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-global-%2ffoo%2f123-http.connection.hijacked "Permalink to http.connection.hijacked") bool If an HTTP request is upgraded to WebSockets, gRPC, etc. and cannot be longer treated as HTTP. ## Data exposed in the proxy layer The proxy layer starts counting **after processing the HTTP request** and comprehends the internal work of dealing with the endpoint, including spawning multiple requests to the backends, aggregating their results, and manipulating the final response. ### Metrics of the `proxy` layer Present when you have an exporter able to collect metrics and the `proxy` attribute configured inside `layers` [`krakend.proxy.duration` `krakend_proxy_duration` float](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-krakend.proxy.duration "Permalink to krakend.proxy.duration") Histogram of the time in seconds that it takes to produce the response → [`error`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-krakend.proxy.duration-error "Permalink to error") bool When the endpoint failed to fetch any data from the backend(s). → [`canceled`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-krakend.proxy.duration-canceled "Permalink to canceled") bool When the client aborted the request → [`complete`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-krakend.proxy.duration-complete "Permalink to complete") bool When all backends successfully returned their information. Matches the header `X-KrakenD-Complete`. → [`http.response.status_code`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-krakend.proxy.duration-http.response.status_code "Permalink to http.response.status_code") integer Status code of the produced response after aggregation → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-krakend.proxy.duration-http.route "Permalink to http.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-krakend.proxy.duration-http.request.method "Permalink to http.request.method") string The method sent by the user (GET, POST, etc.) ### Traces of the `proxy` layer Present when you have an exporter able to collect traces and the `proxy` attribute configured inside `layers`. The spans revealed here include aggregation. [`/foo/123` string](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-%2ffoo%2f123 "Permalink to /foo/123") A span is created with the name of the requested path and is depicted as `/foo/123` as an example. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-%2ffoo%2f123-krakend.stage "Permalink to krakend.stage") string Contains the value `proxy`. It’s meant to filter traces of this stage. → [`error`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-%2ffoo%2f123-error "Permalink to error") string When there is an error, the description → [`canceled`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-%2ffoo%2f123-canceled "Permalink to canceled") bool It appears with a `true` value when the request has been canceled (usually when `concurrent_calls` are used). → [`complete`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-%2ffoo%2f123-complete "Permalink to complete") bool When all backends successfully returned their information. Matches the header `X-KrakenD-Complete`. → [`http.request.header.xxx`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-%2ffoo%2f123-http.request.header.xxx "Permalink to http.request.header.xxx") array When the `report_headers` is set in the configuration, you will have one extra span for each incoming header (`xxx` represents here any header name in lower case). → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-%2ffoo%2f123-http.request.method "Permalink to http.request.method") string The method declared in the endpoint → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-%2ffoo%2f123-http.route "Permalink to http.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. In case no matching is made, for example in 404 results, it would be empty. → [`http.response.status_code`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-proxy-%2ffoo%2f123-http.response.status_code "Permalink to http.response.status_code") integer Status code of the produced response after aggregation ## Data exposed in the backend layer The backend layer monitors the activity of a single backend request and response with any additional components in its level. It contains mainly the timings between KrakenD and your service. It is the richest layer of all. ### Metrics of the `backend` layer Present when you have an exporter able to collect metrics and the `backend` attribute configured inside `layers`. To see all `http.client` metrics you need to enable the `round_trip` in the backend layer. To see the `http.client.request.` header you must enable the `read_payload` flag [`krakend.backend.duration` `krakend_backend_duration` float](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-krakend.backend.duration "Permalink to krakend.backend.duration") Histogram of the time in seconds that it takes to produce the response. The metric includes the whole timing, including any used to perform manipulations at the backend level. → [`error`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-krakend.backend.duration-error "Permalink to error") bool When the backend request failed. → [`canceled`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-krakend.backend.duration-canceled "Permalink to canceled") bool When the gateway aborted the request to the backend (e.g., got an answer from another thread using `concurrent_calls`) → [`complete`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-krakend.backend.duration-complete "Permalink to complete") bool When the backend successfully returned the information. → [`http.response.status_code`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-krakend.backend.duration-http.response.status_code "Permalink to http.response.status_code") integer Status code of the produced response after aggregation → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-krakend.backend.duration-http.route "Permalink to http.route") string The associated `url_pattern` for this route. → [`krakend.endpoint.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-krakend.backend.duration-krakend.endpoint.route "Permalink to krakend.endpoint.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-krakend.backend.duration-krakend.stage "Permalink to krakend.stage") string Contains the value `backend`. It’s meant to filter traces of this stage. → [`server.address`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-krakend.backend.duration-server.address "Permalink to server.address") string Targeted hosts. In case more than one is provided, these are joined with `_`. [`http.client.request.started.count` `http_client_request_started_count` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.started.count "Permalink to http.client.request.started.count") The number of requests started. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.started.count-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.started.count-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.started.count-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.request.failed.count` `http_client_request_failed_count` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.failed.count "Permalink to http.client.request.failed.count") The number of requests that failed. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.failed.count-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.failed.count-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.failed.count-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.request.canceled.count` `http_client_request_canceled_count` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.canceled.count "Permalink to http.client.request.canceled.count") The number of requests canceled. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.canceled.count-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.canceled.count-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.canceled.count-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.request.timedout.count` `http_client_request_timedout_count` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.timedout.count "Permalink to http.client.request.timedout.count") The number of requests that timed-out. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.timedout.count-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.timedout.count-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.timedout.count-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.request.content.length` `http_client_request_content_length` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.content.length "Permalink to http.client.request.content.length") Counter with the sum of bytes in the `Content-Length` header. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.content.length-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.content.length-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.content.length-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.duration` `http_client_duration` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.duration "Permalink to http.client.duration") Histogram with the seconds taken until receiving the first byte of the response. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.duration-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.duration-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.duration-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.response.size` `http_client_response_size` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.response.size "Permalink to http.client.response.size") Histogram with the size of response bodies in bytes as reported in the `Content-Lenght` header. When the header is absent, the metric does not exist. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.response.size-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.response.size-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.response.size-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.response.no-content-length` `http_client_response_no-content-length` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.response.no-content-length "Permalink to http.client.response.no-content-length") When the `Content-Length` is not set, this metric is created. If you sum the number of `http.client.response.size` and the value of `http.client.response.no-content-length` they represent the total responses. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.response.no-content-length-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.response.no-content-length-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.response.no-content-length-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.request.get-conn.duration` `http_client_request_get-conn_duration` float](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.get-conn.duration "Permalink to http.client.request.get-conn.duration") Reports the duration of the connection with the backend in seconds, excluding any data processing time. Requires the flag `detailed_connection` → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.get-conn.duration-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.get-conn.duration-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.get-conn.duration-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.request.tls.duration` `http_client_request_tls_duration` float](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.tls.duration "Permalink to http.client.request.tls.duration") Reports the duration of the TLS negotiation in seconds. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.tls.duration-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.tls.duration-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.tls.duration-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.request.dns.duration` `http_client_request_dns_duration` float](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.dns.duration "Permalink to http.client.request.dns.duration") Reports the duration of the DNS retrieval in seconds. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.dns.duration-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.dns.duration-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.dns.duration-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. [`http.client.request.read.size` `http_client_request_read_size` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size "Permalink to http.client.request.read.size") Number of read bytes. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.endpoint.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-krakend.endpoint.route "Permalink to krakend.endpoint.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. → [`krakend.endpoint.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-krakend.endpoint.method "Permalink to krakend.endpoint.method") string The associated method of the parent endpoint. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. → [`server.address`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-server.address "Permalink to server.address") string Targeted host. [`http.client.request.read.size-hist` `http_client_request_read_size-hist` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-hist "Permalink to http.client.request.read.size-hist") Histogram with the read bytes. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-hist-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-hist-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.endpoint.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-hist-krakend.endpoint.route "Permalink to krakend.endpoint.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. → [`krakend.endpoint.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-hist-krakend.endpoint.method "Permalink to krakend.endpoint.method") string The associated method of the parent endpoint. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-hist-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. → [`server.address`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.size-hist-server.address "Permalink to server.address") string Targeted host. [`http.client.request.read.time` `http_client_request_read_time` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time "Permalink to http.client.request.read.time") Counter with seconds spent reading the body payload of the response. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.endpoint.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-krakend.endpoint.route "Permalink to krakend.endpoint.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. → [`krakend.endpoint.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-krakend.endpoint.method "Permalink to krakend.endpoint.method") string The associated method of the parent endpoint. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. → [`server.address`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-server.address "Permalink to server.address") string Targeted host. [`http.client.request.read.time-hist` `http_client_request_read_time-hist` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-hist "Permalink to http.client.request.read.time-hist") Histogram with the seconds spent reading the body. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-hist-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-hist-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.endpoint.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-hist-krakend.endpoint.route "Permalink to krakend.endpoint.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. → [`krakend.endpoint.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-hist-krakend.endpoint.method "Permalink to krakend.endpoint.method") string The associated method of the parent endpoint. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-hist-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. → [`server.address`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.time-hist-server.address "Permalink to server.address") string Targeted host. [`http.client.request.read.errors` `http_client_request_read_errors` integer](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.errors "Permalink to http.client.request.read.errors") A counter with the number of errors that happened reading the response body. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.errors-http.request.method "Permalink to http.request.method") string The method used to connect to the backend → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.errors-http.route "Permalink to http.route") string The resolved `url_pattern` for this route. → [`krakend.endpoint.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.errors-krakend.endpoint.route "Permalink to krakend.endpoint.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. → [`krakend.endpoint.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.errors-krakend.endpoint.method "Permalink to krakend.endpoint.method") string The associated method of the parent endpoint. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.errors-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. → [`server.address`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.errors-server.address "Permalink to server.address") string Targeted host. ### Traces of the `backend` layer Present when you have an exporter able to collect traces and the `backend` attribute configured inside `layers`. There is a span for the `backend` part, which includes possible backend manipulations, and another span for the `backend-request`, which include the interaction with the backend. [`/my-pattern` string](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern "Permalink to /my-pattern") A span is created with the name of the resolved `url_pattern` is depicted as `/my-pattern` as an example. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-krakend.stage "Permalink to krakend.stage") string Contains the value `backend`. It’s meant to filter traces of this stage. → [`error`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-error "Permalink to error") string When there is an error, the description → [`canceled`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-canceled "Permalink to canceled") bool It appears with a `true` value when the request has been canceled (usually when `concurrent_calls` are used). → [`complete`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-complete "Permalink to complete") bool When the backend successfully returned the information. → [`http.request.header.xxx`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-http.request.header.xxx "Permalink to http.request.header.xxx") array When the `report_headers` is set in the configuration, you will have one extra span for each incoming header (`xxx` represents here any header name in lower case). → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-http.request.method "Permalink to http.request.method") string The method declared in the endpoint → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-http.route "Permalink to http.route") string The associated `url_pattern` for this route. → [`krakend.endpoint.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-krakend.endpoint.route "Permalink to krakend.endpoint.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. → [`krakend.endpoint.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-krakend.endpoint.method "Permalink to krakend.endpoint.method") string The associated method of the parent endpoint. → [`server.address`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-server.address "Permalink to server.address") string Targeted hosts. In case more than one is provided, these are joined with `_`. [`/my-pattern` string](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern "Permalink to /my-pattern") A span is created with the name of the resolved `url_pattern` is depicted as `/my-pattern` as an example. Notice that this span shares the name with the previous but is for the `backend-request` instead of `backend`. → [`krakend.stage`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-krakend.stage "Permalink to krakend.stage") string Contains the value `backend-request`. It’s meant to filter traces of this stage. → [`dns-duration`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-dns-duration "Permalink to dns-duration") integer The duration in seconds of the DNS resolution. Requires the flag `detailed_connection` → [`get-conn-duration`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-get-conn-duration "Permalink to get-conn-duration") integer The duration in seconds to establish the connection. Requires the flag `detailed_connection` → [`tls-duration`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-tls-duration "Permalink to tls-duration") integer The duration in seconds to perform the TLS handshake. Requires the flag `detailed_connection` → [`http.request.body.size`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-http.request.body.size "Permalink to http.request.body.size") integer Size of the request body (payload) in bytes. → [`http.request.header.xxx`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-http.request.header.xxx "Permalink to http.request.header.xxx") array When the `report_headers` is set in the configuration, you will have one extra span for each incoming header (`xxx` represents here any header name in lower case). → [`http.response.status_code`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-http.response.status_code "Permalink to http.response.status_code") integer Status code of the produced response after aggregation → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-http.route "Permalink to http.route") string The associated `url_pattern` for this route. → [`krakend.endpoint.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-krakend.endpoint.route "Permalink to krakend.endpoint.route") string The associated endpoint definition for this route. E.g., `/foo/{bar}`. → [`http.request.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-http.request.method "Permalink to http.request.method") string The method declared in the endpoint → [`krakend.endpoint.method`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-krakend.endpoint.method "Permalink to krakend.endpoint.method") string The associated method of the parent endpoint. → [`http.route`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-http.route "Permalink to http.route") string The associated `url_pattern` for this route. → [`server.address`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-server.address "Permalink to server.address") string Targeted host. → [`url.full`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-url.full "Permalink to url.full") string The complete address used to retrieve the content, including protocol, port and path (e.g., `http://example.com:9876/path`) → [`user_agent.original`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-%2fmy-pattern-user_agent.original "Permalink to user_agent.original") string The user agent used when connecting [`http.client.request.read.tracker` `http_client_request_read_tracker` string](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.tracker "Permalink to http.client.request.read.tracker") When the `read_payload` option is set to true in the configuration, the time spent to read the body. Enable only in debugging scenarios. → [`http.client.request.read.size`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.tracker-http.client.request.read.size "Permalink to http.client.request.read.size") integer Number of read bytes. → [`http.client.request.read.size`](https://www.krakend.io/docs/telemetry/opentelemetry-layers-metrics/#layer-backend-http.client.request.read.tracker-http.client.request.read.size "Permalink to http.client.request.read.size") integer Seconds spent to read the request ## Measuring added latency and similar metrics A common metric developers like to measure is the gateway’s added latency and other similar metrics. In order to get coherent results you must take into account that: - Having more than one backend in a single endpoint will produce more than one record for the `http.client.duration` metric. - Requests handled by KrakenD might not reach backends. So, to find the time spent in KrakenD, subtract the **sum** of `http.client.duration` from the **sum** of `http.server.duration` **only for successful responses** (200 OK) on **endpoints with a single backend**, and divide by the number of received requests. This yields an average per-request added latency attributable to KrakenD. This is a restrictive scenario, so unless you want to continuously monitor that metric, you should enable traces (with sampling) and inspect a few requests to get a better idea of added latency per endpoint. Traces provide a tree view showing how much time is spent in each “stage” of the request and give more actionable insight than aggregated metrics alone. Let’s deep dive a little… Incoming requests are handled at the global layer first. There are many factors that determine whether they reach the backend or not. The gateway is meant to prematurely terminate illegitimate traffic, so **you will never have the same number of records in the global layer as in the other** layers. For example, a not-found 404 route, an invalid JWT token, a violated policy, or a server plugin raising an error, to name a few examples, are examples of records that you can see only in the global layer. As they do not succeed, the request does not produce a record for the proxy or backend layers and your upstream services were never reached in those cases. In other words, **there is never a 1:1 relationship between the global and proxy layers**, because some requests will not pass through the global layer. Those requests are handled very quickly (under a millisecond), which will skew averages if mixed with normal proxy/backend timings. Another aspect to have into account is that KrakenD is not a simple proxy, so an endpoint might have more than one backend request. The interpretation of metrics can also be misleading when an endpoint spawns multiple parallel backend requests. For example, if you have two backend requests and one takes `10 ms` and the other `100 ms`, the naive average of those two backend durations is `55 ms`, but the proxy layer will report a single duration that is higher than `100 ms` (because it waits for all backends and aggregates). That could lead you to incorrectly assume the proxy layer is adding more latency than it actually is. Traces and sampling are the best way to validate and understand these cases: a few sampled traces will show the timings for each backend call and the proxy’s aggregation time. --- # [Granular OpenTelemetry by endpoint](https://www.krakend.io/docs/telemetry/opentelemetry-by-endpoint/) The OpenTelemetry configuration is declared [at the service level](/docs/telemetry/opentelemetry/), but you can override metrics and traces per endpoint and per backend as follows. ## Endpoint override of metrics and traces The following example overrides properties that could be declared at the service level. ```json { "endpoints": [ { "endpoint": "/example", "backend": [ { "host": [ "example.com" ], "url_pattern": "/example" } ], "extra_config": { "telemetry/opentelemetry": { "proxy": { "disable_metrics": false, "disable_traces": false, "report_headers": true, "traces_static_attributes": [ { "key": "owner", "value": "team-charlie" } ], "metrics_static_attributes": [ { "key": "owner", "value": "team-charlie" } ] } } } } ] } ``` The full list of options is: ##### OpenTelemetry settings per endpoint \* required fields ##### [`proxy`](https://www.krakend.io/docs/telemetry/opentelemetry-by-endpoint/#proxy "Click to copy link to proxy") object Reports the activity at the beginning of the proxy layer, including spawning the required requests to multiple backends, merging, endpoint transformation and any other internals of the proxy between the request processing and the backend communication ##### [`disable_metrics`](#proxy-disable_metrics "Click to copy link to disable_metrics") boolean Whether you want to disable all metrics in this endpoint or not. Defaults to `false` ##### [`disable_traces`](#proxy-disable_traces "Click to copy link to disable_traces") boolean Whether you want to disable all traces in this endpoint or not. Defaults to `false` ##### [`metrics_static_attributes`](#proxy-metrics_static_attributes "Click to copy link to metrics_static_attributes") array Static attributes you want to pass for metrics. Each item of metrics\_static\_attributes accepts the following properties: ##### [`key`](#metrics_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#metrics_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send ##### [`report_headers`](#proxy-report_headers "Click to copy link to report_headers") boolean Whether you want to report all headers that passed from the request to the proxy layer (`input_headers` policy in the endpoint plus KrakenD’s headers). Defaults to `false` ##### [`skip_headers`](#proxy-skip_headers "Click to copy link to skip_headers") array of strings A list of headers you want to skip when reporting headers passed to the proxy layer. This is useful to avoid reporting sensitive data. Example: `["X-Sensitive-Data","X-Signature"]` ##### [`traces_static_attributes`](#proxy-traces_static_attributes "Click to copy link to traces_static_attributes") array Static attributes you want to pass for traces. Each item of traces\_static\_attributes accepts the following properties: ##### [`key`](#traces_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#traces_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send Schema: [https://www.krakend.io/schema/v2.13/telemetry/opentelemetry-endpoint.json](https://www.krakend.io/schema/v2.13/telemetry/opentelemetry-endpoint.json "JSON schema definition") In the Enterprise Edition you have more override options, like [override entirely the exporter you want to use](/docs/enterprise/telemetry/opentelemetry-by-endpoint/). ## Backend override of metrics and traces For instance, you have a specific backend that is adding noise to your dashboards and you’d like to disable all layers: ```json { "backend": [ { "extra_config": { "telemetry/opentelemetry": { "proxy": { "disable_metrics": true, "disable_traces": true }, "backend": { "metrics": { "disable_stage": true }, "traces": { "disable_stage": true } } } }, "url_pattern": "/noise", "host": [ "example.com" ] } ] } ``` These are the options: ##### OpenTelemetry settings per backend \* required fields ##### [`backend`](https://www.krakend.io/docs/telemetry/opentelemetry-by-endpoint/#backend "Click to copy link to backend") object Reports the activity between KrakenD and each of your backend services. This is the more granular layer. ##### [`metrics`](#backend-metrics "Click to copy link to metrics") object ##### [`detailed_connection`](#metrics-detailed_connection "Click to copy link to detailed_connection") boolean Whether you want to enable detailed metrics for the HTTP connection phase or not. Includes times to connect, DNS querying, and the TLS handshake. Defaults to `false` ##### [`disable_stage`](#metrics-disable_stage "Click to copy link to disable_stage") boolean Whether to turn off the metrics or not. Setting this to `true` means stop reporting any data. Defaults to `false` ##### [`read_payload`](#metrics-read_payload "Click to copy link to read_payload") boolean Whether you want to enable metrics for the response reading payload or not (HTTP connection not taken into account). Defaults to `false` ##### [`round_trip`](#metrics-round_trip "Click to copy link to round_trip") boolean Whether you want to enable metrics for the actual HTTP request for the backend or not (manipulation not taken into account). This is the time your backend needs to produce a result. Defaults to `false` ##### [`static_attributes`](#metrics-static_attributes "Click to copy link to static_attributes") array of objects Show properties a list of tags or labels you want to associate with these metrics. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) Example: `[{"key":"my_metric_attr","value":"my_metric_val"}]` Each item of static\_attributes accepts the following properties: ##### [`key`](#static_attributes-key "Click to copy link to key") * string the key, tag, or label name you want to use. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) ##### [`value`](#static_attributes-value "Click to copy link to value") * string the static value you want to assign to this key. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) ##### [`traces`](#backend-traces "Click to copy link to traces") object ##### [`detailed_connection`](#traces-detailed_connection "Click to copy link to detailed_connection") boolean Whether you want to add detailed trace attributes for the HTTP connection phase or not. Includes times to connect, DNS querying, and the TLS handshake. Defaults to `false` ##### [`disable_stage`](#traces-disable_stage "Click to copy link to disable_stage") boolean Whether to turn off the traces or not. Setting this to `true` means stop reporting any data. Defaults to `false` ##### [`read_payload`](#traces-read_payload "Click to copy link to read_payload") boolean Whether you want to add trace attributes for the response reading payload or not (HTTP connection not taken into account). Defaults to `false` ##### [`report_headers`](#traces-report_headers "Click to copy link to report_headers") boolean Whether you want to report the final headers that reached the backend. Defaults to `false` ##### [`round_trip`](#traces-round_trip "Click to copy link to round_trip") boolean Whether you want to add trace attributes for the actual HTTP request for the backend or not (manipulation not taken into account). This is the time your backend needs to produce a result. Defaults to `false` ##### [`skip_headers`](#traces-skip_headers "Click to copy link to skip_headers") array of strings A list of headers you want to skip when reporting the headers that reached the backend. This is useful to avoid reporting sensitive data. Example: `["X-Backend-Secret","X-All-Secret"]` ##### [`static_attributes`](#traces-static_attributes "Click to copy link to static_attributes") array of objects Show properties a list of tags or labels you want to associate with these metrics. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) Example: `[{"key":"my_metric_attr","value":"my_metric_val"}]` Each item of static\_attributes accepts the following properties: ##### [`key`](#static_attributes-key "Click to copy link to key") * string the key, tag, or label name you want to use. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) ##### [`value`](#static_attributes-value "Click to copy link to value") * string the static value you want to assign to this key. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) Schema: [https://www.krakend.io/schema/v2.13/telemetry/opentelemetry-backend.json](https://www.krakend.io/schema/v2.13/telemetry/opentelemetry-backend.json "JSON schema definition") --- # [Granular OpenTelemetry by endpoint (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/telemetry/opentelemetry-by-endpoint/) The OpenTelemetry configuration is declared [at the service level](/docs/enterprise/telemetry/opentelemetry/), but in the Enterprise edition you have more granularity and you can override metrics and traces per endpoint and per backend as follows. ## Endpoint Override of metrics and traces The following example overrides some properties that could be declared at the service level, and changes the exporters defined there to use a different set. While the `proxy` section can be defined in the Community edition, the `exporters_override` and `backend` sections are Enterprise only. Only a small set of settings are displayed in this example: ```json { "endpoints": [ { "endpoint": "/example", "backend": [ { "host": [ "example.com" ], "url_pattern": "/example" } ], "extra_config": { "telemetry/opentelemetry": { "global": { "traces_static_attributes": [ { "key": "owner", "value": "team-charlie" } ], "metrics_static_attributes": [ { "key": "owner", "value": "team-charlie" } ] }, "proxy": { "disable_metrics": false, "disable_traces": false }, "backend": { "metrics": { "read_payload": false, "static_attributes": [ { "key": "my_metric_attr", "value": "my_metric_val" } ] }, "exporters_override": { "metric_reporting_period": 10, "trace_sample_rate": 1, "metric_exporters": [ "local_prometheus" ], "trace_exporters": [ "debug_jaeger", "newrelic", "local_tempo" ] } } } } } ] } ``` Exporter names must be typed exactly as in the service level. The full set of options are: ##### Options to override OpenTelemetry settings at the endpoint level \* required fields ##### [`backend`](https://www.krakend.io/docs/enterprise/telemetry/opentelemetry-by-endpoint/#backend "Click to copy link to backend") object **Enterprise only.** Reports the activity between KrakenD and each of your backend services. This is the more granular layer. ##### [`metrics`](#backend-metrics "Click to copy link to metrics") object ##### [`detailed_connection`](#metrics-detailed_connection "Click to copy link to detailed_connection") boolean Whether you want to enable detailed metrics for the HTTP connection phase or not. Includes times to connect, DNS querying, and the TLS handshake. Defaults to `false` ##### [`disable_stage`](#metrics-disable_stage "Click to copy link to disable_stage") boolean Whether to turn off the metrics or not. Setting this to `true` means stop reporting any data. Defaults to `false` ##### [`read_payload`](#metrics-read_payload "Click to copy link to read_payload") boolean Whether you want to enable metrics for the response reading payload or not (HTTP connection not taken into account). Defaults to `false` ##### [`round_trip`](#metrics-round_trip "Click to copy link to round_trip") boolean Whether you want to enable metrics for the actual HTTP request for the backend or not (manipulation not taken into account). This is the time your backend needs to produce a result. Defaults to `false` ##### [`static_attributes`](#metrics-static_attributes "Click to copy link to static_attributes") array of objects Show properties A list of tags or labels you want to associate with these metrics. Example: `[{"key":"my_metric_attr","value":"my_metric_val"}]` Each item of static\_attributes accepts the following properties: ##### [`key`](#static_attributes-key "Click to copy link to key") * string The key, tag, or label name you want to use. ##### [`value`](#static_attributes-value "Click to copy link to value") * string The static value you want to assign to this key. ##### [`traces`](#backend-traces "Click to copy link to traces") object ##### [`detailed_connection`](#traces-detailed_connection "Click to copy link to detailed_connection") boolean Whether you want to add detailed trace attributes for the HTTP connection phase or not. Includes times to connect, DNS querying, and the TLS handshake. Defaults to `false` ##### [`disable_stage`](#traces-disable_stage "Click to copy link to disable_stage") boolean Whether to turn off the traces or not. Setting this to `true` means stop reporting any data. Defaults to `false` ##### [`read_payload`](#traces-read_payload "Click to copy link to read_payload") boolean Whether you want to add trace attributes for the response reading payload or not (HTTP connection not taken into account). Defaults to `false` ##### [`report_headers`](#traces-report_headers "Click to copy link to report_headers") boolean Whether you want to report the final headers that reached the backend. Defaults to `false` ##### [`round_trip`](#traces-round_trip "Click to copy link to round_trip") boolean Whether you want to add trace attributes for the actual HTTP request for the backend or not (manipulation not taken into account). This is the time your backend needs to produce a result. Defaults to `false` ##### [`skip_headers`](#traces-skip_headers "Click to copy link to skip_headers") array of strings A list of headers you want to skip when reporting the headers that reached the backend. This is useful to avoid reporting sensitive data. Example: `["X-Backend-Secret","X-All-Secret"]` ##### [`static_attributes`](#traces-static_attributes "Click to copy link to static_attributes") array of objects Show properties A list of tags or labels you want to associate to these traces. Example: `[{"key":"my_trace_attr","value":"my_trace_val"}]` Each item of static\_attributes accepts the following properties: ##### [`key`](#static_attributes-key "Click to copy link to key") * string The key, tag, or label name you want to use. ##### [`value`](#static_attributes-value "Click to copy link to value") * string The static value you want to assign to this key. ##### [`exporters_override`](https://www.krakend.io/docs/enterprise/telemetry/opentelemetry-by-endpoint/#exporters_override "Click to copy link to exporters_override") object **Enterprise only.** Override exporter configuration for this endpoint ##### [`metric_exporters`](#exporters_override-metric_exporters "Click to copy link to metric_exporters") array of strings Overrides the metrics exporters used in this endpoint Example: `["local_prometheus"]` ##### [`metric_reporting_period`](#exporters_override-metric_reporting_period "Click to copy link to metric_reporting_period") integer Override how often you want to report and flush the metrics in seconds. ##### [`trace_exporters`](#exporters_override-trace_exporters "Click to copy link to trace_exporters") array of strings Overrides the trace exporters used in this endpoint Example: `["debug_jaeger","newrelic","local_tempo"]` ##### [`trace_sample_rate`](#exporters_override-trace_sample_rate "Click to copy link to trace_sample_rate") number Overrides the sample rate for traces defines the percentage of reported traces. This option is key to reduce the amount of data generated (and resource usage), while you still can debug and troubleshoot issues. For instance, a number of `0.25` will report a 25% of the traces seen in the system. Example: `0.25` ##### [`global`](https://www.krakend.io/docs/enterprise/telemetry/opentelemetry-by-endpoint/#global "Click to copy link to global") Overrides the global configuration for this endpoint. ##### [`metrics_static_attributes`](#global-metrics_static_attributes "Click to copy link to metrics_static_attributes") array Static attributes you want to pass for metrics. Overrides the `metrics_static_attributes` defined at the service level. Each item of metrics\_static\_attributes accepts the following properties: ##### [`key`](#metrics_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#metrics_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send ##### [`traces_static_attributes`](#global-traces_static_attributes "Click to copy link to traces_static_attributes") array Static attributes you want to pass for traces. Overrides the `traces_static_attributes` defined at the service level. Each item of traces\_static\_attributes accepts the following properties: ##### [`key`](#traces_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#traces_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send ##### [`proxy`](https://www.krakend.io/docs/enterprise/telemetry/opentelemetry-by-endpoint/#proxy "Click to copy link to proxy") object Reports the activity at the beginning of the proxy layer, including spawning the required requests to multiple backends, merging, endpoint transformation and any other internals of the proxy between the request processing and the backend communication ##### [`disable_metrics`](#proxy-disable_metrics "Click to copy link to disable_metrics") boolean Whether you want to disable all metrics in this endpoint or not. Defaults to `false` ##### [`disable_traces`](#proxy-disable_traces "Click to copy link to disable_traces") boolean Whether you want to disable all traces in this endpoint or not. Defaults to `false` ##### [`metrics_static_attributes`](#proxy-metrics_static_attributes "Click to copy link to metrics_static_attributes") array Static attributes you want to pass for metrics. Each item of metrics\_static\_attributes accepts the following properties: ##### [`key`](#metrics_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#metrics_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send ##### [`report_headers`](#proxy-report_headers "Click to copy link to report_headers") boolean Whether you want to report all headers that passed from the request to the proxy layer (`input_headers` policy in the endpoint plus KrakenD’s headers). Defaults to `false` ##### [`skip_headers`](#proxy-skip_headers "Click to copy link to skip_headers") array of strings A list of headers you want to skip when reporting headers passed to the proxy layer. This is useful to avoid reporting sensitive data. Example: `["X-Sensitive-Data","X-Signature"]` ##### [`traces_static_attributes`](#proxy-traces_static_attributes "Click to copy link to traces_static_attributes") array Static attributes you want to pass for traces. Each item of traces\_static\_attributes accepts the following properties: ##### [`key`](#traces_static_attributes-key "Click to copy link to key") string The key of the static attribute you want to send ##### [`value`](#traces_static_attributes-value "Click to copy link to value") string The value of the static attribute you want to send Schema: [https://www.krakend.io/schema/v2.13/telemetry/opentelemetry-endpoint.json](https://www.krakend.io/schema/v2.13/telemetry/opentelemetry-endpoint.json "JSON schema definition") ## Backend Override of metrics and traces For instance, you have a specific backend that is adding noise to your dashboards and you’d like to disable all layers: ```json { "backend": [ { "extra_config": { "telemetry/opentelemetry": { "proxy": { "disable_metrics": true, "disable_traces": true }, "backend": { "metrics": { "disable_stage": true }, "traces": { "disable_stage": true } } } }, "url_pattern": "/noise", "host": [ "example.com" ] } ] } ``` These are the options: ##### Options to override OpenTelemetry settings at the backend level \* required fields ##### [`backend`](https://www.krakend.io/docs/enterprise/telemetry/opentelemetry-by-endpoint/#backend "Click to copy link to backend") object Reports the activity between KrakenD and each of your backend services. This is the more granular layer. ##### [`metrics`](#backend-metrics "Click to copy link to metrics") object ##### [`detailed_connection`](#metrics-detailed_connection "Click to copy link to detailed_connection") boolean Whether you want to enable detailed metrics for the HTTP connection phase or not. Includes times to connect, DNS querying, and the TLS handshake. Defaults to `false` ##### [`disable_stage`](#metrics-disable_stage "Click to copy link to disable_stage") boolean Whether to turn off the metrics or not. Setting this to `true` means stop reporting any data. Defaults to `false` ##### [`read_payload`](#metrics-read_payload "Click to copy link to read_payload") boolean Whether you want to enable metrics for the response reading payload or not (HTTP connection not taken into account). Defaults to `false` ##### [`round_trip`](#metrics-round_trip "Click to copy link to round_trip") boolean Whether you want to enable metrics for the actual HTTP request for the backend or not (manipulation not taken into account). This is the time your backend needs to produce a result. Defaults to `false` ##### [`static_attributes`](#metrics-static_attributes "Click to copy link to static_attributes") array of objects Show properties a list of tags or labels you want to associate with these metrics. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) Example: `[{"key":"my_metric_attr","value":"my_metric_val"}]` Each item of static\_attributes accepts the following properties: ##### [`key`](#static_attributes-key "Click to copy link to key") * string the key, tag, or label name you want to use. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) ##### [`value`](#static_attributes-value "Click to copy link to value") * string the static value you want to assign to this key. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) ##### [`traces`](#backend-traces "Click to copy link to traces") object ##### [`detailed_connection`](#traces-detailed_connection "Click to copy link to detailed_connection") boolean Whether you want to add detailed trace attributes for the HTTP connection phase or not. Includes times to connect, DNS querying, and the TLS handshake. Defaults to `false` ##### [`disable_stage`](#traces-disable_stage "Click to copy link to disable_stage") boolean Whether to turn off the traces or not. Setting this to `true` means stop reporting any data. Defaults to `false` ##### [`read_payload`](#traces-read_payload "Click to copy link to read_payload") boolean Whether you want to add trace attributes for the response reading payload or not (HTTP connection not taken into account). Defaults to `false` ##### [`report_headers`](#traces-report_headers "Click to copy link to report_headers") boolean Whether you want to report the final headers that reached the backend. Defaults to `false` ##### [`round_trip`](#traces-round_trip "Click to copy link to round_trip") boolean Whether you want to add trace attributes for the actual HTTP request for the backend or not (manipulation not taken into account). This is the time your backend needs to produce a result. Defaults to `false` ##### [`skip_headers`](#traces-skip_headers "Click to copy link to skip_headers") array of strings A list of headers you want to skip when reporting the headers that reached the backend. This is useful to avoid reporting sensitive data. Example: `["X-Backend-Secret","X-All-Secret"]` ##### [`static_attributes`](#traces-static_attributes "Click to copy link to static_attributes") array of objects Show properties a list of tags or labels you want to associate with these metrics. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) Example: `[{"key":"my_metric_attr","value":"my_metric_val"}]` Each item of static\_attributes accepts the following properties: ##### [`key`](#static_attributes-key "Click to copy link to key") * string the key, tag, or label name you want to use. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) ##### [`value`](#static_attributes-value "Click to copy link to value") * string the static value you want to assign to this key. see: [https://www.krakend.io/docs/telemetry/opentelemetry/](https://www.krakend.io/docs/telemetry/opentelemetry/) Schema: [https://www.krakend.io/schema/v2.13/telemetry/opentelemetry-backend.json](https://www.krakend.io/schema/v2.13/telemetry/opentelemetry-backend.json "JSON schema definition") --- # [Authenticated metrics to SaaS providers using OTEL (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/telemetry/opentelemetry-security/) The OpenTelemetry Security component allows your KrakenD nodes to push data to a remote collector over the Internet that needs authentication, usually adding a token header. It allows you to run KrakenD without installing a collector in your infrastructure and push data directly to a SaaS provider, saving time by simplifying the infrastructure needed to monitor the activity. It combines with the `telemetry/opentelemetry` configuration and separates the authentication data into a new namespace: `telemetry/opentelemetry-security`. ![Push metrics to SaaS](/images/documentation/diagrams/opentelemetry-otlp-auth.mmd.svg) To add authentication, you need a configuration like this: ```json { "version": 3, "telemetry/opentelemetry": { "exporters": { "otlp": [ { "name": "newrelic", "host": "https://otlp.eu01.nr-data.net", "port": 4317, "use_http": true } ] } }, "telemetry/opentelemetry-security": { "otlp": [ { "name": "newrelic", "headers": { "api-key": "YOUR_NEWRELIC_LICENSE_KEY" } } ] } } ``` In the example above, the `telemetry/opentelemetry` is simplified and does not contain all required fields, but notice the following: - Both components use the same exporter `name`, which is a must. - The flag `use_http` is set to `true` to send all data through HTTPS instead of gRPC, also a must. - The example header is `api-key`, the header name New Relic expects, but each SaaS provider uses a different one. - The value of the header is your license or API key as assigned by the SaaS provider Here are the fields allowed by the component: ##### Fields of OpenTelemetry Security \* required fields ##### [`otlp`](https://www.krakend.io/docs/enterprise/telemetry/opentelemetry-security/#otlp "Click to copy link to otlp") * array The list of OTLP exporters that require authentication. Set at least one object to push metrics and traces to an external collector using OTLP. Each item of otlp accepts the following properties: ##### [`headers`](#otlp-headers "Click to copy link to headers") * object The custom headers you will send to authenticate requests. Each key is the header name you will add to all outgoing reports. ##### [`.*`](#headers-.%2a "Click to copy link to .*") string The value of the header, usually an API token. ##### [`name`](#otlp-name "Click to copy link to name") * string The **exact name** you used to define the exporter under `telemetry/opentelemetry`. Examples: `"newrelic"` , `"remote_datadog"` Schema: [https://www.krakend.io/schema/v2.13/telemetry/opentelemetry-security.json](https://www.krakend.io/schema/v2.13/telemetry/opentelemetry-security.json "JSON schema definition") When the credentials do not work, you’ll see failure messages in the log when pushing the metrics. These usually look like this: ```log ▶ ERROR [SERVICE: OpenTelemetry] failed to upload metrics: failed to send metrics to https://otlp.eu01.nr-data.net:4318/v1/metrics: 403 Forbidden ``` --- # [Prometheus' metrics endpoint](https://www.krakend.io/docs/telemetry/prometheus/) [Prometheus](https://prometheus.io/) is an open-source system monitoring and alerting toolkit that you can use to scrape a `/metrics` endpoint on KrakenD in the selected port. For instance, you could have an endpoint like `http://localhost:9091/metrics`. When using Prometheus with OpenTelemetry, you can use a [ready-to-use Grafana dashboard](/docs/telemetry/grafana/) to visualize metrics, as shown in the image above. The mechanics are simple: you add the `telemetry/opentelemetry` integration with a `prometheus` exporter, and then you add a Prometheus job to scrape from your KrakenD instances the metrics. ![Prometheus scrapping from KrakenD image](/images/documentation/diagrams/opentelemetry-prometheus.mmd.svg) ## Prometheus Configuration To enable scrapeable Prometheus metrics on Krakend, add the [OpenTelemetry integration](/docs/telemetry/opentelemetry/) with a `prometheus` exporter. The following configuration is an example of how to do it: ```json { "version": 3, "extra_config": { "telemetry/opentelemetry": { "service_name": "krakend_prometheus_service", "metric_reporting_period": 1, "exporters": { "prometheus": [ { "name": "local_prometheus", "port": 9090, "process_metrics": true, "go_metrics": true } ] } } } } ``` The full list of the Prometheus exporter settings are as follows: ##### Fields of `"telemetry/opentelemetry": { "exporters":{} }` \* required fields ##### [`prometheus`](https://www.krakend.io/docs/telemetry/prometheus/#prometheus "Click to copy link to prometheus") array Set here at least the settings for one Prometheus exporter. Each exporter will start a local port that offers metrics to be pulled from KrakenD. Each item of prometheus accepts the following properties: ##### [`custom_metric_reporting_period`](#prometheus-custom_metric_reporting_period "Click to copy link to custom_metric_reporting_period") integer Whether you want to override the global `metric_reporting_period` attribute set for all exporters or not. Value in seconds. A missing attribute, or set to `0` means using whatever value was used in `metric_reporting_period` at the global level. Defaults to `0` ##### [`disable_metrics`](#prometheus-disable_metrics "Click to copy link to disable_metrics") boolean Leave this exporter declared but disabled (useful in development). It won’t report any metrics when the flag is `true`. Defaults to `false` ##### [`go_metrics`](#prometheus-go_metrics "Click to copy link to go_metrics") boolean Whether you want fine-grained details of Go language metrics or not. ##### [`listen_ip`](#prometheus-listen_ip "Click to copy link to listen_ip") string The IP address that KrakenD listens to in IPv4 or IPv6. You can, for instance, expose the Prometheus metrics only in a private IP address. An empty string, or no declaration means listening on all interfaces. The inclusion of `::` is intended for IPv6 format only (**this is not the port**). Examples of valid addresses are `192.0.2.1` (IPv4), `2001:db8::68` (IPv6). The values `::` and `0.0.0.0` listen to all addresses, which are valid for IPv4 and IPv6 simultaneously. Examples: `"172.12.1.1"` , `"::1"` Defaults to `"0.0.0.0"` ##### [`name`](#prometheus-name "Click to copy link to name") * string A unique name to identify this exporter. Examples: `"local_prometheus"` , `"remote_grafana"` ##### [`port`](#prometheus-port "Click to copy link to port") integer The port in KrakenD where Prometheus will connect to. Defaults to `9090` ##### [`process_metrics`](#prometheus-process_metrics "Click to copy link to process_metrics") boolean Whether this exporter shows detailed metrics about the running process like CPU or memory usage or not. Schema: [https://www.krakend.io/schema/v2.13/telemetry/opentelemetry.json](https://www.krakend.io/schema/v2.13/telemetry/opentelemetry.json "JSON schema definition") In addition, you can do a **granular configuration** of the metrics you want to expose using the `layers` attribute and other [OpenTelemetry options](/docs/telemetry/opentelemetry/#layers). ### Demonstration setup The following configuration allows you to test a complete metrics experience, from generation and collection to visualization. The first code snippet is a `docker-compose.yaml` that declares three different services: - The `krakend` service exposing port 8080 - The `prometheus` service that will scrape the metrics from KrakenD - A `grafana` dashboard to display them (it uses our [Grafana dashboard](/docs/telemetry/grafana/)) Notice that the three services declare volumes to pick the configuration. ```yaml version: "3" services: krakend: image: "krakend:2.13.8" ports: - "8080:8080" volumes: - "./krakend:/etc/krakend/" prometheus: image: prom/prometheus:latest ports: - "9090:9090" volumes: - "./prometheus.yml:/etc/prometheus/prometheus.yml" grafana: image: grafana/grafana:latest ports: - "3000:3000" environment: GF_SECURITY_ADMIN_USER: krakend GF_SECURITY_ADMIN_PASSWORD: krakend GF_AUT_ANONYMOUS_ENABLED: "true" volumes: - "./conf/provisioning/datasources:/etc/grafana/provisioning/datasources" - "./conf/provisioning/dashboards:/etc/grafana/provisioning/dashboards" - "./conf/data/dashboards:/var/lib/grafana/dashboards" ``` The following YAML configuration is a simple example of pulling data from the `/metrics` endpoint in KrakenD integration from three different instances: Make sure ports are accessible To let the scrapper access the metrics endpoint, make sure that the path and the port are the ones you configured, that the listen address allows you to access the data, and that if you use containers, the port is exposed in KrakenD. Also, remember that you cannot use `localhost` as a target because the Prometheus container does not run inside the KrakenD container; use the service name instead. ```yaml global: scrape_interval: 15s rule_files: # - "first.rules" # - "second.rules" scrape_configs: - job_name: krakend_otel scrape_interval: 5s metrics_path: '/metrics' static_configs: - targets: - 'krakend1:9091' - 'krakend2:9091' - 'krakend3:9091' labels: app: kotel_example ``` ## Visualizing metrics in a dashboard When the Prometheus configuration is added into KrakenD, and your Prometheus is scrapping it, you can visualize the data using our [Grafana dashboard](/docs/telemetry/grafana/) or make your own. Which layers do you need? Our Grafana dashboard contains a lot of options, and **not all are enabled by default**. Because generating low-detail metrics is an expensive operation, some options in the `layers` are disabled by default. Enable the options that matter to you, knowing that the more detail you add, the more resources the gateway will need to run. ![Screenshot of a grafana dashboard with KrakenD metrics](/images/documentation/screenshots/grafana-prometheus-otel.png) ## Migrating from an old OpenCensus configuration (legacy) Prior to KrakenD v2.6, you had to configure the Prometheus endpoint using the opencensus component. The OpenTelemetry integration is much more powerful and delivers more data while simultaneously giving you more configuration options. If you had an OpenCensus configuration with a `prometheus` exporter like the following: ```json { "version": 3, "extra_config": { "telemetry/opencensus": { "sample_rate": 100, "reporting_period": 0, "exporters": { "prometheus": { "port": 9091, "namespace": "krakend", "tag_host": false, "tag_path": true, "tag_method": true, "tag_statuscode": false } } } } } ``` Then you should make the following changes to upgrade: - `telemetry/opencensus` -> Rename to `telemetry/opentelemetry` - `sample_rate` -> Delete this field - `reporting_period` -> Rename to `metric_reporting_period` - `prometheus: {...}` -> Add an array surrounding the object, so it becomes `prometheus: [{...}]` - `namespace` -> Rename to `name` - `tag_host`, `tag_path`,`tag_method`,`tag_statuscode` -> Delete them From here, add any of the additional properties you can add. --- # [Telemetry and Monitoring with Grafana](https://www.krakend.io/docs/telemetry/grafana/) The preconfigured Grafana dashboard for KrakenD offers valuable information to understand the performance of your services and detect anomalies in the service. The dashboard is extensive and offers you metrics like: - Requests from users to KrakenD - Requests from KrakenD to your backends - Response times - Memory usage and details - Endpoints and status codes - Latencies - Heatmaps - Open connections - Throughput - Distributions, timers, garbage collection and a long etcetera The following video makes a quick tour of the information you can find on our Grafana Dashboard using Prometheus as data source: ## Importing a Grafana dashboard The Grafana dashboard has evolved over time, and these are the different versions you can use depending on your KrakenD version and data sources: Datasource Description Grafana Cloud ID Source Prometheus (**recommended**) Latest dashboard to display metrics when you add an [OpenTelemetry Prometheus exporter](/docs/telemetry/prometheus/). [`20651`](https://grafana.com/grafana/dashboards/20651) [for-prometheus.json](https://github.com/krakend/telemetry-dashboards/blob/main/grafana/krakend/for-prometheus.json) Prometheus (Websockets) Latest dashboard to display WebSocket metrics [OpenTelemetry Prometheus exporter](/docs/telemetry/prometheus/). [`22518`](https://grafana.com/grafana/dashboards/22518) [for-prometheus-ws.json](https://github.com/krakend/telemetry-dashboards/blob/main/grafana/krakend/for-prometheus-websockets.json) Prometheus (Redis) Latest dashboard to display [Redis connection pools](/docs/enterprise/service-settings/redis-connection-pools/). [`22517`](https://grafana.com/grafana/dashboards/22517) [for-prometheus-redis.json](https://github.com/krakend/telemetry-dashboards/blob/main/grafana/krakend/for-prometheus-redis.json) Prometheus (AI LLM) Latest dashboard to display [AI LLM tokens usage](/docs/enterprise/ai-gateway/). [`24868`](https://grafana.com/grafana/dashboards/24868) [for-prometheus-ai-llm.json](https://github.com/krakend/telemetry-dashboards/blob/main/grafana/krakend/for-prometheus-ai-llm.json) InfluxDB v2.x (*legacy*) Legacy dashboard for InfluxDB v2 and KrakenD under v2.6. Uses Flux queries [`17074`](https://grafana.com/grafana/dashboards/17074) [for-influxdb-v2.json](https://github.com/krakend/telemetry-dashboards/blob/main/grafana/krakend/for-influxdb-v2.json) InfluxDB v1.x (*legacy*) Legacy dashboard for InfluxDB v1 and KrakenD under v2.6. Uses InfluxQL queries [`15029`](https://grafana.com/grafana/dashboards/15029) [for-influxdb-v1.json](https://github.com/krakend/telemetry-dashboards/blob/main/grafana/krakend/for-influxdb-v1.json) You can get all the dashboards on: - [Grafana Cloud](https://grafana.com/orgs/krakendio/dashboards) - [Github repository](https://github.com/krakend/telemetry-dashboards) To import them, there are several options, being the most common: - From your Grafana UI (hosted or cloud), click the `+` icon in the side menu, and then click *Import*. Choose import via Grafana.com and use the IDs above. - From the same UI, import the JSON source files instead - Copy or mount in your Grafana container the dashboards when starting ([Volume content here](https://github.com/krakend/telemetry-dashboards)): ```yml volumes: - "./grafana/datasources/all.yml:/etc/grafana/provisioning/datasources/all.yml" - "./grafana/dashboards/all.yml:/etc/grafana/provisioning/dashboards/all.yml" - "./grafana/krakend:/var/lib/grafana/dashboards/krakend" ``` You can see an example integrated on [KrakenD Playground](https://github.com/krakend/playground-community)’s Docker compose file. ## Getting the metrics on Grafana Grafana does not require any specific configuration on KrakenD, but it feeds from a data source, so you will need to push data to one of the following: 1. [Prometheus exporter](/docs/telemetry/prometheus/) (**recommended**) 2. [Legacy integration for InfluxDb](/docs/telemetry/extended-metrics/), for older versions of KrakenD ## What’s in the dashboard This new dashboard allows you to load data from a Prometheus data source that scraps data from your KrakenD machines using OpenTelemetry. The dashboards present a lot of information, allowing you to detect anomalies quickly. The information is separated into different sections. All sections can be filtered by different criteria, such as the application name, the reporting servers, and the endpoint names. You can also set the intervals of data and the number of items you want to get on lists. The overview section shows the gateway’s activity. You can see the number of requests, the throughput, load times, and memory consumption. The latency percentiles will reveal worrying response times, which you can drill down in the following sections. Knowing how much data the gateway is moving is also essential for networking. There is low-level detail splitting the proxy and backend phases. In the proxy phase, you can see the endpoint times, including data aggregation from multiple upstream services, and in the backend graphs, you see data happening when KrakenD connects to your services. The global section contains everything that KrakenD saw in and out. It includes the total timings for a request hitting the service until it is delivered to the client. It contains the throughput, latencies, data size, fastest and slowest endpoints, status codes, and heat maps. In the proxy section, you see data after processing the HTTP request and comprehend the internal work of dealing with the endpoint, including spawning multiple requests to the backends, aggregating their results, and manipulating the final response, and you get similar data. In the Backends section, you can see the activity of single backend requests and responses. It mainly contains the times between KrakenD and your service. It is the richest layer of all. The application section shows the internals and the Garbage Collector’s operations and performance. Goroutines are an essential metric in terms of application. Finally, there is a small section for tracing. You will need a Grafana Tempo data source to view it. We hope you enjoy our new Grafana dashboard for OpenTelemetry. Do not hesitate to share your thoughts with us! --- # [InfluxDB Telemetry Integration](https://www.krakend.io/docs/telemetry/influxdb/) [InfluxDB](https://www.influxdata.com/) is a time series database designed to handle high write and query loads and allows you to store and visualize metrics data. Influx is offered as an open-source solution you can host but also as a cloud service. Native influx component Before KrakenD v2.6, the only option to push data to Influx was configuring the native exporter `telemetry/influx`. Although the component is development-frozen in favor of OTEL, you can still use it as described in the [Metrics API](/docs/telemetry/extended-metrics/). KrakenD instruments your API automatically code and generates telemetry data that is pushed using the **OpenTelemetry Protocol** (OTLP) via the [OpenTelemetry integration](/docs/telemetry/opentelemetry/). The data can travel through gRPC or HTTP and uses the standard OTLP format an [OTEL Collector](https://opentelemetry.io/docs/collector/) expects. The OpenTelemetry integration populates metrics data to an Influx database when you add the `telemetry/opentelemetry` namespace to the configuration with an `otlp` exporter. There is no specific setting for InfluxDB because OTEL is a standard shared with many other technologies. The flow for the Influx population using OTLP is as follows: ![KrakenD OTLP communication with a Collector that sends to Influx](/images/documentation/diagrams/opentelemetry-otlp-influx.mmd.svg) You can configure [OpenTelemetry](/docs/telemetry/opentelemetry/) with an `otlp` exporter to push data to InfluxDB. --- # [OpenCensus Telemetry Integration](https://www.krakend.io/docs/telemetry/opencensus/) OpenCensus is deprecated KrakenD has traditionally offered its telemetry integration through this **OpenCensus** component, which has provided reliable service for over six years, but now has transitioned to the more modern and robust [OpenTelemetry](/docs/telemetry/opentelemetry/) framework. As a result of a change in the industry, the OpenCensus integration is deprecated, and all efforts are focused on [OpenTelemetry](/docs/telemetry/opentelemetry/). Move away from this component. The Opencensus exporter is a single component that allows you to **export data to multiple providers**, both open source and privative. You will be interested in Opencensus when you want to see data in one of its supported `exporters`. For instance, you might want to send metrics to Prometheus. That would be as easy as adding this snippet in the **root level** of your `krakend.json` file: ```json { "version": 3, "extra_config": { "telemetry/opencensus": { "exporters": { "prometheus": { "port": 9091, "namespace": "krakend" } } } } } ``` ## Configuration The Opencensus needs at least an exporter to work, although multiple exporters can be added in the same configuration. Notice that adding several exporters that push metrics out to multiple systems will affect the performance of the server. Every exporter has its own configuration and it’s described in its section. By default, all exporters use a `sample_rate=0`, meaning that **they won’t report anything**, but this can be changed by specifying the configuration another percentage, like 100% of the activity: ```json { "version": 3, "extra_config": { "telemetry/opencensus": { "sample_rate": 100, "reporting_period": 0, "enabled_layers": { "backend": true, "router": true, "pipe": true }, "exporters": { "prometheus": { "port": 9091 } } } } } ``` ##### Fields of Telemetry via Opencensus \* required fields ##### [`enabled_layers`](https://www.krakend.io/docs/telemetry/opencensus/#enabled_layers "Click to copy link to enabled_layers") Lets you specify what data you want to export. All layers are enabled by default unless you declare this section. ##### [`backend`](#enabled_layers-backend "Click to copy link to backend") boolean Reports the activity between KrakenD and your services Defaults to `false` ##### [`pipe`](#enabled_layers-pipe "Click to copy link to pipe") boolean Reports the activity at the beginning of the proxy layer. It gives a more detailed view of the internals of the pipe between end-users and KrakenD, having into account merging of different backends. Defaults to `false` ##### [`router`](#enabled_layers-router "Click to copy link to router") boolean Reports the activity between end-users and KrakenD Defaults to `false` ##### [`exporters`](https://www.krakend.io/docs/telemetry/opencensus/#exporters "Click to copy link to exporters") * object The exporter(s) you would like to enable. See each exporter configuration in its own section. ##### [`reporting_period`](https://www.krakend.io/docs/telemetry/opencensus/#reporting_period "Click to copy link to reporting_period") integer The number of seconds passing between reports. If duration is less than or equal to zero, it enables the default behavior of each exporter. Defaults to `0` ##### [`sample_rate`](https://www.krakend.io/docs/telemetry/opencensus/#sample_rate "Click to copy link to sample_rate") integer A number between 0 (no requests at all) and 100 (all requests) representing the percentage of sampled requests you want to send to the exporter. **Sampling the 100% of the requests is generally discouraged** when the relationship between traffic and dedicated resources is sparse. Defaults to `0` Schema: [https://www.krakend.io/schema/v2.13/telemetry/opencensus.json](https://www.krakend.io/schema/v2.13/telemetry/opencensus.json "JSON schema definition") ## Transition from OpenCensus If you have been using telemetry on KrakenD for the past six years (before KrakenD 2.6), you were using the [OpenCensus](/docs/telemetry/opencensus/) exporters, which have worked like a Swiss clock. While OpenCensus has been working very well, it has merged with OpenTracing to form [OpenTelemetry](https://opentelemetry.io/), which serves as the next major version of OpenCensus and OpenTracing. While our OpenCensus integration will keep functioning on KrakenD for now, it won’t receive additional updates (neither security fixes), so we recommend replacing it with OpenTelemetry. --- # [Datadog Telemetry Integration](https://www.krakend.io/docs/telemetry/datadog/) [Datadog](https://www.datadoghq.com/) is a cloud monitoring and security platform for developers, IT operations teams, and businesses. The [OpenTelemetry integration](/docs/telemetry/opentelemetry/) allows you to send **metrics and traces** to Datadog using their collector. ## Datadog configuration Datadog uses the standard OTLP exporter, here is a configuration example: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "host": [ "http://localhost:8080" ], "debug_endpoint": true, "echo_endpoint": true, "extra_config": { "telemetry/opentelemetry": { "exporters": { "otlp": [ { "use_http": false, "port": 4317, "host": "ddagent", "name": "my_dd_exporter", "disable_metrics": false, "disable_traces": false } ] }, "trace_sample_rate": 1, "service_name": "krakend_dd_telemetry", "metric_reporting_period": 1 } } } ``` The important part of the configuration is the `otlp` exporter, which accepts the following fields: ##### Fields of `"telemetry/opentelemetry": { "exporters":{} }` \* required fields ##### [`otlp`](https://www.krakend.io/docs/telemetry/datadog/#otlp "Click to copy link to otlp") array The list of OTLP exporters you want to use. Set at least one object to push metrics and traces to an external collector using OTLP. Each item of otlp accepts the following properties: ##### [`custom_metric_reporting_period`](#otlp-custom_metric_reporting_period "Click to copy link to custom_metric_reporting_period") integer Whether you want to override the global `metric_reporting_period` attribute set for all exporters or not. Value in seconds. A missing attribute, or set to `0` means using whatever value was used in `metric_reporting_period` at the global level. Defaults to `0` ##### [`disable_metrics`](#otlp-disable_metrics "Click to copy link to disable_metrics") boolean Disable metrics in this exporter (leaving only traces if any). It won’t report any metrics when the flag is `true`. Defaults to `false` ##### [`disable_traces`](#otlp-disable_traces "Click to copy link to disable_traces") boolean Disable traces in this exporter (leaving only metrics if any). It won’t report any metrics when the flag is `true`. Defaults to `false` ##### [`host`](#otlp-host "Click to copy link to host") * string The host where you want to push the data. It can be a sidecar or a remote collector. ##### [`name`](#otlp-name "Click to copy link to name") * string A unique name to identify this exporter. Examples: `"local_prometheus"` , `"remote_grafana"` ##### [`port`](#otlp-port "Click to copy link to port") integer A custom port to send the data. The port defaults to 4317 for gRPC unless you enable `use_http`, which defaults to 4318. Defaults to `4317` ##### [`use_http`](#otlp-use_http "Click to copy link to use_http") boolean Whether this exporter uses HTTP instead of gRPC. Schema: [https://www.krakend.io/schema/v2.13/telemetry/opentelemetry.json](https://www.krakend.io/schema/v2.13/telemetry/opentelemetry.json "JSON schema definition") In addition, you can configure how the `layers` behave ([see all options](/docs/telemetry/opentelemetry/#layers)). ## Datadog agent You must set your Datadog API key in the agent. The exporter communicates with the agent and is the agent the one reporting to Datadog. Here’s an example of how to run the Datadog agent together with KrakenD in a docker compose file: ```yml version: '3' services: krakend: image: krakend:2.13.8 volumes: - "./:/etc/krakend" command: ["run", "-c", "krakend.json"] ports: - "8080:8080" datadog: image: gcr.io/datadoghq/agent:7 pid: host environment: - DD_API_KEY=XXXXXXXXXXXXXXX - DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_GRPC_ENDPOINT=0.0.0.0:4317 - DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT=0.0.0.0:4318 - DD_SITE=datadoghq.com volumes: - /var/run/docker.sock:/var/run/docker.sock - /proc/:/host/proc/:ro - /sys/fs/cgroup:/host/sys/fs/cgroup:ro ``` Notice that we are naming the service `ddagent` in Docker compose, and this matches our `host` field in the configuration. ## Migrating from OpenCensus Prior to v2.6, telemetry sent to Datadog used the OpenCensus exporter. Enabling required adding the `datadog` exporter in the [opencensus module](/docs/telemetry/opencensus/), and the configurations looked like this: ```json { "version": 3, "extra_config": { "telemetry/opencensus": { "sample_rate": 100, "reporting_period": 0, "exporters": { "datadog": { "tags": [ "gw" ], "global_tags": { "env": "prod" }, "disable_count_per_buckets": true, "trace_address": "localhost:8126", "stats_address": "localhost:8125", "namespace": "krakend", "service": "gateway" } } } } } ``` You can migrate to OpenTelemetry doing the following changes: - Rename `telemetry/opencensus` to `telemetry/opentelemetry`. - `sample_rate` -> Delete this field - `reporting_period` -> Rename to `metric_reporting_period` - `datadog` -> Rename to `otlp`, and add an array surrounding the object, so it becomes `"otlp": [{...}]` - `namespace` -> Rename to `name` - `tag_host`, `tag_path`,`tag_method`,`tag_statuscode` -> Delete them --- # [Zipkin Telemetry Integration](https://www.krakend.io/docs/telemetry/zipkin/) [Zipkin](https://zipkin.io/) is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems in service architectures. The Opencensus exporter allows you export data to Zipkin. Enabling it only requires you to add the `zipkin` exporter in the [opencensus module](/docs/telemetry/opencensus/). The following configuration snippet sends data to your Zipkin: ```json { "version": 3, "extra_config": { "telemetry/opencensus": { "sample_rate": 100, "reporting_period": 0, "exporters": { "zipkin": { "collector_url": "http://192.168.99.100:9411/api/v2/spans", "service_name": "krakend" } } } } } ``` As with all [OpenCensus exporters](/docs/telemetry/opencensus/), you can add optional settings in the `telemetry/opencensus` level: ##### Fields of `"telemetry/opencensus"` \* required fields ##### [`enabled_layers`](https://www.krakend.io/docs/telemetry/zipkin/#enabled_layers "Click to copy link to enabled_layers") Lets you specify what data you want to export. All layers are enabled by default unless you declare this section. ##### [`backend`](#enabled_layers-backend "Click to copy link to backend") boolean Reports the activity between KrakenD and your services Defaults to `false` ##### [`pipe`](#enabled_layers-pipe "Click to copy link to pipe") boolean Reports the activity at the beginning of the proxy layer. It gives a more detailed view of the internals of the pipe between end-users and KrakenD, having into account merging of different backends. Defaults to `false` ##### [`router`](#enabled_layers-router "Click to copy link to router") boolean Reports the activity between end-users and KrakenD Defaults to `false` ##### [`reporting_period`](https://www.krakend.io/docs/telemetry/zipkin/#reporting_period "Click to copy link to reporting_period") integer The number of seconds passing between reports. If duration is less than or equal to zero, it enables the default behavior of each exporter. Defaults to `0` ##### [`sample_rate`](https://www.krakend.io/docs/telemetry/zipkin/#sample_rate "Click to copy link to sample_rate") integer A number between 0 (no requests at all) and 100 (all requests) representing the percentage of sampled requests you want to send to the exporter. **Sampling the 100% of the requests is generally discouraged** when the relationship between traffic and dedicated resources is sparse. Defaults to `0` Schema: [https://www.krakend.io/schema/v2.13/telemetry/opencensus.json](https://www.krakend.io/schema/v2.13/telemetry/opencensus.json "JSON schema definition") Then, the `exporters` key must contain an `zipkin` entry with the following properties: ##### Fields of `"telemetry/opencensus": { "exporters":{} }` \* required fields ##### [`zipkin`](https://www.krakend.io/docs/telemetry/zipkin/#zipkin "Click to copy link to zipkin") object Export telemetry data to a Zipkin collector ##### [`collector_url`](#zipkin-collector_url "Click to copy link to collector_url") * string The URL (including port and path) where your Zipkin is accepting the spans, e.g., `http://zipkin:9411/api/v2/spans` Example: `"http://192.168.99.100:9411/api/v2/spans"` ##### [`service_name`](#zipkin-service_name "Click to copy link to service_name") * string The service name registered in Zipkin. Example: `"krakend"` Schema: [https://www.krakend.io/schema/v2.13/telemetry/opencensus.json](https://www.krakend.io/schema/v2.13/telemetry/opencensus.json "JSON schema definition") --- # [Jaeger Telemetry Integration - KrakenD API Gateway](https://www.krakend.io/docs/telemetry/jaeger/) The KrakenD exporter to [Jaeger](https://www.jaegertracing.io/) allows you to submit spans to an OpenTelemetry Collector (HTTP or gRPC) automatically. Jaeger is an open-source, end-to-end distributed tracing system that allows you to monitor and troubleshoot transactions in complex distributed systems. Use Jaeger when you want to see the complete flow of a user request through KrakenD and its connected services. ## Jaeger configuration To add Jaeger, configure a new exporter to the [OpenTelemetry settings](/docs/telemetry/opentelemetry/). For instance: ```json { "version": 3, "extra_config": { "telemetry/opentelemetry": { "service_name": "my_krakend_service", "metric_reporting_period": 1, "trace_sample_rate": 0.15, "layers": { "global": { "report_headers": true }, "proxy": { "report_headers": true }, "backend": { "metrics": { "disable_stage": true }, "traces": { "disable_stage": false, "round_trip": true, "read_payload": true, "detailed_connection": true, "report_headers": true } } }, "exporters": { "otlp": [ { "name": "local_jaeger", "host": "jaeger", "port": 4317, "use_http": false, "disable_metrics": true } ] } } } } ``` The fields relative to the exporter are: ##### Fields of `"telemetry/opentelemetry": { "exporters":{} }` \* required fields ##### [`otlp`](https://www.krakend.io/docs/telemetry/jaeger/#otlp "Click to copy link to otlp") array The list of OTLP exporters you want to use. Set at least one object to push metrics and traces to an external collector using OTLP. Each item of otlp accepts the following properties: ##### [`custom_metric_reporting_period`](#otlp-custom_metric_reporting_period "Click to copy link to custom_metric_reporting_period") integer Whether you want to override the global `metric_reporting_period` attribute set for all exporters or not. Value in seconds. A missing attribute, or set to `0` means using whatever value was used in `metric_reporting_period` at the global level. Defaults to `0` ##### [`disable_metrics`](#otlp-disable_metrics "Click to copy link to disable_metrics") boolean Disable metrics in this exporter (leaving only traces if any). It won’t report any metrics when the flag is `true`. Defaults to `false` ##### [`disable_traces`](#otlp-disable_traces "Click to copy link to disable_traces") boolean Disable traces in this exporter (leaving only metrics if any). It won’t report any metrics when the flag is `true`. Defaults to `false` ##### [`host`](#otlp-host "Click to copy link to host") * string The host where you want to push the data. It can be a sidecar or a remote collector. ##### [`name`](#otlp-name "Click to copy link to name") * string A unique name to identify this exporter. Examples: `"local_prometheus"` , `"remote_grafana"` ##### [`port`](#otlp-port "Click to copy link to port") integer A custom port to send the data. The port defaults to 4317 for gRPC unless you enable `use_http`, which defaults to 4318. Defaults to `4317` ##### [`use_http`](#otlp-use_http "Click to copy link to use_http") boolean Whether this exporter uses HTTP instead of gRPC. Schema: [https://www.krakend.io/schema/v2.13/telemetry/opentelemetry.json](https://www.krakend.io/schema/v2.13/telemetry/opentelemetry.json "JSON schema definition") But as you can see there is a `layers` attribute in the example configuration that defines settings for all exporters (not only Jaeger). See the [layers options](/docs/telemetry/opentelemetry/#layers). Also notice that port `4317` and `"use_http": false` are set, meaning that gRPC communication is used. Change to `4318` and the flag to `true` for HTTP communication. ## Jaeger demo environment You can test this setup by running the **All in One** official Jaeger image and opening the necessary ports. For instance: ```yaml version: "3" services: krakend: image: krakend:2.13.8 volumes: - "./:/etc/krakend" ports: - "8080:8080" jaeger: image: jaegertracing/all-in-one:1.54 environment: COLLECTOR_ZIPKIN_HOST_PORT: ":9411" ports: - "5778:5778" # serve configs - "16686:16686" # serve frontend UI - "4317:4317" # otlp grpc: we remap this to be able to run other envs - "4318:4318" # otlp http: we remap this to be able to run other envs deploy: resources: limits: memory: 4096M # Adjust according to your setup ``` --- # [AWS X-Ray Telemetry Integration](https://www.krakend.io/docs/telemetry/xray/) [AWS X-Ray](https://aws.amazon.com/xray/) is a service offered by Amazon that provides an end-to-end view of requests as they travel through your application, and shows a map of your application’s underlying components. The Opencensus exporter allows you export data to AWS X-Ray. Enabling it only requires you to add the `xray` exporter in the [opencensus module](/docs/telemetry/opencensus/). Consider switching to OpenTelemetry The [AWS Distro for OpenTelemetry](https://aws-otel.github.io/) Collector (ADOT Collector) is an AWS supported version of the upstream OpenTelemetry Collector and is distributed by Amazon. It enables users to send telemetry data to AWS CloudWatch Metrics, Traces, and Logs backends as well as the other supported backends. See how to [configure OpenTelemetry](/docs/telemetry/opentelemetry/) The following configuration snippet sends data to your X-Ray: ```json { "extra_config": { "telemetry/opencensus": { "sample_rate": 100, "reporting_period": 0, "exporters": { "xray": { "version": "latest", "region": "eu-west-1", "use_env": false, "access_key_id": "myaccesskey", "secret_access_key": "mysecretkey" } } } } } ``` As with all [OpenCensus exporters](/docs/telemetry/opencensus/), you can add optional settings in the `telemetry/opencensus` level: ##### Fields of `"telemetry/opencensus"` \* required fields ##### [`enabled_layers`](https://www.krakend.io/docs/telemetry/xray/#enabled_layers "Click to copy link to enabled_layers") Lets you specify what data you want to export. All layers are enabled by default unless you declare this section. ##### [`backend`](#enabled_layers-backend "Click to copy link to backend") boolean Reports the activity between KrakenD and your services Defaults to `false` ##### [`pipe`](#enabled_layers-pipe "Click to copy link to pipe") boolean Reports the activity at the beginning of the proxy layer. It gives a more detailed view of the internals of the pipe between end-users and KrakenD, having into account merging of different backends. Defaults to `false` ##### [`router`](#enabled_layers-router "Click to copy link to router") boolean Reports the activity between end-users and KrakenD Defaults to `false` ##### [`reporting_period`](https://www.krakend.io/docs/telemetry/xray/#reporting_period "Click to copy link to reporting_period") integer The number of seconds passing between reports. If duration is less than or equal to zero, it enables the default behavior of each exporter. Defaults to `0` ##### [`sample_rate`](https://www.krakend.io/docs/telemetry/xray/#sample_rate "Click to copy link to sample_rate") integer A number between 0 (no requests at all) and 100 (all requests) representing the percentage of sampled requests you want to send to the exporter. **Sampling the 100% of the requests is generally discouraged** when the relationship between traffic and dedicated resources is sparse. Defaults to `0` Schema: [https://www.krakend.io/schema/v2.13/telemetry/opencensus.json](https://www.krakend.io/schema/v2.13/telemetry/opencensus.json "JSON schema definition") Then, the `exporters` key must contain an `xray` entry with the following properties: ##### Fields of `"telemetry/opencensus": { "exporters":{} }` \* required fields ##### [`xray`](https://www.krakend.io/docs/telemetry/xray/#xray "Click to copy link to xray") object AWS X-Ray is a service offered by Amazon that provides an end-to-end view of requests as they travel through your application, and shows a map of your application’s underlying components. \* Required **one of**: ( `region` + `version` + `access_key_id` + `secret_access_key` ) , or ( `region` + `version` + `use_env` ) ##### [`access_key_id`](#xray-access_key_id "Click to copy link to access_key_id") string Your access key ID provided by Amazon. Needed when `use_env` is unset or set to false. ##### [`region`](#xray-region "Click to copy link to region") * string The AWS geographical region, e.g, `us-east-1`. Example: `"eu-west-1"` ##### [`secret_access_key`](#xray-secret_access_key "Click to copy link to secret_access_key") string Your secret access key provided by Amazon. Needed when `use_env` is unset or set to false. ##### [`use_env`](#xray-use_env "Click to copy link to use_env") boolean When true the AWS credentials (access\_key\_id and secret\_access\_key) are taken from environment vars. Don’t specify them then. Defaults to `false` ##### [`version`](#xray-version "Click to copy link to version") * string The version of the AWS X-Ray service to use. Defaults to `"KrakenD-opencensus"` Schema: [https://www.krakend.io/schema/v2.13/telemetry/opencensus.json](https://www.krakend.io/schema/v2.13/telemetry/opencensus.json "JSON schema definition") See also the [additional settings](/docs/telemetry/opencensus/) of the Opencensus module that can be declared. --- # [Google Cloud and Stackdriver Integration](https://www.krakend.io/docs/telemetry/stackdriver/) [Google Cloud’s Operation Suite](https://cloud.google.com/products/operations) (formerly [Stackdriver](https://cloud.google.com/stackdriver/)) aggregates metrics, logs, and events from infrastructure, giving developers and operators a rich set of observable signals that speed root-cause analysis and reduce mean time to resolution (MTTR). The Opencensus exporter allows you to export **metrics and traces** to Google Cloud. Enabling it only requires you to add the `stackdriver` exporter in the [opencensus module](/docs/telemetry/opencensus/). The following configuration snippet sends the data: ```json { "extra_config": { "telemetry/opencensus": { "sample_rate": 100, "reporting_period": 60, "enabled_layers": { "backend": true, "router": true, "pipe": true }, "exporters": { "stackdriver": { "project_id": "my-krakend-project", "metric_prefix": "krakend", "default_labels": { "env": "production" } } } } } } ``` As with all [OpenCensus exporters](/docs/telemetry/opencensus/), you can add optional settings in the `telemetry/opencensus` level: ##### Fields of `"telemetry/opencensus"` \* required fields ##### [`enabled_layers`](https://www.krakend.io/docs/telemetry/stackdriver/#enabled_layers "Click to copy link to enabled_layers") Lets you specify what data you want to export. All layers are enabled by default unless you declare this section. ##### [`backend`](#enabled_layers-backend "Click to copy link to backend") boolean Reports the activity between KrakenD and your services Defaults to `false` ##### [`pipe`](#enabled_layers-pipe "Click to copy link to pipe") boolean Reports the activity at the beginning of the proxy layer. It gives a more detailed view of the internals of the pipe between end-users and KrakenD, having into account merging of different backends. Defaults to `false` ##### [`router`](#enabled_layers-router "Click to copy link to router") boolean Reports the activity between end-users and KrakenD Defaults to `false` ##### [`reporting_period`](https://www.krakend.io/docs/telemetry/stackdriver/#reporting_period "Click to copy link to reporting_period") integer The number of seconds passing between reports. If duration is less than or equal to zero, it enables the default behavior of each exporter. Defaults to `0` ##### [`sample_rate`](https://www.krakend.io/docs/telemetry/stackdriver/#sample_rate "Click to copy link to sample_rate") integer A number between 0 (no requests at all) and 100 (all requests) representing the percentage of sampled requests you want to send to the exporter. **Sampling the 100% of the requests is generally discouraged** when the relationship between traffic and dedicated resources is sparse. Defaults to `0` Schema: [https://www.krakend.io/schema/v2.13/telemetry/opencensus.json](https://www.krakend.io/schema/v2.13/telemetry/opencensus.json "JSON schema definition") Then, the `exporters` key must contain an `stackdriver` entry with the following properties: ##### Fields of `"telemetry/opencensus": { "exporters":{} }` \* required fields ##### [`stackdriver`](https://www.krakend.io/docs/telemetry/stackdriver/#stackdriver "Click to copy link to stackdriver") object Export metrics and traces to Google Cloud ##### [`default_labels`](#stackdriver-default_labels "Click to copy link to default_labels") * object A map object. Enter here any label that will be assigned by default to the reported metric so you can filter later on Stack Driver. Example: `{"env":"production"}` ##### [`metric_prefix`](#stackdriver-metric_prefix "Click to copy link to metric_prefix") string A prefix that you can add to all your metrics for better organization. ##### [`project_id`](#stackdriver-project_id "Click to copy link to project_id") * string The identifier of your Google Cloud project. The `project_id` **is not the project name**. You can omit this value from the configuration if you have an application credential file for Google. Example: `"ID"` Defaults to `""` Schema: [https://www.krakend.io/schema/v2.13/telemetry/opencensus.json](https://www.krakend.io/schema/v2.13/telemetry/opencensus.json "JSON schema definition") See also the [additional settings](/docs/telemetry/opencensus/) of the Opencensus module that can be declared. Google does not accept low reporting periods The number of **seconds** passing between reports in `reporting_period` must be **`60` or greater**, otherwise, Google will reject the connection. ## Authentication to Google Cloud The exporter searches for the **Application Default Credentials**. It looks for credentials in the following places, preferring the first location found: 1. A JSON file whose path is specified by the `GOOGLE_APPLICATION_CREDENTIALS` environment variable. 2. A JSON file in a location known to the `gcloud` command-line tool (e.g.: `$HOME/.config/gcloud/application_default_credentials.json`). 3. On Google Compute Engine and Google App Engine flexible environment, it fetches credentials from the metadata server. --- # [Azure Monitor Telemetry Integration](https://www.krakend.io/docs/telemetry/azure/) [Azure Monitor](https://azure.microsoft.com/en-us/services/monitor/) collects, analyzes, and acts on telemetry data from your Azure and on-premises environments. Azure Monitor helps you maximize performance and availability of your applications and proactively identify problems in seconds. [![Azure Monitor screenshot](/images/documentation/screenshots/azure-app-insights-1.png)](/images/documentation/screenshots/azure-app-insights-1.png) [![Azure Monitor screenshot](/images/documentation/screenshots/azure-app-insights-2.png)](/images/documentation/screenshots/azure-app-insights-2.png) [![Azure Monitor screenshot](/images/documentation/screenshots/azure-app-insights-3.png)](/images/documentation/screenshots/azure-app-insights-3.png) The gateway sends all the traces to a local **OpenTelemetry Collector** ([see repository](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/azuremonitorexporter)), allowing the gateway to offload data quickly and the collector can take care of additional handling like retries, batching, encryption or even sensitive data filtering. Finally, the Otel Collector pushes all the data to your **Application Insights** on Azure Monitor. ![Otel collector](/images/documentation/diagrams/azure-collector.mmd.svg) ## Application Insights configuration OpenTelemetry allows you to export telemetry data, Azure included. Nevertheless, the official **OpenTelemetry Collector** is flagged as **beta** and still **does not support pushing metrics** for Azure. So without further development on the OpenTelemetry side, the integration is limited to traces. There are three things you need to do: 1. Create the Application Insights resource on Azure 2. Start the OpenTelemetry Collector 3. Add it to KrakenD’s configuration ### Create an Application Insights resource To enable the Azure Monitor integration, you need to add a new resource **Application Insights**, under your Azure account and fill in the information as required (the details provided during the registration form are not relevant to the configuration). ![Application Insights](/images/documentation/screenshots/azure-application-insights.png) When the resource finishes creating, **save the Instrumentation Key** or **Connection String** for later usage. ### Starting the OpenTelemetry Collector You can start the OpenTelemetry Collector with Azure’s compatibility using its [Docker image](https://hub.docker.com/r/otel/opentelemetry-collector-contrib). Here there is a `docker-compose.yml` example that includes a KrakenD and a collector: ```yaml version: "3" services: krakend: image: krakend:2.13.8 command: [ "krakend", "run", "-d", "-c", "/etc/krakend/krakend.json"] volumes: - ./:/etc/krakend ports: - 8080:8080 # Collector otel-collector: image: otel/opentelemetry-collector-contrib command: ["--config=/etc/otel-collector.yaml"] volumes: - ./otel-collector.yaml:/etc/otel-collector.yaml ports: - "4317" - "4318" ``` The configuration mounted for the collector is below, `otel-collector.yaml`: ```yaml receivers: otlp: protocols: grpc: endpoint: "0.0.0.0:4317" http: endpoint: "0.0.0.0:4318" exporters: # logging: # verbosity: detailed azuremonitor: connection_string: XXXXXXX service: telemetry: logs: level: "warn" pipelines: traces: receivers: [otlp] exporters: [azuremonitor] ``` Enable the logging only if you find problems and want extra information. Replace the value of the `connection_string` entry with the one you got in your Azure dashboard. ### Configuration for KrakenD Lastly, add at the service level of KrakenD the following configuration: ```json { "version": 3, "extra_config": { "telemetry/opentelemetry": { "trace_sample_rate": 1, "exporters": { "otlp": [ { "name": "azure_monitor", "host": "localhost", "port": 4317 } ] } } } } ``` The configuration above reports a 100% of the activity (`trace_sample_rate = 1`), when in production consider lowering this value (e.g., `0.1`) as you will want a sample of the traffic only. The `otel-collector` above is the name of the docker compose service running the collector. You might need to replace it if you are not using this example. With these three steps, you can start sending data to KrakenD. You should start seeing the graphs populated on Azure Monitor in a couple of minutes. Insecure flag Most of the times, the communication between KrakenD and the collector happens in the same machine. It is rare that this connection uses SSL, and if it doesn’t, the `insecure` flag must be set to `true`. Otherwise, KrakenD will fail silently. ## Other Resources - [OpenTelemetry Collector Docker Image](https://hub.docker.com/r/otel/opentelemetry-collector-contrib) with Azure support. - [Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview?tabs=net) - [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) --- # [New Relic Telemetry Integration (native SDK) (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/telemetry/newrelic/) The New Relic native integration lets you push KrakenD **metrics and distributed traces** to your New Relic dashboard. It uses internally the official New Relic SDK and brings its features to your **APM dashboard**. How is the native SDK integration different from OpenTelemetry? Open-source and Enterprise users can also report traces and metrics to New Relic using the [OpenTelemetry](/docs/telemetry/opentelemetry/) integration. Nevertheless, New Relic supports an OTLP specification dated from November 2021 (`v1.8.0`), which is two years behind the current OTLP protocol at the time of writing. The native SDK component in KrakenD uses their official toolset, and sends richer data and is easier to set up without needing an external collector. Examples of the data you can see on New Relic: [![NewRelic integration screenshot](/images/documentation/nr/new-relic-1.png)](/images/documentation/nr/new-relic-1.png) [![NewRelic integration screenshot](/images/documentation/nr/new-relic-2.png)](/images/documentation/nr/new-relic-2.png) [![NewRelic integration screenshot](/images/documentation/nr/new-relic-3.png)](/images/documentation/nr/new-relic-3.png) [![NewRelic integration screenshot](/images/documentation/nr/new-relic-4.png)](/images/documentation/nr/new-relic-4.png) [![NewRelic integration screenshot](/images/documentation/nr/new-relic-5.png)](/images/documentation/nr/new-relic-5.png) [![NewRelic integration screenshot](/images/documentation/nr/new-relic-6.png)](/images/documentation/nr/new-relic-6.png) ## New Relic configuration Logs are not part of this component If you want to see your logs on NewRelic, you must [forward them](https://docs.newrelic.com/docs/logs/forward-logs/enable-log-management-new-relic/) using your preferred solution. To add KrakenD telemetry reporting to New Relic, you only need add the following configuration with your API Key: ```json { "version": 3, "name": "My KrakenD API gateway", "extra_config": { "telemetry/newrelic": { "license": "XXXX", "debug": true } } } ``` This is all the configuration you need for New Relic, and **you don’t need to install anything else** to see metrics and traces, everything else is automatic, including choosing the region to push the data (which is linked to your license). The service name in the New Relic dashboard will match the `name` attribute in the root of your KrakenD configuration. ##### Fields of NewRelic exporter \* required fields ##### [`debug`](https://www.krakend.io/docs/enterprise/telemetry/newrelic/#debug "Click to copy link to debug") boolean Set to true when configuring New Relic for the first time while in development, to see the activity in the logs. Set to false in production. Defaults to `false` ##### [`headers_to_pass`](https://www.krakend.io/docs/enterprise/telemetry/newrelic/#headers_to_pass "Click to copy link to headers_to_pass") array of strings Defines an explicit list of headers sent during the client request that will be reported to NewRelic, in addition to the default headers NewRelic sets. Setting the `["*"]` value will send all headers sent by the client to NewRelic. Whether you declare this setting or not, you will usually receive from the NewRelic SDK the `Accept`, `Content-Type`, `User-Agent`, and `Referer` headers. Example: `["*"]` ##### [`license`](https://www.krakend.io/docs/enterprise/telemetry/newrelic/#license "Click to copy link to license") * string The API key provided by New Relic to push data into your account. Schema: [https://www.krakend.io/schema/v2.13/telemetry/newrelic.json](https://www.krakend.io/schema/v2.13/telemetry/newrelic.json "JSON schema definition") ## Troubleshooting If you don’t see reports in your New Relic account after sending activity to KrakenD, enable the `debug` flag locally to see what is happening. The logs will show information to help you understand what is going on. The [logger](/docs/logging/) should be configured with a `DEBUG` level to see these messages. ## Getting your API Key from New Relic To set the `license` attribute on KrakenD you need to: 1. Go to your account, and select *API keys* ![Setting an API Key](/images/documentation/nr/getting_api_key.png) 2. In the *API keys* screen, select *Create a key* ![Setting an API Key](/images/documentation/nr/getting_api_key2.png) 3. Select the *Key type* `Ingest - License` from the dropdown and create the key ![Setting an API Key](/images/documentation/nr/getting_api_key3.png) 4. Copy the resulting API key into the configuration. *The screenshots and options above might change as a result of NewRelic continuous improvement of their interfaces.* --- # [Advanced Logging (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/logging/) The logging component is an essential configuration block for any installation that lets you choose **where** and **how** to log the gateway activity. It also opens the door to integrating other components for more advanced usage. The Enterprise version of the logging component is much more extensible as it supports **custom formatting and additional placements**. The `telemetry/logging` has the following logging capabilities: - Access Log details - Application Log - Backend Log (upstream) - Write to the **Stdout (the console)** - Write to the **Syslog (local file or remote server)** - Set different reporting levels - Use custom layouts ## Types of log messages The content that KrakenD writes in its log represents three types of logging: - **Access Log**, declared by `access_log_format`, is set by default. It shows traffic activity. - **Application Log**, declared by `format`, is set by default. It shows gateway events. - **Backend Log**, declared by `backend`, is optional. It shows the interaction with backend services. ## Logging Configuration To add ample logging capabilities, you need to place the `telemetry/logging` namespace under the `extra_config`. When added at the service level (root of your configuration), you define the logging properties for the entire gateway. When added in a specific `backend` (Backend Log only), you choose what gets printed when talking to that specific service. You can **customize the format** of the logs and send them both to the *stdout* and the *syslog*. However, if you don’t use this component, then KrakenD uses the basic capabilities of the [Lura Project](https://luraproject.org): standard output only and a fixed `DEBUG` level. The different log types support independent formats and types of fields to output. For instance, you can have one log in plain text and another in JSON, or have the same field outputted with different precisions. When you do this, you must parse logs accordingly in your log ingestion. When customized, all log types support **variables along with formats** to print the information in a specific way. For instance, a configuration using the default options and no variables for Access and Application Logs would be: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "stdout": true } } } ``` While another configuration customizing the different Log types using variables could be: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "stdout": true, "format": "default", "access_log_format": "custom", "access_log_custom_format": "[AccessLog] %{prefix} %{time} | %{statusCode} | %{latencyMs} | %{clientIP} | %{method} %{scheme}://%{host}%{path}?%{query} %{query.bar} %{header.Authorization} %{jwt.sub}\n", "backend": { "prefix": ">>> ", "custom_format": "%{prefix}%{time} | %{statusCode} | %{latencyMs} | %{method} %{host} %{path}\n" } } } } ``` Remember: - `format` defines the Application Log - `access_log_format` defines the Access Log - `backend` defines the Backend Log ## Customizing Log formats Below are the different options to customize each of the different log types. Each has a list of available variables. All the variables are specified as a `%{verb}` and you must write them precisely as documented in each section. However, you can inject into them a format using the syntax `%{verb:format}` (unless otherwise indicated) where the `verb` contains the information available for printing, and `:format` is an optional string that you can use to fine-tune even more the final content. The `:format` part can be one of: - A Go [formatting verb](https://pkg.go.dev/fmt) - A Go [Time layout](https://pkg.go.dev/time#Layout) for the `%{time}` variables, or a string with one of the following predefined formats: - `ansic` returns time in ANSIC format. Example: `Tue Nov 10 23:00:00 2009` - `unixdate` returns time in UnixDate format. Example: `Tue Nov 10 23:00:00 UTC 2009` - `rfc822` returns time in RFC822 format. Example: `10 Nov 09 23:00 UTC` - `rfc822z` returns time in RFC822Z format. Example: `10 Nov 09 23:00 +0000` - `rfc850` returns time in RFC850 format. Example: `Tuesday, 10-Nov-09 23:00:00 UTC` - `rfc1123` returns time in RFC1123 format. Example: `Tue, 10 Nov 2009 23:00:00 UTC` - `rfc1123z` returns time in RFC1123Z format. Example: `Tue, 10 Nov 2009 23:00:00 +0000` - `rfc3339` returns time in RFC3339 format. Example: `2009-11-10T23:00:00Z` - `rfc3339nano` returns time in RFC3339Nano format. Example:`2009-11-10T23:00:00.999999999Z07:00` - `kitchen` returns time in Kitchen format. Example: `11:00PM` For instance, in a **Backend Log**, you could have the formats specified like this: - `%{time:kitchen}` –> Prints time like `11:00PM` - `%{time:2006-01-02 15:04:05}` –> Prints a date time. Notice that Go uses an **exact date an time** to specify formats `¯\_(ツ)_/¯`. - `%{host:q}` –> Prints the host with a single-`q`uoted character literal safely escaped - `%{resp.stats.consumedTokens:d}` –> Prints the value `723` when the response is `{"stats": { "consumedTokens": 723 } }`, as an integer (`d`) ## Access Log The Access Log shows **users’ traffic** and prints: which endpoints are requested, when, the status code, the response duration, the requesting IP, and the method. The default format of the Access Log is as follows: ```log [GIN] yyyy/mm/dd - hh:mm:ss | 200 | 0.019ms | 172.17.0.1 | GET "/user/foo" [GIN] yyyy/mm/dd - hh:mm:ss | 200 | 0.551ms | 172.17.0.1 | GET "/category/bar" ``` The duration is printed uniformly in milliseconds `ms` with a three-digit precision (for microsecond resolution). Customizations of the Access Log usually add information in tokens or headers for use cases where you want to identify a specific user in the logs to trace it back. To customize the Access Log you can use a predefined format, or use your own pattern. The Access Log is configured like this: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "stdout": true, "access_log_format": "httpdCommon" } } } ``` The example above prints the Access Log as in the Apache Web server. The following options are involved: ##### Logging options related with the Access Log \* required fields ##### [`access_log_custom_format`](https://www.krakend.io/docs/enterprise/logging/#access_log_custom_format "Click to copy link to access_log_custom_format") string **Enterprise only.** You can write the access log pattern you would like to use. Add a newline `\n` at the end of the pattern. See the variables you can use. Example: `"%{prefix} %{time} [AccessLog] |%{statusCode}| %{latencyMs} | %{clientIP} | %{method} %{path}\n"` Defaults to `""` ##### [`access_log_format`](https://www.krakend.io/docs/enterprise/logging/#access_log_format "Click to copy link to access_log_format") **Enterprise only.** Enable a formatter for the access log. You can write your own pattern using the `custom` value, or you can use one of the predefined ones. Possible values are: `""` , `"default"` , `"httpdCommon"` , `"httpdCombine"` , `"json"` , `"custom"` Defaults to `""` ##### [`level`](https://www.krakend.io/docs/enterprise/logging/#level "Click to copy link to level") * What type of **reporting level** do you expect from the application? The options below go from more verbose to least. Use the `DEBUG` level in the development stages but not in production. Some components can add extra verbosity while in DEBUG mode and send **multiline content**, which is not always suitable for automated log parsing. Possible values are: `"DEBUG"` , `"INFO"` , `"WARNING"` , `"ERROR"` , `"CRITICAL"` ##### [`prefix`](https://www.krakend.io/docs/enterprise/logging/#prefix "Click to copy link to prefix") string Adds the defined string at the beginning of every logged line, so you can quickly filter messages with external tools later on. It’s recommended to always add a prefix `[INSIDE BRACKETS]` to make use of predefined dashboards. ##### [`stdout`](https://www.krakend.io/docs/enterprise/logging/#stdout "Click to copy link to stdout") boolean Set to true to send logs to stdout. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/telemetry/logging.json](https://www.krakend.io/schema/v2.13/telemetry/logging.json "JSON schema definition") These are the predefined patterns for the `access_log_format`: - `default`: Uses `%{prefix} %{time} [AccessLog] |%{statusCode}| %{latencyMs} | %{clientIP} | %{method} %{path}\n` as pattern. - `httpdCommon`: Uses `%{clientIP} - - [%{time}] \"%{method} %{uri} %{proto}\" %{statusCode} -\n` as in the Apache HTTPd log format - `httpdCombined`: The Apache HTTPd Combined log format `%{clientIP} - - [%{time}] \"%{method} %{uri} %{proto}\" %{statusCode} - \"%{header.Referer}\" \"%{header.User-Agent}\"\n` - `json`: Uses `{\"prefix\":\"%{prefix}\", \"time\":\"%{time}\", \"status_code\":%{statusCode}, \"latency\":\"%{latency}\", \"client_ip\":\"%{clientIP}\", \"method\":\"%{method}\", \"path\":\"%{path}\"}\n` - `custom`: Write your own pattern, as defined in the `access_log_custom_format` attribute. As explained below, you can use variables to fine-tune the output when you need a specific Access Log format. ### Variables for the Access Log When the `access_log_format` is set to `custom`, you can write the final pattern in the field `access_log_custom_format` using your desired format. These are the variables available: - `%{prefix}`: The value you have set under the `prefix` attribute. - `%{time}`: The time when the access finished. The default format prints dates like 2006/01/02 - 15:04:05.000 - `%{statusCode}`: The response status code as given to the consumer - `%{latency}`: The latency in seconds. The default format is `.3fs` (3 decimal float + `s` for seconds). This computes the time of the request from beginning to end. - `%{latencyMs}`: The latency in milliseconds. The default format is `.3fms` (3 decimal float + `ms` for milliseconds). This computes the time of the request from beginning to end. - `%{latencyUs}`: The latency in microseconds. The default format is `.3fus` (3 decimal float + `us` for microseconds). This computes the time of the request from beginning to end. - `%{latencyNs}`: The latency in nanoseconds. The default format is `dns` (integer + `ns` for nanoseconds). This computes the time of the request from beginning to end. - `%{clientIP}`: The real IP of the client - `%{method}`: The HTTP verb used - `%{path}`: The endpoint path, does not include the query string (e.g., `/path/to/endpoint`) - `%{uri}`: The endpoint path and query string (e.g., `/path/to/endpoint?foo=bar&doe=baz`) - `%{host}`: The host of the URL - `%{header.key[.index]}`: The value of a specific header, where `key` is the header name. On multiheader values, you can optionally add an index. For instance, `%{header.Cookie.1}` prints the second value of the Cookie, while `%{header.Cookie}` prints a unique header or the first match. - `%{scheme}`: The scheme used (e.g., `http`, `https`, `ws`) - `%{jwt.key}`: The value of a specific claim in the token, where `key` is the claim name (only first level, non-nested, claims). - `%{query}`: The query strings passed in the request - `%{proto}`: The protocol used (e.g., `HTTP/1.0`, `HTTP/2`, etc.) Remember that as explained above, you can [add a `:format`](/docs/enterprise/logging/#customizing-log-formats) to these variables. For instance, you could **print the Access Log in JSON format** as follows: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "syslog": false, "stdout": true, "access_log_format": "json" } } } ``` Or you could have a log that includes the JWT subject, the authorization header, and some query strings as follows: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "syslog": false, "stdout": true, "access_log_format": "custom", "access_log_custom_format": "[AccessLog] %{prefix} %{time} | %{statusCode} | %{latencyMs} | %{clientIP} | %{method} %{scheme}://%{host}%{path}?%{query} %{query.foo} %{header.Authorization} %{jwt.sub}\n" } } } ``` ### Disabling the Access Log You can also disable the Access Log, but this option is **not** handled through this component, but from the `router` namespace and setting the flag `disable_access_log`. For instance: ```json { "version": 3, "extra_config": { "router":{ "disable_access_log": true } } ``` ## Application Log The Application Log messages are the errors, warnings, debugging information, and other events shown by the gateway while it operates. The Application Logs are customizable as you can extend the functionality, such as sending the events to the **syslog**, using JSON format, choosing the verbosity level, or using the [Graylog Extended Log Format](/docs/enterprise/logging/graylog-gelf/) (GELF). Application Logs might look different on each application, but this is an example: ``` yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: Gin] Debug enabled yyyy/mm/dd hh:mm:ss KRAKEND INFO: Starting the KrakenD instance yyyy/mm/dd hh:mm:ss KRAKEND INFO: [SERVICE: Gin] Building the router yyyy/mm/dd hh:mm:ss KRAKEND INFO: [SERVICE: Gin] Listening on port: 8080 yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: AsyncAgent][mkt-event] Starting the async agent yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [ENDPOINT: mkt-event] Building the proxy pipe yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [BACKEND: /__debug/some] Building the backend pipe yyyy/mm/dd hh:mm:ss KRAKEND INFO: [SERVICE: AsyncAgent][AMQP][mkt-event] Starting the consumer yyyy/mm/dd hh:mm:ss KRAKEND ERROR: [SERVICE: Asyncagent][mkt-event] building the amqp subscriber: dial tcp 192.168.2.223:5672: connect: connection refused ``` Here’s an example configuring the Application Log using Logstash format: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "stdout": true, "format": "logstash" } } } ``` The following options are involved: ##### Logging options related with the Application Log \* required fields ##### [`custom_format`](https://www.krakend.io/docs/enterprise/logging/#custom_format "Click to copy link to custom_format") string Lets you write a custom logging pattern using variables, e.g: `%{message}`. ##### [`format`](https://www.krakend.io/docs/enterprise/logging/#format "Click to copy link to format") string Specify the format of the application logs: default, logstash, or custom. The custom format needs an additional key “custom\_format”. Examples: `"default"` , `"logstash"` , `"custom"` Defaults to `"default"` ##### [`level`](https://www.krakend.io/docs/enterprise/logging/#level "Click to copy link to level") * What type of **reporting level** do you expect from the application? The options below go from more verbose to least. Use the `DEBUG` level in the development stages but not in production. Some components can add extra verbosity while in DEBUG mode and send **multiline content**, which is not always suitable for automated log parsing. Possible values are: `"DEBUG"` , `"INFO"` , `"WARNING"` , `"ERROR"` , `"CRITICAL"` ##### [`prefix`](https://www.krakend.io/docs/enterprise/logging/#prefix "Click to copy link to prefix") string Adds the defined string at the beginning of every logged line, so you can quickly filter messages with external tools later on. It’s recommended to always add a prefix `[INSIDE BRACKETS]` to make use of predefined dashboards. ##### [`stdout`](https://www.krakend.io/docs/enterprise/logging/#stdout "Click to copy link to stdout") boolean Set to true to send logs to stdout. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/telemetry/logging.json](https://www.krakend.io/schema/v2.13/telemetry/logging.json "JSON schema definition") The attribute `format` allows you to set a formatter for the Application Log. The following values are available: - `default`: Uses the pattern `%{time:2006/01/02 - 15:04:05.000} %{color}▶ %{level:.6s}%{color:reset} %{message}` - `logstash`: **Logs in JSON format** using the logstash format. See [Logstash](/docs/enterprise/logging/logstash/) for more information. E.g.: `{"@timestamp":"%{time:2006-01-02T15:04:05.000+00:00}", "@version": 1, "level": "%{level}", "message": "%{message}", "module": "%{module}"}`. - `custom`: Write the pattern from scratch, as defined in the `custom_format` attribute. ### Variables available in the Application Log The variables available when defining the `custom_format` of the Application Log are: - `%{id}`: Prints the sequence number for log message (uint64). - `%{pid}`: Prints the process id (int) - `%{time}`: Prints the time when log occurred. It uses the [Go time format](https://pkg.go.dev/time). For instance `%{time:2006/01/02 - 15:04:05.000}` - `%{level}`: Prints the log level - `%{program}`: Prints the command running - `%{message}`: Prints the Application Log message - `%{module}`: Prints the module - `%{color}`: Prints the ANSI color based on log level, the output can be adjusted to either use bold colors, e.g, `%{color:bold}` or to reset the ANSI attributes `%{color:reset}`. For example, you can customize your pattern like this: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "syslog": false, "stdout": true, "format": "custom", "custom_format": "[MY APP LOG] %{time:2006/01/02 - 15:04:05.000} %{color}▶ %{level:.6s}%{color:reset} %{message}" } } } ``` ## Backend Log The Backend Log is a special type of Application Log between the gateway and the upstream service, regardless of what the consumer client could be doing. It is inserted inside a `backend`’s `extra_config` or in the service directly, and allows you to **track detailed activity** with your backends, offering a flexible and powerful logging system using a custom formatter that injects dynamic verbs and custom layouts. It helps capture exactly the backend request/response data you need for monitoring, troubleshooting, or auditing, but it also sets a scenario for monetization and other exploitation of data. ### Global Backend Log The following example sets a Backend Log for all backends across the configuration. ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "stdout": true, "backend": { "custom_format": "%{prefix}%{time:kitchen} ||| %{time:3PM} |%{statusCode}| %{method} %{host} %{path}", "prefix": ">>> " } } } } ``` The important part here is everything under `backend`, and accepts the following parameters: ##### Logging options related with the Backend Log \* required fields ##### [`custom_format`](https://www.krakend.io/docs/enterprise/logging/#custom_format "Click to copy link to custom_format") string Specify the custom format of the Backend Logs. Example: `"%{time:kitchen} | (╯°□°)╯( ┻━┻ %{statusCode} | %{method} %{host} %{path}\n"` Defaults to `"%{prefix}%{time} |%{statusCode}| %{latencyMs} | %{method} %{host} %{path}\n"` ##### [`log_with_level`](https://www.krakend.io/docs/enterprise/logging/#log_with_level "Click to copy link to log_with_level") What type of **reporting level** do you want to set at the backends? The options below go from more verbose to least. Use the `DEBUG` level in the development stages but not in production. Some components can add extra verbosity while in DEBUG mode and send **multiline content**, which is not always suitable for automated log parsing. Possible values are: `"DEBUG"` , `"INFO"` , `"WARNING"` , `"ERROR"` , `"CRITICAL"` ##### [`no_value`](https://www.krakend.io/docs/enterprise/logging/#no_value "Click to copy link to no_value") string When the variable does not resolve to any value, the string you want to write in the log. If the string is set to an empty value, a dash `-` is printed. Examples: `"-"` , `"null"` , `"no_value"` , `""` Defaults to `"-"` ##### [`prefix`](https://www.krakend.io/docs/enterprise/logging/#prefix "Click to copy link to prefix") string Adds the defined string at the beginning of every logged line, so you can quickly filter messages with external tools later on. Example: `">>> "` Defaults to `""` Schema: [https://www.krakend.io/schema/v2.13/telemetry/backend\_log.json](https://www.krakend.io/schema/v2.13/telemetry/backend_log.json "JSON schema definition") ### Per-Service Backend Log You can also set the Backend Log on specific backends by adding the configuration under a backend. If you have a Backend Log in the service, the backend one inherits and overrides properties. In this case, you need to use the `telemetry/logging` namespace, but directly adding the attributes of the `backend`. Like this: ```json { "endpoint": "/test", "backend": [ { "url_pattern": "/__debug/test", "host": ["http://api.example.com"], "extra_config": { "telemetry/logging": { "prefix": ">>> ", "@comment": "Table-flip all the things!", "custom_format": "%{time:kitchen} | (╯°□°)╯( ┻━┻ %{statusCode} | %{method} %{host} %{path}\n" } } } ] } ``` ### Variables available in the Backend Log The `custom_format` accepts the following variables: - `%{prefix}`: Prints the prefix defined in the config file. It does not allow formats. - `%{time}`: Prints the current time, the format can be an alias or a Go time format ([check above](/docs/enterprise/logging/#customizing-log-formats)). The default format is `rfc3339` - `%{latency}`: The latency in seconds. The default format is `.3fs` (3 decimal float + `s` for seconds). This computes the time of the **backend** request from beginning to end. - `%{latencyMs}`: The latency in milliseconds. The default format is `.3fms` (3 decimal float + `ms` for milliseconds). This computes the time of the **backend** request from beginning to end. - `%{latencyUs}`: The latency in microseconds. The default format is `.3fus` (3 decimal float + `us` for microseconds). This computes the time of the **backend** request from beginning to end. - `%{latencyNs}`: The latency in nanoseconds. The default format is `dns` (integer + `ns` for nanoseconds). This computes the time of the **backend** request from beginning to end. - `%{host}`: The backend host address. The default format is `s` (string) - `%{path}`: The request path. The default format is `s` - `%{method}`: The request method (uppercase). The default format is `s` - `%{query[.key]}`: The request query string. You can optionally add a `key`(e.g.,`%{query.foo}`). If there are multiple values of the query string (e.g., `?foo=bar&foo=baz`), only the first value is returned. The default format is `s` - `%{reqHeader.key[.index]}`: The request header, an index can be specified in case there are multiple header values. The default format is `v` (value). Take into account that requests are filtered based on `input_headers` configuration and other components that could transform them. This MIGHT NOT match the original request of the user, but the one generated by KrakenD on backend communication. - `%{respHeader.key[.index]}`: Requires `no-op`. The response header of the upstream service. The default format is `v` (as in value) - `%{statusCode}`: Requires `no-op`. The original response status code. The default format is `d` (integer base 10) - `%{resp.key[.key...]}`: A field of the response body. If nested, it can be accessed with dot notation (does not traverse arrays). This variable **does not work on `no-op`** . The default format is `v` - `%{error}`: The error returned by the gateway as a string. For instance, on a timeout, you will get `context deadline exceeded`. The default format is `s` Here are examples of variables that specify additional formatting: - `%{statusCode:x}` –> To return status codes in hexadecimal code, because your DevOps needs to spice their lives a little. - `%{host:q}` –> Prints the host single-quoted (`q`) and safely escaped - `%{reqHeader.X-Test}` –> Prints the value of the `X-Test` header - `%{respHeader.Set-Cookie.1:6s}` –> Prints the second cookie set by the backend (index starts at `0`), then adds spaces to the right to have a total of 6 characters. Requires `no-op`. - `%{resp.gemini.consumedTokens:d}` –> Prints the tokens consumed by Gemini (`31`) when the response is `{"gemini": { "consumedTokens": 31 },"chatgpt": { "consumedTokens": 33 } }`, as an integer (`d`) ## Writing the log to a file Although logging on disk might impact software performance and is discouraged in high-throughput systems, you can still store the logs in a file. **Avoid redirecting the output** (e.g.: `krakend run > krakend.log`) and **use the *syslog* of your machine instead**. To set up logs on disk, you should consider the following steps: 1. Add the syslog configuration to yor `krakend.json` 2. Add a specific entry for krakend under `/etc/rsyslog.d/` 3. Optionally add log rotation ### 1. Syslog configuration ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "WARNING", "syslog": true, "stdout": true } } } ``` You might set the `stdout` to `false` if you don’t want to check on the console but only on the logs. SYSLOG contains Application Logs only **Access Logs are never written in the syslog**, regardless of their configuration, and they show only in **stdout**. ### 2. Add an entry to `rsyslog` The folder `/etc/rsyslog.d/` shows the different configurations of the system. We will create a new file `/etc/rsyslog.d/krakend.conf` and place this content inside: ``` local3.* -/var/log/krakend.log ``` If you are familiar with *syslog*, you change the `syslog_facility` to any other (local) value and adjust it in the file above. ### 3. KrakenD log rotation The syslog will populate the log and can be used conveniently with the default system tools like **rotating the logs** with `logrotate`. Add a new configuration file `/logrotate.d/krakend` and add the content below: ``` /var/log/krakend.log { rotate 7 daily missingok delaycompress compress postrotate /usr/lib/rsyslog/rsyslog-rotate endscript } ``` --- # [Logging](https://www.krakend.io/docs/logging/) The logging component is an essential configuration block for any installation that lets you choose **where** and **how** to log the gateway activity. It also opens the door to integrating other components for more advanced usage. When you add the logging component, you can customize the format of the logs and send them both to the *stdout* and the *syslog*. However, if you don’t use this component, then KrakenD uses the basic capabilities of [Lura](https://luraproject.org) (standard output only and a `DEBUG` level). The `telemetry/logging` has the following logging capabilities: - Write to the **Stdout (the console)** - Write to the **Syslog (local file or remote server)** - Add a prefix to log lines - Select the reporting level - Option to use a predefined or custom format - Use custom layouts and define logged fields ([Enterprise version](/docs/enterprise/logging/)) ## Types of log messages The content that KrakenD writes in its log represents two types of logging: - **Access log** - **Application log** The access log and the application log can have different formats. For instance, you can have one in text plain and the other in JSON if needed. When you do this, you must parse them accordingly in your log ingestion. ### Access log The access log shows **users’ activity** and prints: which endpoints are requested, when, the status code, the duration, the requesting IP, and the method. The default format of the access log is as follows: ```log [GIN] yyyy/mm/dd - hh:mm:ss | 200 | 4.529µs | 172.17.0.1 | GET "/user/foo" [GIN] yyyy/mm/dd - hh:mm:ss | 200 | 3.647µs | 172.17.0.1 | GET "/category/bar" ``` **Access logs are never written in the syslog**, regardless of their configuration, and they show only in **stdout**. While the access log is not customizable in the Open Source edition, Enterprise allows you to write the format you want to output using the attribute `access_log_format` (see below). #### Disabling the access log You can also disable the access log setting the flag `disable_access_log`, or you can [remove specific requests from logs](/docs/service-settings/router-options/#remove-requests-from-logs) , like when you don’t want to see the health checks. ### Application log The application log messages are the errors, warnings, debugging information, and other messages shown by the gateway while it operates. The application logs are customizable as you can extend the functionality, such as sending the events to the **syslog**, using JSON format, choosing the verbosity level, or using the [Graylog Extended Log Format](/docs/logging/graylog-gelf/) (GELF). In addition to this, a lot of **exporters** are available to send your logs out to third parties (see [Telemetry](/docs/telemetry/)) Application logs might look different on each application, but this is an example: ``` yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: Gin] Debug enabled yyyy/mm/dd hh:mm:ss KRAKEND INFO: Starting the KrakenD instance yyyy/mm/dd hh:mm:ss KRAKEND INFO: [SERVICE: Gin] Building the router yyyy/mm/dd hh:mm:ss KRAKEND INFO: [SERVICE: Gin] Listening on port: 8080 yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: AsyncAgent][mkt-event] Starting the async agent yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [ENDPOINT: mkt-event] Building the proxy pipe yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [BACKEND: /__debug/some] Building the backend pipe yyyy/mm/dd hh:mm:ss KRAKEND INFO: [SERVICE: AsyncAgent][AMQP][mkt-event] Starting the consumer yyyy/mm/dd hh:mm:ss KRAKEND ERROR: [SERVICE: Asyncagent][mkt-event] building the amqp subscriber: dial tcp 192.168.2.223:5672: connect: connection refused ``` ## Logging Configuration To add ample logging capabilities, you need to add the component at the service level of your `krakend.json` configuration under the `extra_config` key: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "syslog": false, "stdout": true } } } ``` These are the different supported configuration options: ##### Fields of Logging \* required fields ##### [`access_log_custom_format`](https://www.krakend.io/docs/logging/#access_log_custom_format "Click to copy link to access_log_custom_format") string **Enterprise only.** You can write the access log pattern you would like to use. Add a newline `\n` at the end of the pattern. See the variables you can use. Example: `"%{prefix} %{time} [AccessLog] |%{statusCode}| %{latencyMs} | %{clientIP} | %{method} %{path}\n"` Defaults to `""` ##### [`access_log_format`](https://www.krakend.io/docs/logging/#access_log_format "Click to copy link to access_log_format") **Enterprise only.** Enable a formatter for the access log. You can write your own pattern using the `custom` value, or you can use one of the predefined ones. Possible values are: `""` , `"default"` , `"httpdCommon"` , `"httpdCombine"` , `"json"` , `"custom"` Defaults to `""` ##### [`access_log_missing_key_marker`](https://www.krakend.io/docs/logging/#access_log_missing_key_marker "Click to copy link to access_log_missing_key_marker") string **Enterprise only.** When you use a custom access log format, the variable you are trying to print could be empty. For instance, you have added in the format `%{header.Authorization}` but the header is missing in the request. In this case, the printed value is what you configure here. If the string is set to an empty value, a dash `-` is printed. Examples: `"%{default}"` , `"%{httpdCommon}"` , `"%{prefix} %{time} [AccessLog] |%{statusCode}| %{latencyMs} | %{clientIP} | %{method} %{path}\n"` Defaults to `"-"` ##### [`backend`](https://www.krakend.io/docs/logging/#backend "Click to copy link to backend") object **Enterprise only.** Add a specific [Backend Log](https://www.krakend.io/docs/enterprise/logging/#backend-log). Useful to see status codes, headers, and other information that come from your backends. ##### [`custom_format`](https://www.krakend.io/docs/logging/#custom_format "Click to copy link to custom_format") string Lets you write a custom logging pattern using variables, e.g: `%{message}`. ##### [`format`](https://www.krakend.io/docs/logging/#format "Click to copy link to format") string Specify the format of the application logs: default, logstash, or custom. The custom format needs an additional key “custom\_format”. Examples: `"default"` , `"logstash"` , `"custom"` Defaults to `"default"` ##### [`level`](https://www.krakend.io/docs/logging/#level "Click to copy link to level") * What type of **reporting level** do you expect from the application? The options below go from more verbose to least. Use the `DEBUG` level in the development stages but not in production. Some components can add extra verbosity while in DEBUG mode and send **multiline content**, which is not always suitable for automated log parsing. Possible values are: `"DEBUG"` , `"INFO"` , `"WARNING"` , `"ERROR"` , `"CRITICAL"` ##### [`prefix`](https://www.krakend.io/docs/logging/#prefix "Click to copy link to prefix") string Adds the defined string at the beginning of every logged line, so you can quickly filter messages with external tools later on. It’s recommended to always add a prefix `[INSIDE BRACKETS]` to make use of predefined dashboards. ##### [`stdout`](https://www.krakend.io/docs/logging/#stdout "Click to copy link to stdout") boolean Set to true to send logs to stdout. Defaults to `false` ##### [`syslog`](https://www.krakend.io/docs/logging/#syslog "Click to copy link to syslog") boolean Set to true to send logs to syslog. Defaults to `false` ##### [`syslog_facility`](https://www.krakend.io/docs/logging/#syslog_facility "Click to copy link to syslog_facility") When using syslog, the facility tells KrakenD where to send the messages as set by the locals of the [syslog standard](https://www.rfc-editor.org/rfc/rfc5424.html). Possible values are: `"local0"` , `"local1"` , `"local2"` , `"local3"` , `"local4"` , `"local5"` , `"local6"` , `"local7"` Defaults to `"local3"` Schema: [https://www.krakend.io/schema/v2.13/telemetry/logging.json](https://www.krakend.io/schema/v2.13/telemetry/logging.json "JSON schema definition") ### Customizing the application log The attribute `format` allows you to set a formatter for the application log. The following values are available: - `default`: Uses the pattern `%{time:2006/01/02 - 15:04:05.000} %{color}▶ %{level:.6s}%{color:reset} %{message}` - `logstash`: **Logs in JSON format** using the logstash format. See [Logstash](/docs/logging/logstash/) for more information. E.g.: `{"@timestamp":"%{time:2006-01-02T15:04:05.000+00:00}", "@version": 1, "level": "%{level}", "message": "%{message}", "module": "%{module}"}`. - `custom`: Write the pattern from scratch, as defined in the `custom_format` attribute. #### Variables available under custom\_format You can use these variables when defining the `custom_format`: - `%{id}`: Prints the sequence number for log message (uint64). - `%{pid}`: Prints the process id (int) - `%{time}`: Prints the time when log occurred. It uses the [Go time format](https://pkg.go.dev/time). For instance `%{time:2006/01/02 - 15:04:05.000}` - `%{level}`: Prints the log level - `%{program}`: Prints the command running - `%{message}`: Prints the application log message - `%{module}`: Prints the module - `%{color}`: Prints the ANSI color based on log level, the output can be adjusted to either use bold colors, e.g, `%{color:bold}` or to reset the ANSI attributes `%{color:reset}`. For example, you can customize your pattern like this: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "syslog": false, "stdout": true, "format": "custom", "custom_format": "[MY APP LOG] %{time:2006/01/02 - 15:04:05.000} %{color}▶ %{level:.6s}%{color:reset} %{message}" } } } ``` ### Customizing the access log Similarly, only on Enterprise you can customize how the access log prints. The following `access_log_format` values are available: - `default`: Uses `%{prefix} %{time} [AccessLog] |%{statusCode}| %{latencyMs} | %{clientIP} | %{method} %{path}\n` as pattern. - `httpdCommon`: Uses `%{clientIP} - - [%{time}] \"%{method} %{uri} %{proto}\" %{statusCode} -\n` as in the Apache HTTPd log format - `httpdCombined`: The Apache HTTPd Combined log format `%{clientIP} - - [%{time}] \"%{method} %{uri} %{proto}\" %{statusCode} - \"%{header.Referer}\" \"%{header.User-Agent}\"\n` - `json`: Uses `{\"prefix\":\"%{prefix}\", \"time\":\"%{time}\", \"status_code\":%{statusCode}, \"latency\":\"%{latency}\", \"client_ip\":\"%{clientIP}\", \"method\":\"%{method}\", \"path\":\"%{path}\"}\n` - `custom`: Write your own pattern, as defined in the `access_log_custom_format` attribute. #### Variables available to `access_log_custom_format` When the `access_log_format` is set to `custom`, you can use these variables under `access_log_custom_format` to specify your format: - `%{prefix}`: The value you have set under the `prefix` attribute. - `%{time}`: The time when the the access finished. The layout prints a format like 2006/01/02 - 15:04:05.000 - `%{statusCode}`: The response status code as given to the consumer - `%{latency}`: The latency in seconds. The default format is `.3fs` (3 decimal float + `s` for seconds). This computes the time of the request from beginning to end. - `%{latencyMs}`: The latency in milliseconds. The default format is `.3fms` (3 decimal float + `ms` for milliseconds). This computes the time of the request from beginning to end. - `%{latencyUs}`: The latency in microseconds. The default format is `.3fus` (3 decimal float + `us` for microseconds). This computes the time of the request from beginning to end. - `%{latencyNs}`: The latency in nanoseconds. The default format is `dns` (integer + `ns` for nanoseconds). This computes the time of the request from beginning to end. - `%{clientIP}`: The real IP of the client - `%{method}`: The HTTP verb used - `%{path}`: The endpoint path, does not include the query string (e.g., `/path/to/endpoint`) - `%{uri}`: The endpoint path and query string (e.g., `/path/to/endpoint?foo=bar&doe=baz`) - `%{host}`: The host of the URL - `%{header.xxx}`: The value of a specific header, where `xxx` is the header name. - `%{scheme}`: The scheme used (e.g., `http`, `https`, `ws`) - `%{jwt.xxx}`: The value of a specific claim in the token, where `xxx` is the claim name (only first level, non-nested, claims). - `%{query}`: The query strings passed in the request - `%{proto}`: The protocol used (e.g., `HTTP/1.0`, `HTTP/2`, etc.) For instance, you could **print the access log in JSON format** as follows: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "syslog": false, "stdout": true, "access_log_format": "json" } } } ``` Or you could have a log that includes the JWT subject, the authorization header and query strings as follows: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "syslog": false, "stdout": true, "access_log_format": "custom", "access_log_custom_format": "[AccessLog] %{prefix} %{time} | %{statusCode} | %{latencyMs} | %{clientIP} | %{method} %{scheme}://%{host}%{path}?%{query} %{query.bar} %{header.Authorization} %{jwt.sub}\n" } } } ``` ## Writing the log on a file Although logging on disk might impact software performance and is discouraged in high-throughput systems, you can still store the logs in a file. **Avoid redirecting the output** (e.g.: `krakend run > krakend.log`) and **use the *syslog* of your machine instead**. To setup logs on disk, you should consider the following steps: 1. Add the syslog configuration to yor `krakend.json` 2. Add a specific entry for krakend under `/etc/rsyslog.d/` 3. Optionally add log rotation ### 1. Syslog configuration ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "WARNING", "syslog": true, "stdout": true } } } ``` You might set the `stdout` to `false` if you don’t want to check on the console but only on the logs. ### 2. Add an entry to `rsyslog` The folder `/etc/rsyslog.d/` shows the different configurations of the system. We will create a new file `/etc/rsyslog.d/krakend.conf` and place this content inside: ``` local3.* -/var/log/krakend.log ``` If you are familiar with *syslog*, you change the `syslog_facility` to any other (local) value and adjust it in the file above. ### 3. KrakenD log rotation The syslog will take care of populating the log and can be used conveniently with the default system tools like **rotating the logs** with `logrotate`. Add a new configuration file `/logrotate.d/krakend` and add the content below: ``` /var/log/krakend.log { rotate 7 daily missingok delaycompress compress postrotate /usr/lib/rsyslog/rsyslog-rotate endscript } ``` --- # [Graylog GELF Logging Integration](https://www.krakend.io/docs/logging/graylog-gelf/) KrakenD supports sending structured events in GELF format to your Graylog Cluster thanks to the [krakend-gelf](https://github.com/krakend/krakend-gelf) integration. The setup of GELF is straightforward and requires to add **two components** in the configuration: - `telemetry/logging` to capture the logs - `telemetry/gelf` to format the logs The configuration you need to add is this, and explained below: ```json { "extra_config": { "telemetry/gelf": { "address": "myGraylogInstance:12201", "enable_tcp": false }, "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "syslog": false, "stdout": false } } } ``` The GELF configuration parameters for `telemetry/gelf` are: ##### Fields of GELF \* required fields ##### [`address`](https://www.krakend.io/docs/logging/graylog-gelf/#address "Click to copy link to address") * string The address (including the port) of your Graylog cluster (or any other service that receives GELF inputs). E.g., `myGraylogInstance:12201` ##### [`enable_tcp`](https://www.krakend.io/docs/logging/graylog-gelf/#enable_tcp "Click to copy link to enable_tcp") * boolean Set to false (recommended) to use UDP, or true to use TCP. TCP performance is worst than UDP under heavy load. Schema: [https://www.krakend.io/schema/v2.13/telemetry/gelf.json](https://www.krakend.io/schema/v2.13/telemetry/gelf.json "JSON schema definition") In addition, you must also add the `telemetry/logging`: ##### Fields of `"telemetry/logging"` \* required fields ##### [`level`](https://www.krakend.io/docs/logging/graylog-gelf/#level "Click to copy link to level") * What type of **reporting level** do you expect from the application? The options below go from more verbose to least. Use the `DEBUG` level in the development stages but not in production. Some components can add extra verbosity while in DEBUG mode and send **multiline content**, which is not always suitable for automated log parsing. Possible values are: `"DEBUG"` , `"INFO"` , `"WARNING"` , `"ERROR"` , `"CRITICAL"` ##### [`prefix`](https://www.krakend.io/docs/logging/graylog-gelf/#prefix "Click to copy link to prefix") string Adds the defined string at the beginning of every logged line, so you can quickly filter messages with external tools later on. It’s recommended to always add a prefix `[INSIDE BRACKETS]` to make use of predefined dashboards. ##### [`stdout`](https://www.krakend.io/docs/logging/graylog-gelf/#stdout "Click to copy link to stdout") boolean Set to true to send logs to stdout. Defaults to `false` ##### [`syslog`](https://www.krakend.io/docs/logging/graylog-gelf/#syslog "Click to copy link to syslog") boolean Set to true to send logs to syslog. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/telemetry/logging.json](https://www.krakend.io/schema/v2.13/telemetry/logging.json "JSON schema definition") --- # [Logstash Integration (JSON logs)](https://www.krakend.io/docs/logging/logstash/) The [Logstash](https://www.elastic.co/logstash/) integration prints **KrakenD application logs in JSON format** (not access logs) to ingest them and process them later. If you want to log using the Logstash standard via stdout, you need to add the `telemetry/logging` integration as a dependency. ## Configuration The configuration you need to enable Logstash is very simple: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "INFO", "prefix": "[KRAKEND]", "stdout": true, "syslog": false, "format":"custom", "custom_format": "%{message}" }, "telemetry/logstash":{ "enabled": true } } } ``` When the `format` of the logging format is `logstash` then the output of the application log (not the access log) is printed in JSON format, as follows: ``` {"@timestamp":"2022-06-15T15:37:02.619+00:00", "@version": 1, "level": "DEBUG", "message": "[SERVICE: Gin] Debug enabled", "module": "KRAKEND"} {"@timestamp":"2022-06-15T15:37:02.619+00:00", "@version": 1, "level": "INFO", "message": "Starting the KrakenD instance", "module": "KRAKEND"} {"@timestamp":"2022-06-15T15:37:02.619+00:00", "@version": 1, "level": "DEBUG", "message": "[ENDPOINT: /test] Building the proxy pipe", "module": "KRAKEND"} {"@timestamp":"2022-06-15T15:37:02.619+00:00", "@version": 1, "level": "DEBUG", "message": "[BACKEND: /404] Building the backend pipe", "module": "KRAKEND"} ``` The application **access log** will still show in plain text. For example, you might see the application logs in JSON and the **application logs** in JSON. For example: ``` {"@timestamp":"2022-06-15T15:37:07.619+00:00", "@version": 1, "level": "DEBUG", "message": "[SERVICE: Telemetry] Registering usage stats for Cluster ID GH9V15Rf22Zp3F4HBtvF9NkGO9WL7HKp8h7St7l+qc0=", "module": "KRAKEND"} [GIN] 2022/06/15 - 15:38:58 | 200 | 4.529µs | 172.17.0.1 | GET "/test/access" ``` If you don’t want to parse the access log on Logstash, you can remove it from the stdout using `disable_access_log` (see how to [remove requests from logs options](/docs/service-settings/router-options/#remove-requests-from-logs)). --- # [ELK Integration for Logging](https://www.krakend.io/docs/logging/elk-integration/) KrakenD can push logs to external services; a good example is an integration with the ELK Stack (**Elastic + Logstash + Kibana**). The ELK integration allows you to have KrakenD pushing logs to your Elastic server and **visualize them through a Kibana dashboard**. The Kibana dashboard lets you monitor the logging activity of the gateway and identify problems quickly. The included dashboard is a starting point that provides typical graphs and metrics, but you can extend it as per your needs and add other metrics to watch. ## ELK Configuration The configuration you need on your `krakend.json` to enable ELK integration is: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "extra_config": { "telemetry/logging": { "level": "DEBUG", "@comment": "Prefix should always be inside [] to keep the grok expression working", "prefix": "[KRAKEND]", "syslog": false, "stdout": true }, "telemetry/gelf": { "address": "logstash:12201", "enable_tcp": false } } } ``` There’s nothing else on KrakenD that you need to do. Where are the logs now? When you enable the ELK integration, you will stop seeing the application logs on `stdout` as KrakenD pushes them to the ELK stack. ## Logstash and Kibana configuration The configuration files you need for Logstash and Kibana can be downloaded from the [Telemetry Dashboards repository](https://github.com/krakend/telemetry-dashboards). [Download ELK configuration files](https://github.com/krakend/telemetry-dashboards "Download ELK configuration files") ### Logstash The `logstash.conf` file includes an example of a Logstash configuration. First, change the **hostname** of your Elasticsearch server and any custom ports you might use. Then, start Logstash with this configuration to properly ingest KrakenD logs. ### Kibana To import the Kibana dashboard included in the ELK repository above, execute the following command once your Kibana is up and running. Replace `localhost:5601` if needed: Term  ``` $curl -X POST "localhost:5601/api/saved_objects/_import" -H "kbn-xsrf: true" --form file=@dashboard.ndjson -H "kbn-xsrf: true" ``` ## ELK live demo If you want to see how this works, you can start the [KrakenD Playground](/docs/overview/playground/). --- # [Extended Metrics API](https://www.krakend.io/docs/telemetry/extended-metrics/) The **metrics API** offers a new `/__stats/` endpoint in a different port and contains a lot of metrics that you can scrape in a custom collector, or you can push them to [InfluxDB](/docs/telemetry/influxdb/). This component is unrelated to the [OpenTelemetry](/docs/telemetry/opentelemetry/) metrics, and they can coexist. Previous to the creation of OpenTelemetry, the combination of Influx and the metrics API, offered the older versions of [Grafana dashboard](/docs/telemetry/grafana/). ## Configuration In order to add the metrics API to your KrakenD installation add the `telemetry/metrics` namespace under `extra_config` in the root of your configuration file, e.g.: ```go { "version": 3, "extra_config": { "telemetry/metrics": { "collection_time": "60s", "proxy_disabled": false, "router_disabled": false, "backend_disabled": false, "endpoint_disabled": false, "listen_address": ":8090" } } } ``` ##### Fields of Extended metrics \* required fields ##### [`backend_disabled`](https://www.krakend.io/docs/telemetry/extended-metrics/#backend_disabled "Click to copy link to backend_disabled") boolean Skip any metrics happening in the backend layer. Disabling layers saves memory consumption but reduces visibility. Defaults to `false` ##### [`collection_time`](https://www.krakend.io/docs/telemetry/extended-metrics/#collection_time "Click to copy link to collection_time") string The time window to consolidate collected metrics. Metrics are updated in their internal counters all the time, but the `/__stats/` endpoint, or the Influx reporter, won’t see them updated until this window completes. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). Defaults to `"60s"` ##### [`endpoint_disabled`](https://www.krakend.io/docs/telemetry/extended-metrics/#endpoint_disabled "Click to copy link to endpoint_disabled") boolean When true do not publish the /\_\_stats/ endpoint. Metrics won’t be accessible via the endpoint but still collected (and you can send them to Influx for instance). Defaults to `false` ##### [`listen_address`](https://www.krakend.io/docs/telemetry/extended-metrics/#listen_address "Click to copy link to listen_address") string Change the listening address where the metrics endpoint is exposed. Defaults to `":8090"` ##### [`proxy_disabled`](https://www.krakend.io/docs/telemetry/extended-metrics/#proxy_disabled "Click to copy link to proxy_disabled") boolean Skip any metrics happening in the proxy layer (traffic against your backends). Disabling layers saves memory consumption but reduces visibility. Defaults to `false` ##### [`router_disabled`](https://www.krakend.io/docs/telemetry/extended-metrics/#router_disabled "Click to copy link to router_disabled") boolean Skip any metrics happening in the router layer (activity in KrakenD endpoints). Disabling layers saves memory consumption but reduces visibility. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/telemetry/metrics.json](https://www.krakend.io/schema/v2.13/telemetry/metrics.json "JSON schema definition") The structure of the metrics looks like this (truncated): Sample of /\_\_stats endpoint  ``` $curl http://localhost:8090/__stats { "cmdline": [ "/usr/bin/krakend", "run", "-c", "/etc/krakend/krakend.json", "-d" ], "krakend.router.connected": 0, "krakend.router.connected-gauge": 0, "krakend.router.connected-total": 0, "krakend.router.disconnected": 0, "krakend.router.disconnected-gauge": 0, "krakend.router.disconnected-total": 0, "krakend.service.debug.GCStats.LastGC": 1605724147216402400, "krakend.service.debug.GCStats.NumGC": 102, "krakend.service.debug.GCStats.Pause.50-percentile": 0, "krakend.service.debug.GCStats.Pause.75-percentile": 0, "krakend.service.debug.GCStats.Pause.95-percentile": 0, "krakend.service.debug.GCStats.Pause.99-percentile": 0, "krakend.service.debug.GCStats.Pause.999-percentile": 0, "krakend.service.debug.GCStats.Pause.count": 0, "krakend.service.debug.GCStats.Pause.max": 0, "krakend.service.debug.GCStats.Pause.mean": 0, "krakend.service.debug.GCStats.Pause.min": 0, "krakend.service.debug.GCStats.Pause.std-dev": 0, ... } } ``` ## Push metrics to InfluxDB You can accomplish it with the following snippet. ```json { "version": 3, "extra_config": { "telemetry/influx":{ "address":"http://influxdb:8086", "ttl":"25s", "buffer_size":0, "db": "krakend", "username": "your-influxdb-user", "password": "your-influxdb-password" }, "telemetry/metrics": { "collection_time": "30s", "listen_address": "127.0.0.1:8090" } } } ``` The properties of the `telemetry/influx` are as follows: ##### Fields of Telemetry via influx \* required fields ##### [`address`](https://www.krakend.io/docs/telemetry/extended-metrics/#address "Click to copy link to address") * string The complete url of the influxdb including the port if different from defaults in http/https. ##### [`buffer_size`](https://www.krakend.io/docs/telemetry/extended-metrics/#buffer_size "Click to copy link to buffer_size") integer The buffer size is a protection mechanism that allows you to temporarily store datapoints for later reporting when Influx is unavailable. If the buffer is `0`, reported metrics that fail are discarded immediately. If the buffer is a positive number, KrakenD creates a buffer with the number of datapoints set. When the buffer is full because the Influx server keeps failing, newer datapoints replace older ones in the buffer. ##### [`db`](https://www.krakend.io/docs/telemetry/extended-metrics/#db "Click to copy link to db") string Name of the InfluxDB database (Influx v1) or the bucket name (Influx v2). Defaults to `"krakend"` ##### [`password`](https://www.krakend.io/docs/telemetry/extended-metrics/#password "Click to copy link to password") string Password to authenticate to InfluxDB. In Influx v2, you also need to add grant access with `influx v1 auth`. ##### [`ttl`](https://www.krakend.io/docs/telemetry/extended-metrics/#ttl "Click to copy link to ttl") * TTL against Influx. Specify units using `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). ##### [`username`](https://www.krakend.io/docs/telemetry/extended-metrics/#username "Click to copy link to username") string Username to authenticate to InfluxDB. Schema: [https://www.krakend.io/schema/v2.13/telemetry/influx.json](https://www.krakend.io/schema/v2.13/telemetry/influx.json "JSON schema definition") See below how to configure InfluxDB, and you are ready to [publish a Grafana dashboard](/docs/telemetry/grafana/). ## Setting up Influx For **InfluxDB v2.x**, we have included in our [Telemetry Dashboards](https://github.com/krakend/telemetry-dashboards/) the files that create the authorization part. For **InfluxDB v1.x** (older) the process is straightforward and requires you nothing else than start an Influx instance with the desired configuration. ### Influx v2 If you use Docker, you can start InfluxDB as part of a docker compose file. You need to specify in the configuration above the same data you used to run InfluxDB. For instance, the following `docker-compose.yml` sets the credentials you need to reflect in the KrakenD configuration. ```yaml version: "3" services: influxdb: image: influxdb:2.4 environment: - "DOCKER_INFLUXDB_INIT_MODE=setup" - "DOCKER_INFLUXDB_INIT_USERNAME=krakend-dev" - "DOCKER_INFLUXDB_INIT_PASSWORD=pas5w0rd" - "DOCKER_INFLUXDB_INIT_ORG=my-org" - "DOCKER_INFLUXDB_INIT_BUCKET=krakend" - "DOCKER_INFLUXDB_INIT_RETENTION=1w" - "DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=my-super-secret-auth-token" ports: - "8086:8086" volumes: - "./config/telemetry/influx:/docker-entrypoint-initdb.d" krakend: image: krakend:2.13 volumes: - ./krakend:/etc/krakend ports: - "8080:8080" ``` In the fields `db`, `username`, and `password` of the component configuration reflect the same values as in `DOCKER_INFLUXDB_INIT_BUCKET`, `DOCKER_INFLUXDB_INIT_USERNAME`, and `DOCKER_INFLUXDB_INIT_PASSWORD` accordingly. The Influx **volume** below must have the contents of the [influx initdb script](https://github.com/krakend/telemetry-dashboards/tree/main/influx), that it will create the authorization needed to let KrakenD push the metrics. #### Manual configuration If you don’t want to use the automated docker compose above, the manual steps to create the auth are: Connect to your influx Term  ``` $docker exec -it influxdb /bin/bash ``` Create a configuration: Term  ``` $influx config create --config-name krakend-config \ --host-url http://localhost:8086 \ --org my-org \ --token my-super-secret-auth-token \ --active ``` Make sure to replace the values below as follows: - `config-name` is a random name to identify your configuration - `org` Your organization name as written during the setup of InfluxDB - `token` The one you started influx with - `host-url` The address where the influxdb is running (inside Docker is as shown) Access with a web browser to `http://localhost:8086` and select **BUCKETS** from the UI. You’ll see that there is an ID next to the bucket you created during the Setup. **Copy the bucket ID**. ![Getting a token](/images/documentation/influx_bucket.png) And now launch the last command in the shell: Term  ``` $influx v1 auth create \ --read-bucket b492e6f8f3b13aaa \ --write-bucket b492e6f8f3b13aaa \ --username krakend-dev ``` Replace the ID of the buckets above with the ID you just copied, and the username as in the docker compose. The shell will ask for your password. Now your configuration should work and start sending data to InfluxDB: ```json { "version": 3, "extra_config": { "telemetry/metrics": { "collection_time": "60s", "listen_address": ":8090" }, "telemetry/influx": { "address": "http://localhost:8086", "ttl": "25s", "buffer_size": 100, "db": "krakend_db", "username": "user", "password": "password" } } } ``` Make sure to type in `db` the bucket name you created on InfluxDB and the `username` and `password` as well. ### Influx v1 When using InfluxDB v1.x, you need to specify in the configuration above the same data you used to run InfluxDB. For instance, the following docker compose sets the credentials you need to reflect in the KrakenD configuration. ```yaml version: "3" services: influxdb: image: influxdb:1.8 environment: - "INFLUXDB_DB=krakend" - "INFLUXDB_USER=krakend-dev" - "INFLUXDB_USER_PASSWORD=pas5w0rd" - "INFLUXDB_ADMIN_USER=admin" - "INFLUXDB_ADMIN_PASSWORD=supersecretpassword" ports: - "8086:8086" krakend: image: krakend:2.13 volumes: - ./krakend:/etc/krakend ports: - "8080:8080" ``` In the fields `db`, `username`, and `password` of the component configuration reflect the same values as in `INFLUXDB_DB`, `INFLUXDB_USER`, and `INFLUXDB_USER_PASSWORD` accordingly. --- # [API Health Check](https://www.krakend.io/docs/service-settings/health/) The health endpoint (or the **ping endpoint**) allows you to query KrakenD to find out if it is ready to accept connections or not. When KrakenD is up and running correctly, it exposes a `/__health` endpoint returning a `200` HTTP status code. It works automatically and without adding any specific configuration block. Nevertheless you can do customizations to it. ## Health check response When you query the `/__health` endpoint, you should expect a `200` response code **or no response at all**. There are no other status codes that you can receive from the health, as it reflects a binary answer: it’s working, or it’s not. So make sure to check for a `200` when you monitor the health of the service. The content of the health endpoint provides extra information, but you don’t need to parse its content to make automated decisions. For example, the content could look like this: k8s check endpoint  ``` $curl http://localhost:8080/__health { "agents": { "some-agent": "2022-01-19 18:25:17.00225307 +0100 CET m=+0.031662879" }, "now": "2022-01-19 18:38:38.084402465 +0100 CET m=+30.674738658", "status": "ok" } ``` There are three keys inside the response: - `status` with an `ok` value simply tells you that the API is processing HTTP requests correctly. There is no other possible state if the server is up. - `agents` is a map of all [Async Agents](/docs/async/) you have running on KrakenD. The map will be empty if you don’t use them. When agents are running, the value shows the time of the last working ping. - `now` is the current time in the server. ## Customizing the health endpoint You might want to disable the `/__health` endpoint, rename it, or disable its access log. You can set several parameters as [`router` options](/docs/service-settings/router-options/) that change the behaviour of the `/__health` endpoint: ##### Fields of `"router"` \* required fields ##### [`disable_health`](https://www.krakend.io/docs/service-settings/health/#disable_health "Click to copy link to disable_health") boolean When true you don’t have any exposed health endpoint. You can still use a TCP checker or build an endpoint yourself. Defaults to `false` ##### [`health_path`](https://www.krakend.io/docs/service-settings/health/#health_path "Click to copy link to health_path") string The path where you’d like to expose the health endpoint. Defaults to `"/__health"` ##### [`logger_skip_paths`](https://www.krakend.io/docs/service-settings/health/#logger_skip_paths "Click to copy link to logger_skip_paths") array of strings Defines the set of paths that are removed from the logging. Schema: [https://www.krakend.io/schema/v2.13/router.json](https://www.krakend.io/schema/v2.13/router.json "JSON schema definition") ### Example: Disable the health endpoint ```json { "version": 3, "extra_config": { "router": { "@comment": "The health endpoint is no longer available", "disable_health": true } } } ``` ### Example: Rename the health endpoint ```json { "version": 3, "extra_config": { "router": { "@comment": "The health endpoint is now under /health instead of /__health", "health_path": "/health" } } } ``` ### Example: Disable the access log of /\_\_health ```json { "version": 3, "extra_config": { "router": { "@comment": "The health endpoint checks do not show in the logs", "logger_skip_paths": [ "/__health" ] } } } ``` ## Custom response formats of the health endpoint If you’d like to have a health endpoint with a custom response, the simplest solution is to use [stub data](/docs/endpoints/static-proxy/) to alter the existing response. Avoid adding dependencies in your health check When setting custom health checks, try not to use external backends connected to databases to determine if KrakenD has to be reloaded or not. A custom health configuration could look like this: ```json { "version": 3, "port": 8080, "endpoints": [ { "endpoint": "/health", "extra_config": { "proxy": { "static": { "data": { "custom": "response", "foo": "bar" }, "strategy": "always" } } }, "backend": [ { "url_pattern": "/__health", "host": [ "http://localhost:8080" ] } ] } ] } ``` In this configuration, KrakenD connects to itself, but instead of returning the content of the internal health endpoint, it sets the `data` defined in the static structure. Notice that the listening `port` in the configuration and the `host` match your deployed KrakenD. The response content of this custom `/health` endpoint is: k8s check endpoint  ``` $curl http://localhost:8080/health {"custom": "response","foo": "bar"} ``` The `/__health` endpoint will still be available, but you can rename it, or if your backend does not use it, disable it. --- # [KrakenD Development Tools (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/developer/) There are some tools that make your life easier when developing KrakenD. Here is a miscellaneous list of features that might help you speed up KrakenD adoption and development. ## The debug flags There are a lot of components that offer a `debug` or similar flag that makes components go extra verbose, so you can see what is going on when implementing new functionalities. ## IDE integration Autocomplete, automatic validation as you type, showing documentation while hovering an attribute, explanation of errors, are features that you get automatically while working with KrakenD with certain editors. There is nothing specific you need to install to have autocompletion. See [IDE integration](/docs/enterprise/developer/ide-integration/) ## HTTP Logger The HTTP logger is a response dumper that allows you to write the backend request and its response into a file, to help you debug your processes during development. [HTTP Logger: Dump backend requests and responses](/docs/enterprise/backends/http-logger/) ## The debug and echo endpoints The debug and echo endpoints can be used as fake backends and are 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, or returned as the response. They are safe to be used even in production. See [the `/__debug/` endpoint](/docs/enterprise/endpoints/debug-endpoint/), and the [`/__echo/` endpoint](/docs/enterprise/endpoints/echo-endpoint/). ## Wildcard routes The wildcard functionality allows you that URLs starting with known patterns forward to a common endpoint without declaring all possible routes. For instance, you want to forward all traffic to /foo/* (plus any nesting levels) to a specific backend. [Wildcard routes](/docs/enterprise/endpoints/wildcard/) ## Mock responses Create an endpoint pointing to an unfinished backend where its functionality is not in production yet, but your client application needs to go ahead the backend developers and start using the static responses. Or start serving static content directly - [Mock data](/docs/enterprise/endpoints/static-proxy/) - [Load static content from disk](/docs/enterprise/endpoints/serve-static-content/) ## Integration tests In addition to checking the syntax of your KrakenD configuration and ensuring that the gateway can start, you can run integration tests to guarantee that all the active software components from beginning to end have the expected flow and that the gateway returns what you planned. [Automated End-to-End (E2E) tests](/docs/enterprise/developer/integration-tests/) ## Import and export OpenAPI The OpenAPI generators are command-line utilities embedded in the KrakenD binary, offering OpenAPI import and export capabilities [Import and Export OpenAPI](/docs/enterprise/developer/openapi/) ## Import KrakenD in Postman The Postman Swagger generator is a command line utility embedded in the KrakenD binary, offering the automatic generation of Postman collections based on the KrakenD configuration. [Generate Postman collections](/docs/enterprise/developer/postman/) ## Generate graphs from configuration The `krakend.json` file whether it is a single file or the result of using the flexible configuration, it might be a complex file. A generated image of the configuration might help you understand better the running configuration. ![config2dot example](/images/documentation/config2dot.png) --- # [Custom developer tools](https://www.krakend.io/docs/developer/) There are some additional resources that might help you when developing with KrakenD. These tools are meant to be used only in development and **never in production**, they are **not bundled with KrakenD** and are separate components that you must compile. ## Hot reload the configuration There is an additional KrakenD Docker image using Reflex to watch the configuration directory and reload KrakenD when there are changes. This is very convenient while you are developing as it allows you to test new changes without having to restart manually and making the process less tedious. You can use the Docker image `docker pull devopsfaith/krakend:watch` [More information in our blog post](/blog/reloading-the-krakend-configuration/) ## Generating an image with the configuration The [config2dot](https://github.com/krakend/krakend-config2dot) is a tool to create graphs automatically after reading your configuration file `krakend.json`. For instance: ![config2dot example](/images/documentation/config2dot.png) ## Debugging the activity ### krakend-memviz Adds a [DOT](https://en.wikipedia.org/wiki/DOT_%28graph_description_language%29) file exporter of request/response snapshots to your proxy stack for debug and development purposes. Do not use this in production as it will kill your performance. ### krakend-spew Dumps every entity seen in the pipe: requests and responses passing through the whole stack. Do not use this in production as it will kill your performance. Dumps are stored in files like `__.txt`. E.g: ``` $ ls 2,0K 25 sep 19:12 backend_L3VzZXJzL3t7Lk5pY2t9fQ==_1537895547814979000.txt 1,8K 25 sep 19:12 backend_LzIuMC91c2Vycy97ey5OaWNrfX0=_1537895547800941000.txt 92K 25 sep 19:12 client_aHR0cHM6Ly9hcGkuYml0YnVja2V0Lm9yZy8yLjAvdXNlcnMva3BhY2hh_1537895547798571000.txt 92K 25 sep 19:12 client_aHR0cHM6Ly9hcGkuYml0YnVja2V0Lm9yZy8yLjAvdXNlcnMva3BhY2hh_1537895547800824000.txt 104K 25 sep 19:12 client_aHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9rcGFjaGE=_1537895547814647000.txt 1,9K 25 sep 19:12 client_basic_aHR0cHM6Ly9hcGkuYml0YnVja2V0Lm9yZy8yLjAvdXNlcnMva3BhY2hh_1537895547796264000.txt 1,9K 25 sep 19:12 client_basic_aHR0cHM6Ly9hcGkuYml0YnVja2V0Lm9yZy8yLjAvdXNlcnMva3BhY2hh_1537895547798755000.txt 2,7K 25 sep 19:12 client_basic_aHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9rcGFjaGE=_1537895547812621000.txt 2,3K 25 sep 19:12 proxy_L25pY2svOm5pY2s=_1537895547815244000.txt 66K 25 sep 19:12 router_L25pY2sva3BhY2hh_1537895547816402000.txt ``` --- # [Debug Endpoint](https://www.krakend.io/docs/endpoints/debug-endpoint/) The `/__debug/` endpoint is available when you start the server with the `-d` flag, or when you add the `debug_endpoint=true` in the configuration. The endpoint can be used as a **fake backend** if you use as `host` KrakenD itself, 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: ```json { "message": "pong" } ``` When developing, add an additional `backend` pointing to KrakenD itself (`"host": "http://localhost:8080"`) with the `/__debug/` endpoint in its `url_pattern`, so you can see exactly what headers and query string parameters your backends are receiving. The debug endpoint might save you much trouble, as your application might not work when specific headers or parameters are not present. Maybe you are relying upon what your client is sending, but this is not what the gateway is forwarding. Remember: this is not a proxy. For instance, your client might be sending a `Content-Type` or `Accept` header and these are perhaps necessary for the proper functioning of your backend, but unless these are recognized headers by the gateway (they are in `input_headers`), they are not going to reach the backend ever. Seeing the specific headers and parameters in the log clears all the doubts, and you can reproduce the call and conditions easily. ## Configuration To enable the debug endpoint add the following in the configuration: ##### Debug endpoint \* required fields ##### [`debug_endpoint`](https://www.krakend.io/docs/endpoints/debug-endpoint/#debug_endpoint "Click to copy link to debug_endpoint") boolean Enables the `/__debug/` endpoint for this configuration. You can safely enable it in production. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/krakend.json](https://www.krakend.io/schema/v2.13/krakend.json "JSON schema definition") Or to do it during runtime, add `-d` when starting the server. E.g., `krakend run -dc krakend.json` ## Debug endpoint 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: ```json { "version": 3, "port": 8080, "host": ["http://127.0.0.1:8080"], "debug_endpoint": true, "endpoints": [ { "endpoint": "/default-behavior", "backend": [ { "@comment": "IMPORTANT: Notice that the /__debug uses the KrakenD host itself", "host": ["http://127.0.0.1:8080"], "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.13.8] 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 ``` Now let’s repeat the same request but to the `/optional-params` endpoint: Recognized and forwarded query strings  ``` $curl -i 'http://localhost:8080/optional-params?a=1&b=2&c=3' ``` In the KrakenD log we can see now that the `User-Agent` and `Accept` are present (as they are implicitly sent by curl), and that `a` and `b` are reaching the backend (but not `c`): ``` DEBUG: Method: GET DEBUG: URL: /__debug/optional?a=1&b=2 DEBUG: Query: map[a:[1] b:[2]] DEBUG: Params: [{param /optional}] DEBUG: Headers: map[User-Agent:[curl/7.54.0] Accept:[*/*] X-Forwarded-For:[::1] Accept-Encoding:[gzip]] DEBUG: Body: [GIN] 2018/11/27 - 22:33:23 | 200 | 122.507µs | ::1 | GET /__debug/optional?a=1&b=2 [GIN] 2018/11/27 - 22:33:23 | 200 | 542.483µs | ::1 | GET /optional-params?a=1&b=2&c=3 ``` Finally, let’s note what happens when you inject mandatory query strings in the backend definition, the `/mandatory/{variable}` endpoint: Mandatory query strings  ``` $curl -i 'http://localhost:8080/mandatory/foo?a=1&b=2&c=3' ``` As we can see, the backend includes the `?mandatory=foo` variable that was written manually in the backend definition: ``` DEBUG: Method: GET DEBUG: URL: /__debug/qs?mandatory=foo DEBUG: Query: map[mandatory:[foo]] DEBUG: Params: [{param /qs}] DEBUG: Headers: map[X-Forwarded-For:[::1] Accept-Encoding:[gzip] User-Agent:[KrakenD Version 0.7.0]] DEBUG: Body: [GIN] 2018/11/28 - 19:44:19 | 200 | 210.434µs | ::1 | GET /__debug/qs?mandatory=foo [GIN] 2018/11/28 - 19:44:19 | 200 | 1.975103ms | ::1 | GET /mandatory/foo?a=1&b=2&c=3 ``` --- # [Automated integration tests](https://www.krakend.io/docs/developer/integration-tests/) In addition to checking the syntax of your KrakenD configuration and make sure that the gateway can start, you can run **integration tests** to make sure that the gateway returns the expected content from the consumed backends. to make sure all endpoints are properly connected and that they reply with the expected content. To do that, you can use the library that KrakenD is relying on to run its **integration tests**, and complement the unit testing battery. KrakenD comes with a small program that lets you define a folder with tests and execute them all at once, reporting any failures it found. The way this library works is quite simple. You create a folder with all the different specs you want to tested, one per file. Each file is a `.json` file with two keys: - `in`: The parameters used to build the request to a running KrakenD with your configuration - `method`: The request method - `url`: The full URL to the endpoint you want to test - `header`: An optional map of header to include in the request - `body`: An optional payload you can send in the request as data - `out`: The expected response from the gateway - `status_code` (*integer*): The expected status code - `body`: The returned body by the response as a string, or as JSON object. - `header`: Any header included in the response. When the value is empty it means that you don’t expect that header. For instance: ```json { "in": { "method": "GET", "url": "http://localhost:8080/detail_error", "header": { "X-Header": "something" } }, "out": { "status_code": 200, "body": { "message": "pong" }, "header": { "content-type": ["application/json; charset=utf-8"], "Cache-Control": [""], "X-Krakend-Completed": ["true"] } } } ``` In the example above, the response must contain the `content-type` and `X-Krakend-Completed` with the specified values, and the `Cache-Control` cannot be present. You must build the go binary that allows you to run the tests. ## Installing the integration tests tool To install the integration tests you only need to run the following lines in any machine or Docker container with go installed: Installing the integration tool  ``` $go install github.com/krakend/krakend-ce/v2/cmd/krakend-integration@v2.13.8 ``` After this you should have a new binary `krakend-integration` in your PATH. To use it you will to execute: Term  ``` $krakend-integration -krakend_bin_path ./krakend \ -krakend_config_path ./krakend.json \ -krakend_specs_path ./specs ``` The three parameters of the binary are: - `-krakend_bin_path`: The path to the KrakenD binary. Defaults to `./krakend`, but if KrakenD is in your PATH then you can use `krakend`. - `-krakend_config_path`: The path to the KrakenD configuration file that contains all endpoints you want to test. - `-krakend_specs_path`: The path to the folder containing all your specifications. These are all the tests that you want to run against KrakenD and check its output. [Integration tests](https://github.com/krakend/krakend-ce/tree/master/tests "Integration tests") [Fixtures](https://github.com/krakend/krakend-ce/tree/master/tests/fixtures "Fixtures") --- # [Echo Endpoint Configuration](https://www.krakend.io/docs/endpoints/echo-endpoint/) The `/__echo/` endpoint is a developer tool to help you debug configurations. It works similarly to the [`/__debug/` endpoint](/docs/endpoints/debug-endpoint/), but instead of printing the requests in the log and returning a `{"message": "pong"}`, they are printed in the response. It replies with an object containing all the request details, and you can use it as an endpoint or backend. As KrakenD has a [zero-trust](/docs/design/zero-trust/) approach, you will find out the exact information that passes through in this endpoint. Ends in slash! Notice that the `/__echo/` endpoint has two slashes in its definition. ## Configuration To enable the `/__echo/` endpoint, you should add in the configuration (service level) the flag `echo_endpoint`, and then use it directly by calling `http://krakend:8080/__echo/` or by adding it as a `backend` in any endpoint. ##### Echo endpoint \* required fields ##### [`echo_endpoint`](https://www.krakend.io/docs/endpoints/echo-endpoint/#echo_endpoint "Click to copy link to echo_endpoint") boolean Enables the `/__echo/` endpoint for this configuration, that returns information about the incoming request. When using /\_\_echo as a backend you can check the actual headers and content a backend receives after all the zero-trust filtering. Defaults to `false` Schema: [https://www.krakend.io/schema/v2.13/krakend.json](https://www.krakend.io/schema/v2.13/krakend.json "JSON schema definition") When used as a backend, you have a **fake backend** that is very useful for seeing the interaction between the gateway and the backends and testing all sorts of KrakenD components. ### Response fields Given a request following the format `[scheme:][//[userinfo@]host][/]path[?query][#fragment]`, the `/__echo/` endpoint will answer with the following structure: ```json { "req_uri": "/__echo/foo/bar/vaz?q=foo#fragment", "req_uri_details": { "user": "userinfo", "host": "krakend:8080", "path": "/__echo/foo/bar/vaz", "query": "?q=foo", "fragment": "fragment" }, "req_method": "POST", "req_querystring": { "q": ["foo"] }, "req_body": { "@comment": "The correct parsing of the body is not guaranteed as its content is unknown (and even binary)" }, "req_headers": { "Content-Type": ["application/json"] } } ``` ## Echo endpoint example The most beneficial case is when you add KrakenD itself as another backend using the `/__echo/` endpoint. Then, you can see exactly what headers and query string parameters your backends receive in the responses. To test it, save the content of this file in a `krakend.json` and start the server: ```json { "version": 3, "port": 8080, "echo_endpoint": true, "endpoints": [ { "endpoint": "/test/{var}", "backend": [ { "host": ["http://127.0.0.1:8080"], "url_pattern": "/__echo/{var}" } ] } ] } ``` **Default behavior:** Ignore query strings by default  ``` $curl -i -H'Test: foo' 'http://localhost:8080/test/one?a=1&b=2&c=3' ``` In the response, you will see that `a`, `b`, and `c` do not appear, neither the sent headers. The `curl` command automatically sends the `Accept` and `User-Agent` headers, but they are not in the backend call either. Instead, you see the KrakenD User-Agent as set by the gateway. Play now with the [parameter forwarding](/docs/endpoints/parameter-forwarding/), such as the `input_headers` and `input_query_strings`, and repeat the test. --- # [HTTP Logger Integration (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/backends/http-logger/) The HTTP logger is a response dumper that allows you to write the backend request and its response into a file, to help you debug your processes during development. A development tool Although it works, we discourage you from using this plugin in production for the following reasons: 1. **Performance**: The throughput of the filesystem is usually slower than the network. This plugin writes two files on the disk for each request (consuming your disk space and I/O). 2. **Privacy**: Your backend responses are saved as they are in a plain file. If you have sensitive data, you might breach your company policies. ## Response logging configuration The response dumper is an HTTP-client plugin, and you need to load it in the `backend` section. Notice the different elements you need to include in the configuration: ```json { "version": 3, "plugin": { "pattern": ".so", "folder": "/opt/krakend/plugins/" }, "endpoints": [ { "endpoint": "/health", "output_encoding": "no-op", "backend": [ { "url_pattern": "/__health", "host": ["http://localhost:8080"], "encoding": "no-op", "extra_config": { "plugin/http-client": { "name": "http-logger", "path": "./dumps", "file_prefix": "health" } } } ] } ] } ``` The configuration flags relative to the plugin are: - `path` (*string*): The folder where to dump the responses. The **folder destination must exist and be writeable** by the `krakend` user (or any other user you have chosen to start the process). Otherwise, the plugin will fail to save the dumps. So in the example above, a `mkdir -p dumps` is needed to make the plugin work correctly. - `file_prefix` (*string*): As all files will use in the name the timestamp to avoid concurrency problems, you can add a prefix to identify which backend wrote that response. ### Dump file format For each request to KrakenD, two files are written in the directory with the following naming convention: - `${file_prefix}_request_${timestamp}.txt` - `${file_prefix}_response_${timestamp}.txt` Each file contains the request and the response respectively using the **HTTP format**, as if you did a `curl -i` which contains the headers and the body. So for the example above, you would have: Request to your backend (e.g: `health_request_1649753368.txt`) ```http GET /__health HTTP/1.1 Host: localhost:8082 User-Agent: KrakenD Version 2.0.0 X-Forwarded-For: 127.0.0.1 X-Forwarded-Host: localhost:8082 Accept-Encoding: gzip ``` Response from your backend (e.g: `health_response_1649753368.txt`): ```http HTTP/1.1 200 OK Content-Length: 92 Content-Type: application/json; charset=utf-8 Date: Tue, 12 Apr 2022 08:49:28 GMT {"agents":{},"now":"2022-04-12 10:49:28.90471549 +0200 CEST m=+428.707781074","status":"ok"} ``` --- # [Import and Export OpenAPI definitions (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/developer/openapi/) The OpenAPI integration is a series of commands embedded in the KrakenD binary and additional metadata you can add in the configuration to enrich the documentation. KrakenD offers OpenAPI import and export capabilities for OAS3 and Swagger v2 (`--legacy` mode), so you can autogenerate documentation or create a gateway configuration from one or multiple OpenAPI specs. The `openapi` command supports the following functionalities: - `openapi export`: Creates OpenAPI documentation by reading the KrakenD configuration. - `openapi import`: Creates a KrakenD configuration from OpenAPI specification files. - `openapi serve`: Starts a KrakenD server and an **OpenAPI UI** by reading the specification of an OpenAPI document (import+run). ## Exporting to OpenAPI To **generate OpenAPI documentation**, the command needed is `krakend openapi export`. These are the different supported options: Generating the documentation  ```` $krakend openapi export -h ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.6 Generates the OpenAPI descriptor for the gateway based on the configuration file. Usage: krakend openapi export [flags] Examples: krakend openapi export --audience audience --config config.json --out openapi.json Flags: -a, --audience string Filter out endpoints not matching the given 'audience'. -c, --config string Path to a KrakenD configuration file. -h, --help help for export -L, --legacy Use old Swagger v2 -o, --out string Path to the generated result. (default "out.json") -j, --skip-jsonschema Skips including json-schema from the KrakenD component ```` The critical flags are `--config`, pointing to your `krakend.json` file, and `--out` as the final output OpenAPI file in JSON format. If you use [Flexible Configuration](/docs/enterprise/configuration/flexible-config/), you need to pass as `--config` the file delivered by `FC_OUT`. After executing the command, KrakenD will generate a basic version of the OpenAPI specification containing all the declared endpoints in the gateway. To enrich the OpenAPI output, you can [add OpenAPI information to your endpoints](/docs/enterprise/endpoints/openapi/) with additional metadata. The metadata allows you to write user-friendly documentation in the configuration that will be rendered on the final OpenAPI document. ### Filtering which endpoints are documented Sometimes you don’t want to document all the existing endpoints in the gateway, but only a subset. Using the `--audience` flag, you can filter which endpoints appear in the documentation during the generation. This flag selects the audiences [as defined in each endpoint](/docs/enterprise/endpoints/openapi/). You can define multiple audiences and execute the command several times, allowing you to share different specifications. For instance, why would you document specific endpoints intended for internal usage only to final customers? ### Adding OpenAPI documentation in the CI/CD process You can add the OpenAPI generation command to your existing build pipeline. You can save the OpenAPI output somewhere else or keep it inside KrakenD to serve it statically. If you want to serve the OpenAPI file from KrakenD, use the [Static Filesystem](/docs/enterprise/endpoints/serve-static-content/) option. For instance, the following line would generate the OpenAPI file: Generate OpenAPI during the build  ``` $mkdir -p openapi && krakend openapi export -c krakend.json -o openapi/openapi.json ``` And then you could add in the configuration the option to serve it as `http://krakend/oas3/openapi.json`: ```json { "version": 3, "extra_config": { "plugin/http-server": { "name": [ "static-filesystem" ], "static-filesystem": { "prefix": "/oas3/", "path": "/etc/krakend/openapi/" } } } } ``` ## Importing from OpenAPI If what you want, on the other hand, is to generate the KrakenD configuration when you have one or more existing OpenAPI files, then the command you need to use is `krakend openapi import`: Importing an OpenAPI file  ```` $krakend openapi import -h ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.6 Generates a KrakenD configuration from your OpenAPI specification. Usage: krakend openapi import [flags] Examples: krakend openapi import --spec openapi.(json|yaml) --out krakend.json --mock false --lint true Flags: -b, --base-config string Path to a base KrakenD configuration file to extend with the OpenAPI specifications -h, --help help for import -L, --legacy Use old Swagger v2 -l, --lint Lint the generated configuration. -M, --merge-strategy string Sets how to resolve conflicts on endpoints that already exist in the base configuration: discard (base endpoint untouched) | replace (base endpoint lost) | update (adds info missing in the extra_config) | openapi (updates documentation/openapi only) -m, --mock Creates an API with autogenerated mocked responses. -o, --out string Path to the generated result. (default "out.json") -s, --spec string Path to an OpenAPI specification file. -d, --spec-dir string Path to a directory where OpenAPI spec files are stored. (--legacy/-L not supported) ```` The `--spec` flag must point to a valid JSON or YAML file with the OpenAPI specification. For instance, you could generate a `krakend.json` from an existing `openapi.yaml` like this: Simple import  ``` $krakend openapi import -s openapi.yaml -o krakend.json ``` If you have multiple OpenAPI files to import and merge into a final configuration, pass a directory containing OpenAPI specs in YAML or JSON formats. For instance: Import multiple OpenAPI files from directory  ``` $krakend openapi import -d dir_with_openapis/ -o krakend.json ``` When you generate the configuration, KrakenD includes the following: - All the endpoints declared in the files with `no-op` encoding (reverse proxy). Of course, it would be best to use another encoding to avoid coupling and use data manipulation options, but that decision is up to you and your needs. - If the OpenAPI contains a JSON schema validation, the usage of the endpoints will enforce its schema as well using [json-schema request validation](/docs/enterprise/endpoints/json-schema/). - If there are examples and you generate them using `--mock`, the examples are included as a static response until you implement the final backend. - All endpoints point to the host defined in the first OpenAPI directory (alphabetically) or an empty host if not defined, except for mocked data that KrakenD points to itself. - Security schemas (basic auth, API keys) are defined with a template for you to fill with your identity server or credentials information In addition, except if you use Swagger v2 format with the flag `--legacy`: - Required query strings and required headers are added to the endpoint - JWT in the security schemas ### The base configuration When using the `-b` or `--base-config`, whether you are doing one import or loading a whole directory of OpenAPI specs, the file passed in this flag is used as the base configuration for the final configuration file. You can set here any global settings like telemetry, logging, the listening `port`, the configuration `name`, the default `host`, or a few endpoints in addition to the imported specs. The base configuration should also include any component schemas referenced during file import. Both the base configuration and the imported OpenAPI files merge in the final render. Import multiple OpenAPI files  ``` $krakend openapi import -d dir_with_openapis/ -o krakend.json -b base_krakend.json ``` The `base_krakend.json` file could look like this: ```json { "version": 3, "name": "Master configuration", "extra_config": { "telemetry/logging": { "level": "DEBUG", "prefix": "[KRAKEND]", "stdout": true, "syslog": false }, "endpoints": [ { "endpoint": "/health", "backend": [ { "url_pattern": "/infra/health" } ] } ] } } ``` ### Options to resolve conflicts on merged files But what happens with conflicts? You may have a base configuration that defines endpoints that are defined in the same file(s) you are importing. A conflicting endpoint occurs when the combination of endpoint and method is identical in both the base and imported file. The flag `--merge-strategy` (`-M` for short) allows you to specify how to resolve conflicts of endpoints that collide. These are the options and behaviors: - `--merge-strategy update` (default): Leaves the endpoint as found in the base configuration but updates the information missing in the `extra_config` of the endpoint. - `--merge-strategy discard`: Discards the endpoint imported, leaving the base endpoint untouched. - `--merge-strategy replace`: Replaces the endpoint entirely with the content found in the imported file - `--merge-strategy openapi`: Leaves the endpoint as found in the base configuration, but replaces the content of the `documentation/openapi` namespace. This is useful **when the source of truth of the OpenAPI specification is in the imported file**. ### x-krakend metadata In addition to the base configuration file, you can add KrakenD metadata to your OpenAPI files, which is used to generate the configuration. The OpenAPI spec is designed to document APIs as a consumer, but if you want to configure a gateway from an OpenAPI, you will need a lot more information that is irrelevant to the OpenAPI spec (the consumer). For instance, let’s say that you want to add a service rate limit to KrakenD every time you import from an OpenAPI file, or that you need to change the upstream service for a specific consumer path. In cases like this, the consumer still needs to use the same host, but the gateway won’t! In scenarios like this, you can add `x-krakend-` (**in lowercase**) plus the KrakenD configuration attributes you want to set. For instance, the following OpenAPI specification adds an `input_headers` and the full `backend` definition to the path `/pets`: ```json { "paths": { "/pets": { "get": { "x-krakend-input_headers": [ "Authorization" ], "x-krakend-backend": [ { "method": "POST", "url_pattern": "/shop/pet" } ], "summary": "List all pets", "operationId": "listPets" } } } } ``` The metadata attributes are accepted for service-level attributes and endpoints. Notice that you only set the `x-krakend-` key for the attribute you want to define, and anything nested after that level does not need the `x-krakend-` prefix. You can combine the usage of this metadata with a base configuration and the `x-krakend` metadata. Be aware that if you define the same keys, the metadata overwrites any existing keys in the base configuration. ### Multiple OpenAPI file import and merging Suppose several teams generate their OpenAPI specs, and you want to merge all contracts in a single endpoints array. Then, when passing multiple OpenAPI specs during the import, KrakenD merges all the endpoints from the included specifications at once. If there are **repeated endpoints** in different files, KrakenD will load them all in the final configuration, and you will need to manually resolve the conflict. Import multiple OpenAPI files  ``` $krakend openapi import -d dir_with_openapis/ -o krakend.json ``` ### Mock data Suppose your backend services still need to be created or are not ready. In that case, you can import the OpenAPI configuration to KrakenD and let KrakenD respond with mock data so that you can reply to HTTP calls immediately. To create the mock data, pass the `-m` or `--mock` flag: Mock data  ``` $krakend openapi import -s ~/Downloads/openapi.yaml -o krakend.json -m ``` ## Serve an OpenAPI spec The `openapi serve` command receives the OpenAPI file you want to start as input and starts a KrakenD server with its defined endpoints. In addition, it also serves a [Swagger UI](https://github.com/swagger-api/swagger-ui) in an additional port (defaults to `15000`). For instance, by executing the following: Start a gateway and UI from the openapi  ``` $krakend openapi serve -s ~/Downloads/openapi.yaml -o krakend.json -m ``` You would have your API on port `localhost:8080` (unless the configuration changes it) and the UI on `localhost:15000`. The `-m` flag is helpful when your backend does not exist yet, and you want to return mocked data by KrakenD itself. The `-o` flag will write the configuration file KrakenD has started in the disk, but it’s not required for the operation, only for you to see the final result. You can change the host and the UI port by passing the appropriate flags. For instance: Start the OpenAPI UI on port 18000 with Mock data  ``` $krakend openapi serve -s ~/Downloads/openapi.yaml -p 18000 -m ``` When using Docker, remember to expose the OpenAPI UI port if you want to have access from the outside. **The UI is open to anyone with access to the machine**, and it needs OpenAPI version `3`. ## Migration from older versions The commands `krakend generate openapi` and `krakend generate from openapi` used in previous versions keep working in v2.3 as they did in previous versions. Still, they will be removed in the future. These commands are now replaced by `krakend openapi export` and `krakend openapi import`, which **default now to OpenAPI 3** instead of Swagger v2. The new commands also support Swagger, but you must add the `--legacy` flag. These are the changes you need in the code. ### Migration from old export **Diff for OpenAPI3 users**: ```diff - krakend generate openapi -c krakend.json -o openapi.json --oas3 + krakend openapi export -c krakend.json -o openapi.json ``` **Diff for Swagger v2 users**: ```diff - krakend generate openapi -c krakend.json -o openapi.json + krakend openapi export -c krakend.json -o openapi.json --legacy ``` ### Migration from old import **Diff for OpenAPI3 users**: ```diff - krakend generate from openapi -c openapi.json -o krakend.json --oas3 + krakend openapi import -s openapi.json -o krakend.json ``` **Diff for Swagger v2 users**: ```diff - krakend generate from openapi -c openapi.json -o krakend.json + krakend openapi import -s openapi.json -o krakend.json --legacy ``` --- # [Document your endpoints with OpenAPI (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/openapi/) The OpenAPI documentation (formerly Swagger) is automatically generated by the KrakenD Enterprise binary when you use the `krakend openapi export` command (see [Exporting to OpenAPI](/docs/enterprise/developer/openapi/)). ## OpenAPI configuration The OpenAPI documentation configuration has **two different placements** that you can use: - `service` level: Defines the general business information and metadata that provides context about your company and how to get in touch with you as an end-user, it is common to all endpoints. - `endpoint` level: Defines the documentation and metadata for a specific endpoint. Both configuration placements are optional, and when you don’t provide the information, KrakenD sets defaults. ### Setting the basic API information At the service level (the root of the configuration file), you can declare an `extra_config` with the `documentation/openapi` to declare the general API information for your developers. **All the fields are optional**. The available fields are self-explanatory by seeing the following example: ```json { "version": 3, "name": "Your KrakenD API", "extra_config": { "documentation/openapi": { "description": "This is a sample server. You can find out more about at [http://blah](http://blah.blah.com)", "version": "1.0.0", "host": "my.api.com", "base_path": "/v1", "terms_of_service": "http://url.to/tos", "contact_name": "The wonderful API department", "contact_email": "contact@example.com", "contact_url": "https://your.company/developers", "license_name": "MIT", "license_url": "https://opensource.org/licenses/MIT", "tag_definition": [ { "name": "Admin", "description": "All endpoints for administrative users" } ], "schemes": ["http","https"], "components_schemas": { "Pet": { "type": "object", "required": ["id","name"], "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" }, "tag": { "type": "string" } } }, "Pets": { "type": "array", "maxItems": 100, "items": { "$ref": "#/components/schemas/Pet" } } } } } } ``` ##### Fields of `"documentation/openapi"` \* required fields ##### [`base_path`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#base_path "Click to copy link to base_path") string A starting path that is appended to any endpoint. Example: `"/v1"` ##### [`components_schemas`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#components_schemas "Click to copy link to components_schemas") object The JSON Schemas you can reuse inside endpoint definitions using `ref`. You can either pass the JSON Schema object, or a bas64 string. Example: `{"Pet":{"required":["id","name"],"type":"object"}}` Properties of `components_schemas` can use a name matching the pattern `.*`, and their content is an `[string object]` ##### [`contact_email`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#contact_email "Click to copy link to contact_email") string Email where users of your API can write to. Example: `"/v1"` ##### [`contact_name`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#contact_name "Click to copy link to contact_name") string Contact name. Example: `"/v1"` ##### [`contact_url`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#contact_url "Click to copy link to contact_url") string Contact URL that users of your API can read. Example: `"/v1"` ##### [`cookie_name`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#cookie_name "Click to copy link to cookie_name") string When generating an OpenAPI spec, the name of the cookie used under components securitySchemes. Defaults to `"SESSIONID"` ##### [`custom_security_schemes`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#custom_security_schemes "Click to copy link to custom_security_schemes") object Allows you to add custom security schemes under `components/securitySchemes` in the generated OpenAPI spec. This is useful when you want to define your own security schemes, different from the built-in ones (e.g., `jwt`, `apikey`, `cookie`, etc.). When the property is in the service level you must declare the schema (e.g., `"OAuth2Security":{...}`), and when it is in the endpoint you should only write the object name with not properties inside, e.g, `{"OAuth2Security":{}`. Example: `{"OAuth2Security":{"description":"OAuth2 security scheme","flows":{"authorizationCode":{"authorizationUrl":"https://example.com/oauth/authorize","scopes":{},"tokenUrl":"https://example.com/oauth/token"}},"type":"oauth2"}}` Properties of `custom_security_schemes` can use a name matching the pattern `^.*$`, and their content is an `object` with the following properties: ##### [`description`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#description "Click to copy link to description") string An introductory, optionally verbose, explanation supporting [CommonMark](http://commonmark.org/help/) syntax. If you’d like to load an **external markdown file**, you can use flexible configuration, for instance `"description": {{include "openapi/intro.md" | toJson }}` Example: `"Hi there, I am [OpenAPI](https://www.krakend.io/docs/enterprise/endpoints/openapi/)"` ##### [`disable_default_response_definitions`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#disable_default_response_definitions "Click to copy link to disable_default_response_definitions") boolean By default, KrakenD adds a `500` and a `200` response definition to each endpoint. Set this property to `true` if you want to avoid this behavior. Defaults to `false` ##### [`host`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#host "Click to copy link to host") string The hostname where you will publish your API. Example: `"my.api.com"` ##### [`jwt_key`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#jwt_key "Click to copy link to jwt_key") string When generating an OpenAPI spec, the name of the JWT key used under components securitySchemes. Defaults to `"KrakenD-JWT"` ##### [`license_name`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#license_name "Click to copy link to license_name") string The license name (e.g.: Apache License) Example: `"/v1"` ##### [`license_url`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#license_url "Click to copy link to license_url") string The URL where the license is hosted Example: `"/v1"` ##### [`schemes`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#schemes "Click to copy link to schemes") array of strings The list of schemes supported by the API, e.g. `http` or `https` Example: `["https","http"]` Defaults to `["http"]` ##### [`servers`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#servers "Click to copy link to servers") array of objects The list of servers where the API is hosted. The server URL can be a relative path, e.g., `/v1` or an absolute path. The URL might contain `{variables}`, although these are only recognized by OpenAPI and to KrakenD they are just literal strings because it does not use them. Examples: `[{"url":"/v1"}]` , `[{"url":"https://example.com:{port}","variables":{"port":{"default":"8443","enum":["8443","443"]}}}]` Each item of servers accepts the following properties: ##### [`description`](#servers-description "Click to copy link to description") string A short description of the server URL. ##### [`url`](#servers-url "Click to copy link to url") * string, object The URL of the server. This URL will be used as the base path for all endpoints. ##### [`variables`](#servers-variables "Click to copy link to variables") object A [Server Variable Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-variable-object) according to the OpenAPI specification. The variable name is the key and the value is the default value for that variable. Properties of `variables` can use a name matching the pattern `^.*$`, and their content is an `[string object]` ##### [`tag_definition`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#tag_definition "Click to copy link to tag_definition") array of objects Sets a detailed description for the tags classifiying endpoints when generating the OpenAPI spec. Example: `[{"description":"Description of tag1","name":"Tag1"}]` Each item of tag\_definition accepts the following properties: ##### [`description`](#tag_definition-description "Click to copy link to description") string Describe what this tag is grouping ##### [`name`](#tag_definition-name "Click to copy link to name") * string The name of the tag. You will use this name in each endpoint. ##### [`terms_of_service`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#terms_of_service "Click to copy link to terms_of_service") string The URL to the terms of service for using this API. Example: `"/v1"` ##### [`version`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#version "Click to copy link to version") string The version numbering you want to apply to this release of API., e.g.: `1.0`. Example: `"1.0"` Schema: [https://www.krakend.io/schema/v2.13/documentation/openapi.json](https://www.krakend.io/schema/v2.13/documentation/openapi.json "JSON schema definition") The `name` specified in the root of the configuration (outside the OpenAPI’s `extra_config`) is used as the **title of your API**. If none is present, the string `KrakenD - API Gateway` is the default. If you want to include a full markdown file in the introduction, you can use flexible configuration to ease this process. For instance, if you write a markdown file, under `partials/openapi/intro.md` you can load it automatically as follows: ```go-text-template { "version": 3, "name": "Your KrakenD API", "extra_config": { "documentation/openapi": { "description": {{include "openapi/intro.md" | toJson }}, } } } ``` ### Documenting endpoints In addition to setting global metadata for the whole API, you can insert additional information to each endpoint, so users can have an explanation of what every endpoint does. **All fields are optional**: ##### Fields of `"documentation/openapi"` \* required fields ##### [`audience`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#audience "Click to copy link to audience") array of strings The list of audiences that will consume this endpoint. These values **do not define the gateway logic** in any way. They are a way to group endpoints and filter them out when generating the OpenAPI documentation. Use `*` to indicate an endpoint will be present in any audience generated. Example: `["gold","silver","*"]` ##### [`custom_security_schemes`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#custom_security_schemes "Click to copy link to custom_security_schemes") object Allows you to add custom security schemes under `components/securitySchemes` in the generated OpenAPI spec. This is useful when you want to define your own security schemes, different from the built-in ones (e.g., `jwt`, `apikey`, `cookie`, etc.). When the property is in the service level you must declare the schema (e.g., `"OAuth2Security":{...}`), and when it is in the endpoint you should only write the object name with not properties inside, e.g, `{"OAuth2Security":{}`. Example: `{"OAuth2Security":{"description":"OAuth2 security scheme","flows":{"authorizationCode":{"authorizationUrl":"https://example.com/oauth/authorize","scopes":{},"tokenUrl":"https://example.com/oauth/token"}},"type":"oauth2"}}` Properties of `custom_security_schemes` can use a name matching the pattern `^.*$`, and their content is an `object` with the following properties: ##### [`description`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#description "Click to copy link to description") string An introductory, optionally verbose, explanation supporting [CommonMark](http://commonmark.org/help/) syntax. If you’d like to load an **external markdown file**, you can use flexible configuration, for instance `"description": {{include "openapi/intro.md" | toJson }}` Example: `"Hi there, I am [OpenAPI](https://www.krakend.io/docs/enterprise/endpoints/openapi/)"` ##### [`example`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#example "Click to copy link to example") object, string **Deprecated in OAS3** (use `response_definition` instead). A free form JSON object or a string you would like to show as a sample response of the endpoint. The examples assume they are JSON content types except when using the `output_encoding=string`. ##### [`header_definition`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#header_definition "Click to copy link to header_definition") array of objects Sets a detailed description for the headers allowed in the endpoint. Make sure to include the same headers in the endpoint’s `input_headers`. Example: `[{"description":"The type of content","name":"Content-Type"}]` Each item of header\_definition accepts the following properties: ##### [`description`](#header_definition-description "Click to copy link to description") string The description of the header Example: `"The Content-Type sets the encoding of the request"` ##### [`enum`](#header_definition-enum "Click to copy link to enum") array of \[array boolean integer number object string]s The allowed values for this header Example: `["application/json","application/xml"]` ##### [`hide`](#header_definition-hide "Click to copy link to hide") boolean Avoids this header from showing in the generated documentation Defaults to `false` ##### [`name`](#header_definition-name "Click to copy link to name") * string The name of the header, as declared in `input_headers` ##### [`required`](#header_definition-required "Click to copy link to required") boolean Set to `true` when this query string is required Defaults to `false` ##### [`type`](#header_definition-type "Click to copy link to type") The type of data according to the OpenAPI spec Possible values are: `"integer"` , `"string"` , `"boolean"` , `"object"` , `"array"` , `"float"` , `"date-time"` Example: `"string"` Defaults to `"string"` ##### [`operation_id`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#operation_id "Click to copy link to operation_id") string A unique string identifying the operation identifier. Usually the method + the endpoint. If provided, these IDs must be unique among all operations described in your API. Example: `"GET/foo"` ##### [`param_definition`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#param_definition "Click to copy link to param_definition") array of objects Sets a detailed description for the URL parameters (e.g.: `/foo/{param}`) required in the endpoint. Make sure to include to write the param exactly as in the endpoint definition. Example: `[{"description":"The unique user ID","name":"id_user"}]` Each item of param\_definition accepts the following properties: ##### [`description`](#param_definition-description "Click to copy link to description") string The description of the parameter ##### [`enum`](#param_definition-enum "Click to copy link to enum") array of \[array boolean integer number object string]s The allowed values for this parameter Example: `["application/json","application/xml"]` ##### [`example`](#param_definition-example "Click to copy link to example") string, number, boolean, array, object An example value for this parameter. This example is injected in the spec as is. ##### [`examples`](#param_definition-examples "Click to copy link to examples") object Guess what? Multiple example values for this parameter. Properties of `examples` can use a name matching the pattern `^.*$`, and their content is an `[string number boolean array object]` ##### [`format`](#param_definition-format "Click to copy link to format") The extending format for the previously defined type. If the type is `number` or `string` you can apply them one of the available formats. If you choose string formats for numbers or viceversa de linter does not check it. Possible values are: `"int32"` , `"int64"` , `"float"` , `"double"` , `"date"` , `"date-time"` , `"byte"` , `"uuid"` , `""` ##### [`hide`](#param_definition-hide "Click to copy link to hide") boolean Avoids this param from showing in the generated documentation, although is still part of the endpoint definition. Defaults to `false` ##### [`name`](#param_definition-name "Click to copy link to name") * string The name of the parameter, as declared in `endpoint` without the curly braces `{}` ##### [`required`](#param_definition-required "Click to copy link to required") boolean A path parameter is always mandatory on KrakenD, but you can still generate the OpenAPI documentation saying otherwise. Defaults to `true` ##### [`type`](#param_definition-type "Click to copy link to type") The type of data according to the OpenAPI spec Possible values are: `"integer"` , `"number"` , `"string"` , `"boolean"` , `"object"` , `"array"` , `"float"` , `"date-time"` Example: `"string"` Defaults to `"string"` ##### [`query_definition`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#query_definition "Click to copy link to query_definition") array of objects Sets a detailed description for the query strings allowed in the endpoint. Make sure to include the same strings in the endpoint’s `input_query_strings`. Example: `[{"description":"The number of the page","name":"page"}]` Each item of query\_definition accepts the following properties: ##### [`description`](#query_definition-description "Click to copy link to description") string The description of the querystring ##### [`enum`](#query_definition-enum "Click to copy link to enum") array of \[array boolean integer number object string]s The allowed values for this query definition ##### [`example`](#query_definition-example "Click to copy link to example") string, object, array, boolean, integer, null, number A free form JSON object or a string you would like to show as a sample response of the endpoint. The examples assume they are JSON content types except when using the `output_encoding=string`. ##### [`format`](#query_definition-format "Click to copy link to format") The extending format for the previously defined type. If the type is `number` or `string` you can apply them one of the available formats. If you choose string formats for numbers or viceversa de linter does not check it. Possible values are: `"int32"` , `"int64"` , `"float"` , `"double"` , `"date"` , `"date-time"` , `"byte"` , `"uuid"` , `""` ##### [`hide`](#query_definition-hide "Click to copy link to hide") boolean Avoids this querystring from showing in the generated documentation Defaults to `false` ##### [`name`](#query_definition-name "Click to copy link to name") * string The name of the querystring, as declared in `input_query_strings` ##### [`required`](#query_definition-required "Click to copy link to required") boolean Set to `true` when this query string is required Defaults to `true` ##### [`type`](#query_definition-type "Click to copy link to type") The type of data according to the OpenAPI spec Possible values are: `"integer"` , `"number"` , `"string"` , `"boolean"` , `"object"` , `"array"` , `"float"` , `"date-time"` Defaults to `"string"` ##### [`request_definition`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#request_definition "Click to copy link to request_definition") array Describes the payload needed to consume the endpoint. If a JSON Schema validation exists, it takes precedence when generating the documentation. An example use case is when you need to document a `multipart/form-data` request body.This property is an array because you can document requests with multiple content types. Example: `[{"content_type":"application/json","description":"Updates the user","example":{"first_name":"Mary","id_user":33}}]` Each item of request\_definition accepts the following properties: ##### [`content_type`](#request_definition-content_type "Click to copy link to content_type") * string The content type returned by this error, e.g., `application/json`. You cannot repeat this content type in another item. ##### [`description`](#request_definition-description "Click to copy link to description") string The description of the payload this endpoint accepts. ##### [`example`](#request_definition-example "Click to copy link to example") string, object, array, boolean, integer, null, number A free form JSON object or a string you would like to show as a sample response of the endpoint. The examples assume they are JSON content types except when using the `output_encoding=string`. ##### [`example_schema`](#request_definition-example_schema "Click to copy link to example_schema") string, object A JSON schema that describes the request format for the accepted payload in the endpoint. Use either example or example\_schema, but not both. ##### [`examples`](#request_definition-examples "Click to copy link to examples") object A free form JSON object or a string you would like to show as a sample response of the endpoint. The examples assume they are JSON content types except when using the `output_encoding=string`. Examples: `{"Example 1":{"msg":"a message"}}` , `{"Example 1":{"msg":"a message"},"Example 2":{"error":"an error message"}}` ##### [`ref`](#request_definition-ref "Click to copy link to ref") string The relative reference to the `components/schema` OpenAPI definition that will be used as definition of the accepted request. Notice that the path `#/components/schemas/` is not needed. Example: `"your_schema_name"` ##### [`response_definition`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#response_definition "Click to copy link to response_definition") object Describes the different status codes returned by this endpoint. Each key is the definition of the status code, represented by a string. E.g., `200` (success), `500` (internal error), etc. Example: `{"404":{"@comment":"Some comment","content_type":"application/json","description":"Page not found","example":{"status":"KO"}}}` Properties of `response_definition` can use a name matching the pattern `default|^[0-9]+$`, and their content is an `object` with the following properties: ##### [`content_type`](#response_definition-content_type "Click to copy link to content_type") string The content type returned by this error, e.g., `application/json` ##### [`description`](#response_definition-description "Click to copy link to description") string The description of this error code, e.g., `Page not found`. ##### [`example`](#response_definition-example "Click to copy link to example") string, object, array, boolean, integer, null, number A free form JSON object or a string you would like to show as a sample response of the endpoint. The examples assume they are JSON content types except when using the `output_encoding=string`. ##### [`example_schema`](#response_definition-example_schema "Click to copy link to example_schema") string, object A JSON schema that describes the response format for the endpoint, directly as a JSON object, or encoded as a base64 string. Use either example or example\_schema, but not both. ##### [`examples`](#response_definition-examples "Click to copy link to examples") object A free form JSON object or a string you would like to show as a sample response of the endpoint. The examples assume they are JSON content types except when using the `output_encoding=string`. Examples: `{"Example 1":{"msg":"a message"}}` , `{"Example 1":{"msg":"a message"},"Example 2":{"error":"an error message"}}` ##### [`ref`](#response_definition-ref "Click to copy link to ref") string The relative reference to the `components/schema` OpenAPI definition that will be used as definition of the accepted request. Notice that the path `#/components/schemas/` is not needed. Example: `"your_schema_name"` ##### [`summary`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#summary "Click to copy link to summary") string A short summary for the endpoint. Use the description field for the longest explanation. ##### [`tags`](https://www.krakend.io/docs/enterprise/endpoints/openapi/#tags "Click to copy link to tags") array of strings You can assign a list of tags to each API operation. If you declare tags in the `tag_definition` at the OpenAPI service level, they will have a description in the documentation. Tagged operations may be handled differently by tools and libraries. For example, Swagger UI uses tags to group the displayed operations. Schema: [https://www.krakend.io/schema/v2.13/documentation/openapi.json](https://www.krakend.io/schema/v2.13/documentation/openapi.json "JSON schema definition") The example below sets a rich OpenAPI documentation for two endpoints. Notice that: - For the `GET /users` we want to add descriptions to the `?page` query string, and we provide a full example response under `response_definition` - For the `PUT /users/{id_user}` we also add a `request_definition` that defines the payload the user will need to send. Have a look at the different parameters: ```json { "version": 3, "$schema": "https://www.krakend.io/schema/krakend.json", "host": [ "http://localhost:8080" ], "debug_endpoint": true, "echo_endpoint": true, "endpoints": [ { "endpoint": "/users", "method": "GET", "extra_config": { "documentation/openapi": { "description": "Lists all user in the system, paginated in blocks of 50 users", "summary": "List all users", "operation_id": "GET/users/id_user", "tags": [ "users" ], "audience": [ "internal" ], "query_definition": [ { "name": "page", "required": false, "description": "Pass a ?page= if you want to use the pagination" } ], "response_definition": { "200": { "content_type": "application/json", "example": { "users": [ { "id": 1, "first_name": "Juliana", "last_name": "Claire", "email": "jclaire0@livejournal.com" }, { "id": 2, "first_name": "Jeannette", "last_name": "Hugonneau", "email": "jhugonneau1@gmpg.org" } ] } } } } }, "backend": [ { "url_pattern": "/api/1.0/users/list" } ] }, { "endpoint": "/users/{id_user}", "method": "PUT", "extra_config": { "documentation/openapi": { "description": "The PUT method updates the data for the given id_user. You only need to pass the fields you want to update.", "summary": "Updates the user data", "operation_id": "PUT/users/id_user", "tags": [ "users" ], "audience": [ "internal" ], "request_definition": [ { "content_type": "application/json", "description": "Updates provided fields. All fields are optional, id_user taken from the URL", "example": { "address": "Suite 543 907 Green Forks, Wavamouth, CT 10807-8479", "first_name": "Felix", "last_name": "Richardson" } } ], "response_definition": { "200": { "content_type": "application/json", "example": { "id_user": 33007, "status": "success" } } } } }, "backend": [ { "url_pattern": "/api/1.0/users/update/{id_user}" } ] } ] } ``` Quite verbose for two endpoints right? The example above does not even have all possible options to document endpoints. Remember that you can move any bit of the configuration to an external file using [Flexible Configuration](/docs/enterprise/configuration/flexible-config/). You can still generate the OpenAPI file without any of this, but a rich and detailed documentation is always aprreciated by consumers. This is in an example on how you could move out the documentation from the endpoint definition, but there are more ways of doing it: ```json { "endpoints": [ { "endpoint": "/users", "method": "GET", "extra_config": { "documentation/openapi": { "$ref": "./documentation/GET_users.json" } }, "backend": [ { "url_pattern": "/api/1.0/users/list" } ] }, { "endpoint": "/users/{id_user}", "method": "PUT", "extra_config": { "documentation/openapi": { "$ref": "./documentation/PUT_users_id_user.json" } }, "backend": [ { "url_pattern": "/api/1.0/users/update/{id_user}" } ] } ] } ``` ### Integration with JSON Schema When your endpoint uses a [`validation/json-schema`](/docs/endpoints/json-schema/), all write methods (`POST`, `PUT`, `PATCH`, `DELETE`) add in the documentation automatically its validation conditions. There is nothing else that you need to add in the configuration, but of course you can explain the payloads using the `request_definition` property. ### Define different audiences of your OpenAPI When you declare your endpoints, you might want to restrict some of them from appearing in the final documentation export because the audience consuming them doesn’t need to be aware of their existence. For instance, let’s say your API exposes endpoints like `/user`, `/cart`, or `/admin`, where the administration endpoint is for internal usage. Why would you document it for your end-users if they are not supposed to use it or know its existence? For cases like this, you might want to add definitions of specific audiences to the endpoints. For instance: ```json [ { "endpoint": "/user", "extra_config": { "documentation/openapi": { "description": "The user endppoint", "audience": [ "public", "internal" ] } } }, { "endpoint": "/cart", "extra_config": { "documentation/openapi": { "description": "The cart endpoint", "audience": [ "public", "internal" ] } } }, { "endpoint": "/admin", "extra_config": { "documentation/openapi": { "description": "The admin endpoint", "audience": [ "internal" ] } } }, { "endpoint": "/no-audience", "extra_config": { "documentation/openapi": { "description": "The admin endpoint" } } } ] ``` Then, [when generating the documentation](/docs/enterprise/developer/openapi/), you can filter out the `/admin` endpoint and exclude it from the final documentation with `krakend openapi export -c krakend.json -a public`. The command would document `/user`, `/cart`, and `/no-audience`. It is important to notice that when you filter by audience, **all endpoints without defining one**, will appear in the final documentation. Similarly, using `krakend openapi export -c krakend.json -a internal` would generate the documentation for all the endpoints, but not specifying the `-a` flag would have produced the same effect. Notice that the `audience` attribute lets you exclude endpoints from the final documentation, but they exist anyway in the service, and you must protect them against unwanted usage. ### Documenting response examples and errors The `response_definition` object allows you to set examples of the response an API endpoint will deliver. For instance: ```json { "endpoint": "/cart", "extra_config": { "documentation/openapi": { "description": "The cart endpoint", "response_definition": { "200": { "example": { "status": "OK", "message": "Product added to the cart!" } }, "500": { "example": { "status": "KO", "message": "Couldn't add the product to the cart!" } } } } } } ``` When the examples are long, you might want to have the example responses in a separate file (like settings or partial) using [Flexible Configuration](/docs/configuration/flexible-config/), so the endpoint template keeps being readable. For instance ```go-text-template { "endpoint": "/cart", "extra_config": { "documentation/openapi": { "description": "The cart endpoint", "response_definition": { "200": { "example": {{- include openapi_cart_response.json -}} } } } } } ``` You can also use define an example using the `example_schema` (schema takes precedence), in base64. There are different ways you can alliviate the burden of writing code in base64. For instance: ```go-text-template { "endpoint": "/cart", "extra_config": { "documentation/openapi": { "description": "The cart endpoint", "response_definition": { "200": { "example_schema": {{- include cart_schema.json | b64enc -}} } } } } } ``` Or writing the schema inline, escaped with backticks: ```go-text-template { "endpoint": "/cart", "extra_config": { "documentation/openapi": { "description": "The cart endpoint", "response_definition": { "200": { "example_schema":: "{{ `{ "type": "object", "title": "This is the response" }` | b64enc }}" } } } } } ``` --- # [Generating API documentation (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/developer/api-docs/) KrakenD supports exporting (and importing) OpenAPI Specification. This flexibility allows you to **auto-generate documentation** that you can serve from the gateway itself, or host somewhere else. There are multiple ways of generating docs API documentation is a dragon of many heads. This page sets an example for you to understand how to do it, and open the door to start working with it. It does not mean that you have to generate the docs on the Docker image, or that you need to serve the documentation from KrakenD itself. You can see a working example with a few more options, like creating an intro in markdown, or define requests and responses in the [examples repository](https://github.com/krakend/examples/tree/main/10.api-docs-with-redocly). The idea of the auto-generation is that after every commit or release, you: - Export your gateway configuration to an `openapi.json` file with the [`openapi export` command](/docs/enterprise/developer/openapi/#exporting-to-openapi) - Build the UI automatically using the specification - Optionally add a route in KrakenD to serve the generated documentation ## Automatic documentation with Redocly ![Screenshot of Redocly auto-generated documentation](/images/documentation/screenshots/redocly_autogenerated_documentation.png) The following example generates the documentation using [Redocly](https://redocly.com/docs/) during the Docker build process. The steps are: - It copies the configuration to `/etc/krakend` - Runs the `openapi export` command - Runs the [Redocly CLI](https://redocly.com/docs/cli/) which generates the documentation from the OpenAPI spec. - Generates a clean KrakenD image with the configuration and the documentation The `Dockerfile` could look like this: ```Dockerfile FROM krakend/krakend-ee:2.13 AS builder # This is an Enterprise feature, make sure to have the LICENSE in the krakend dir COPY krakend /etc/krakend RUN FC_DEBUG=true krakend check -d -t -c "krakend.json" RUN krakend openapi export -c /tmp/krakend.json -o /tmp/openapi.json # Redocly generation FROM redocly/cli as redocly COPY --from=builder /tmp/openapi.json /spec/swagger.json # Outputs under /spec/redoc-static.html RUN /usr/local/bin/redocly build-docs swagger.json FROM krakend/krakend-ee:2.13 AS production COPY krakend/LICENSE /etc/krakend/LICENSE # Copy the final configuration and the documentation # which will be served from KrakenD itself under /docs/ COPY --from=builder /tmp/krakend.json /etc/krakend/krakend.json COPY --from=redocly /spec/redoc-static.html /etc/krakend/docs/index.html ``` And the paths used assume that you want to serve the documentation under `/docs/`, so you would need an endpoint definition like the following: ```json { "@comment": "This endpoint serves the documentation under /docs/", "endpoint": "/docs/*", "output_encoding": "no-op", "backend": [ { "extra_config": { "backend/static-filesystem": { "path": "./docs/" } }, "url_pattern": "/", "host": [ "http://leave-this-value" ] } ] } ``` Explore the Redocly options for an even richer documentation. [Download example](https://github.com/krakend/examples/tree/main/10.api-docs-with-redocly "Download example") ## Automatic documentation with Swagger UI ![Screenshot of SwaggerUI auto-generated documentation](/images/documentation/screenshots/swaggerui_autogenerated_documentation.png) The well known [Swagger UI](https://swagger.io/tools/swagger-ui/) could also be generated simlary. The following example has these steps: - It copies the configuration to `/etc/krakend` - Runs the `openapi export` command - Clones the plain [HTML Swagger UI](https://github.com/swagger-api/swagger-ui) - Replaces the default swagger URL (there are much better ways to do this) with a `sed`. - Generates a clean KrakenD image with the configuration and the documentation The `Dockerfile` could look like this: ```Dockerfile FROM krakend/krakend-ee:2.13 AS builder # This is an Enterprise feature, make sure to have the LICENSE in the krakend dir COPY krakend /etc/krakend RUN FC_DEBUG=true krakend check -d -t -c "krakend.json" # The flexible_config.json has an "out" pointing to /tmp/krakend.json RUN krakend openapi export -c /tmp/krakend.json -o /tmp/swagger.json # Swagger UI download FROM alpine as swagger # Clone the Swagger UI RUN apk add git RUN git clone https://github.com/swagger-api/swagger-ui.git # Replace the demo URL with yours: /docs/ RUN sed -i "s@https://petstore.swagger.io/v2/@/docs/@" swagger-ui/dist/swagger-initializer.js RUN mv swagger-ui/dist /docs # Final clean image FROM krakend/krakend-ee:2.13 AS production COPY krakend/LICENSE /etc/krakend/LICENSE # Copy the final configuration and the documentation # which will be served from KrakenD itself under /docs/ COPY --from=builder /tmp/krakend.json /etc/krakend/krakend.json COPY --from=swagger /docs /etc/krakend/docs/ COPY --from=builder /tmp/swagger.json /etc/krakend/docs/swagger.json ``` [Download example](https://github.com/krakend/examples/tree/main/10.api-docs-with-redocly "Download example") --- # [Postman Integration Guide for Developers (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/developer/postman/) The Postman generator is a command line utility embedded in the KrakenD binary, offering the automatic generation of Postman collections based on the KrakenD configuration. When you call the `generate postman` command, it reads the KrakenD configuration and generates a collection of endpoints as a Postman specification. Another option you have to import your API specifications is to [import an OpenAPI file](https://learning.postman.com/docs/integrations/available-integrations/working-with-openAPI/) into Postman. You can generate the OpenAPI specification from a KrakenD API using the [OpenAPI export](/docs/enterprise/developer/openapi/) ![Postman collections](/images/documentation/postman-collections.png) ## Generating the Postman collection The command needed to generate the Postman collection is `krakend generate postman`: Command to start KrakenD  ```` $krakend generate postman -h ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.6 Generates the Postman descriptor for the gateway based on the configuration file. Usage: krakend generate postman [flags] Examples: krakend generate postman -c config.json -o postman.json Flags: -h, --help help for postman Global Flags: -c, --config string Path to the configuration filename -o, --out string Path to the generated result. (default "out.json") ```` When the output file is generated, you can import it from Postman and share it with your peers. ## Import the collection to Postman To import the generated collections into Postman go to `File` > `Import`. You can then either upload the file, or serve it from a KrakenD URL using the [static-filesystem component](/docs/enterprise/endpoints/serve-static-content/). Postman accepts importing Collections and OpenAPI files. ## Postman configuration While **you don’t need any configuration block** to use Postman, the following options are here to enrich the way your documentation is presented on Postman. For instance, if you want to add descriptions, or group endpoints in folders, you can add this information at the service level and per endpoint. No configuration is required to generate Postman collections All items described above are optional and they enrich the Postman output. You can go as far as you want depending on the level of detail you want to give to your Postman collection. The following is a full example of Postman-documented configuration. It includes a service configuration block that declares names and descriptions for the different folders that are used in the endpoints, and a Postman block for each endpoint. ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json", "version": 3, "extra_config": { "documentation/postman": { "name": "The Awesome Books API", "description": "This is a **sample** books API. You can find out more about at your local library [http://blah](http://blah.blah.com)", "version": "0.3.0", "folder": [ { "name": "/Users", "description": "Users reading books. Our **audience**" }, { "name": "/Users/Assets", "description": "User assets endpoints" }, { "name": "/Books", "description": "All books endpoints, **the real deal**" } ] } }, "endpoints": [ { "endpoint": "/books", "method": "POST", "backend": [ { "url_pattern": "/__echo/create-book" } ], "extra_config": { "documentation/postman": { "name": "Create book", "description": "Creates a book in the library. Note that this is not AI generation of books, but rather simple insertion", "folder": "/Books" } } }, { "endpoint": "/users/{id}/books", "method": "GET", "backend": [ { "url_pattern": "/__echo/list-user-books?id={id}" } ], "extra_config": { "documentation/postman": { "name": "List user books", "folder": "/Users/Assets" } } }, { "endpoint": "/users/{id}/book/{bookId}", "method": "DELETE", "backend": [ { "url_pattern": "/__echo/delete-user-book?id={id}&bid={bookId}" } ], "extra_config": { "documentation/postman": { "name": "Delete user book ownership", "description": "Removes the association of a user with a book", "folder": "/Users/Assets" } } } ] } ``` ### Postman configuration at the service level The configuration of Postman at the `service` level is as follows: ##### Fields of Generate documentation using Postman collections \* required fields ##### [`description`](https://www.krakend.io/docs/enterprise/developer/postman/#description "Click to copy link to description") string An introductory, optionally verbose, explanation supporting Markdown syntax. If you’d like to load an **external markdown file**, you can use flexible configuration, for instance `"description": {{include "postman/intro.md" | toJson }}` Example: `"Hi there, I am a [postman collection](https://www.krakend.io/docs/enterprise/developer/postman/)"` ##### [`folder`](https://www.krakend.io/docs/enterprise/developer/postman/#folder "Click to copy link to folder") array of objects The folder definition where you will add endpoints Each item of folder accepts the following properties: ##### [`description`](#folder-description "Click to copy link to description") A description explaining the items inside this folder Example: `"Books from post-apocalyptic dystopias to brilliant time-travel"` ##### [`name`](#folder-name "Click to copy link to name") * string The folder name. This might look like the path in an operative system with a slash /, but is not strictly necessary. Usage of special chars is allowed. Examples: `"/Books"` , `"/Books/Science Fiction/Dystopia"` , `"/书籍/科幻小说"` , `"/पुस्तकें/विज्ञान कथा"` , `"/本/サイエンスフィクション"` ##### [`name`](https://www.krakend.io/docs/enterprise/developer/postman/#name "Click to copy link to name") string The name of the Postman collection you are generating. Example: `"KrakenD Config v1"` ##### [`version`](https://www.krakend.io/docs/enterprise/developer/postman/#version "Click to copy link to version") string The version you assign to this Postman collection you are generating using **semantic versioning**. Examples: `"1.2.3"` , `"0.7.9"` Schema: [https://www.krakend.io/schema/v2.13/documentation/postman.json](https://www.krakend.io/schema/v2.13/documentation/postman.json "JSON schema definition") ### Postman configuration at the endpoint level Then, in each `endpoint` you can also add a `documentation/postman` block with this configuration: ##### Fields of Generate documentation using Postman collections \* required fields ##### [`description`](https://www.krakend.io/docs/enterprise/developer/postman/#description "Click to copy link to description") string An introductory, optionally verbose, explanation supporting Markdown syntax. If you’d like to load an **external markdown file**, you can use flexible configuration, for instance `"description": {{include "postman/intro.md" | toJson }}` Example: `"Hi there, I am a [postman endpoint](https://www.krakend.io/docs/enterprise/developer/postman/)"` ##### [`folder`](https://www.krakend.io/docs/enterprise/developer/postman/#folder "Click to copy link to folder") string The folder name where you want to put this endpoint. If you defined folders at the service level, use the same name to reuse their name and description Example: `"/Books"` ##### [`name`](https://www.krakend.io/docs/enterprise/developer/postman/#name "Click to copy link to name") string The name of the endpoint you are generating. If you don’t set any name the last member path is used. Examples: `"List books"` , `"Create user"` Schema: [https://www.krakend.io/schema/v2.13/documentation/postman\_endpoint.json](https://www.krakend.io/schema/v2.13/documentation/postman_endpoint.json "JSON schema definition") --- # [Generate graphs from your configuration (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/developer/config2dot/) The `krakend.json` file whether it is a single file or the result of using the flexible configuration, it might be a complex file. A generated image of the configuration might help you understand better the running configuration. The config2dot generator will generate internally an intermediate [DOT dile](https://graphviz.org/doc/info/lang.html) that you can render into a `png` image with GraphViz or similar software. The following image illustrates an example of a KrakenD configuration: ![Config to dot](/images/documentation/config2dot.png) ## Generating the Config2dot The command needed to generate a DOT file is `krakend generate config2dot`: Command to start KrakenD  ```` $krakend generate config2dot -h ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.6 Generates a DOT graph from your KrakenD configuration. Usage: krakend generate config2dot [flags] Examples: krakend generate config2dot -c config.json -o config.dot Flags: -h, --help help for config2dot Global Flags: -c, --config string Path to the configuration filename -o, --out string Path to the generated result.") ```` ## Generating the image Once you have generated the `dot` file, you get a plain text content that is ready to be rendered by any software library that is capable of processing dot files. A good of example is [Graphviz](https://graphviz.org/). You can install Graphviz on Linux (Debian/Ubuntu) with the following command, but other distributions are similar: Install Graphviz  ``` $sudo apt-get install graphviz ``` On Mac: Install on Mac  ``` $brew install graphviz ``` Or you can also add Graphviz to the official Enterprise Docker image (gets heavier): ```Dockerfile RUN apk update RUN apk add graphviz ``` Then you can generate the image with the following command: Generate image  ``` $dot -Tpng graph.dot -o graph.png ``` --- # [IDE Integration Guide for Developers](https://www.krakend.io/docs/developer/ide-integration/) Automatic validation as you type, showing documentation while hovering an attribute, explanation of errors, and autocompletion of properties, are features that you get automatically while working with KrakenD. For users of **Visual Studio Code**, **Android Studio**, **JetBrains** editors (PHPStorm, PyCharm, GoLand, WebStorm, IntelliJ IDEA…), **Eclipse**, and other IDEs that have built-in json schema validation capabilities, there is nothing to install to have these features. Other editors can be used as well, but you will likely need to instal a JSON schema validator. This is how it could look like: ![Visual Code integration](/images/documentation/krakend-ide-integration.png) ## Editor integration for KrakenD files KrakenD has published an updated JSON-schema definition ([source](https://github.com/krakend/krakend-schema)) to validate configuration files from your IDE automatically. The editors with built-in json-schema validation will offer this feature **without installing any additional plugin**. All you need to do, is add in the beginning of your `krakend.json` configuration file a line specifying the schema: ```json { "$schema": "https://www.krakend.io/schema/v2.13/krakend.json" } ``` If you want to point to the latest version of KrakenD, and not to a specific version, you can add: ```json { "$schema": "https://www.krakend.io/schema/krakend.json" } ``` There is nothing else you need to do! Part of the URL is the KrakenD version you want to validate, notice that it does not contain the patch number (e.g.: `vA.B` instead of `vA.B.C`). ## Highlighting on Flexible Configuration templates If you are working with flexible configuration, look in your favourite’s editor marketplace extensions to deal with go `text/template` files (not html/template) that support code highlighting. Some editors also allow you the combination of templating + JSON format, so you can work with both. --- # [End-to-End Testing for Developers (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/developer/integration-tests/) In addition to checking the syntax of your KrakenD configuration and ensuring that the gateway can start, an essential step in any CI/CD strategy is including **end-to-end tests** to guarantee that all the active software components from beginning to end have the expected flow and that the gateway returns what you planned when working together with your upstream services. ## How E2E testing works The end-to-end testing definition is quite simple. You must create a folder to hold all the test cases you want to execute (e.g., `specs`). Inside the folder, you make a file per test case. Each test case is a JSON file containing the input request you will send to the gateway ( `in`) and the form of the expected output ( `out`). The test file has a structure supported by a JSON schema definition. With a folder full of test cases, the `krakend e2e` command **starts a gateway** with the provided configuration and **launches all the test cases in the folder**. You are creating real traffic to the gateway and the backends and analyzing their responses through the gateway to validate the behavior before setting a configuration online. ## Creating e2e test files Tests must be deterministic Make sure tests are deterministic and reproducible with a consistent output in different executions. If the output of a call varies over time, consider using the `schema` property to describe the properties rather than checking their exact values. For instance, you could have the following contents on your disk: Contents of E2E testing  ``` $tree /etc/krakend ├── krakend.json └── specs ├── test-foo.json ├── test-bar.json └── some-other-test.json ``` KrakenD will run all the tests declared in the folder alphabetically (according to the file sorting of the OS). ### Test Case files Each test case is a simple `.json` file containing an object with an `in` (the input request you want to do to KrakenD) and an `out` (the output you expect in the response). Sometimes, a test case needs to have multiple calls to validate a scenario. When this happens, you can add a `next` entry, an array containing couples of `in` and `out` objects that will be executed and analyzed in the declared order. The `next` does not evaluate until the tool has already processed the siblings `in` and `out`. The test case file must follow the [e2e schema](https://www.krakend.io/schema/v2.13/e2e.json) (explained below) to work correctly, but the **tool does not validate the JSON format of each test case during runtime** (this makes processing faster). Here’s an example of test you can save under `specs/test1.json` and run it with any KrakenD configuration ([`debug_endpoint`](/docs/endpoints/debug-endpoint/) set to `true` required): ```json { "$schema": "https://www.krakend.io/schema/v2.13/e2e.json", "@comment": "Makes sure that the debug endpoint returns a status ok", "in": { "method": "GET", "url": "http://localhost:8080/__debug/something", "header": { "User-Agent": "krakend-e2e-tool" } }, "out": { "status_code": 200, "body": { "status": "ok" }, "header": { "content-type": ["application/json; charset=utf-8"], "X-Krakend-Completed": ["true"], "Cache-Control": [""] } } } ``` The test above connects to the URL `http://localhost:8080/__debug/something` and analyzes its response `out`put, and checks that: - It returns an HTTP `status_code` equal to `200` - That the `body` is literally `{"status": "ok"}`, and the entirety of the payload. - It also makes sure that there are three `header` conditions: - The `content-type` and `X-Krakend-Completed` have the specified values - The `Cache-Control` header cannot be present (we compare it to an empty string). Any other headers that are not mapped here are ignored, so the response could have more. Finally, the `$schema` you see at the top of the file points to the E2E JSON Schema that helps you autocomplete and validate from your IDE (provided that it has automatic JSON-schema validation). The value can be either of: - `https://www.krakend.io/schema/v2.13/e2e.json` (minor version) - `https://www.krakend.io/schema/e2e.json` (latest version) See the format of the test cases below. ## Test Case format Test cases must be in JSON format and might contain two or three top-level attributes: ##### Fields of Schema validation for End To End testing specs \* required fields ##### [`in`](https://www.krakend.io/docs/enterprise/developer/integration-tests/#in "Click to copy link to in") * object The input request definition. At least you should define the URL used to connect to KrakenD ##### [`body`](#in-body "Click to copy link to body") object, array, string If you want to add a payload in the request, set its body here ##### [`header`](#in-header "Click to copy link to header") object An optional map of headers you want to include in the request. Example: `{"User-Agent":"krakend e2e tool"}` ##### [`method`](#in-method "Click to copy link to method") The method sent in the request Possible values are: `"GET"` , `"POST"` , `"PUT"` , `"PATCH"` , `"DELETE"` Defaults to `"GET"` ##### [`url`](#in-url "Click to copy link to url") * string The full URL you want to use in the request, including schema, host, port, path, and any additional query string parameters you might need. Example: `"http://localhost:8080/__debug/something"` ##### [`next`](https://www.krakend.io/docs/enterprise/developer/integration-tests/#next "Click to copy link to next") array Additional checks, in sequential order, of `in` and `out` definitions. The outter `in` and `out` are always executed first. Example: `[{"in":{"url":"http://localhost/__health"},"out":{"status_code":200}},{"in":{"url":"http://localhost:8080/foo"},"out":{"status_code":500}}]` Each item of next accepts the following properties: ##### [`in`](#next-in "Click to copy link to in") * object The input request definition. At least you should define the URL used to connect to KrakenD ##### [`body`](#in-body "Click to copy link to body") object, array, string If you want to add a payload in the request, set its body here ##### [`header`](#in-header "Click to copy link to header") object An optional map of headers you want to include in the request. Example: `{"User-Agent":"krakend e2e tool"}` ##### [`method`](#in-method "Click to copy link to method") The method sent in the request Possible values are: `"GET"` , `"POST"` , `"PUT"` , `"PATCH"` , `"DELETE"` Defaults to `"GET"` ##### [`url`](#in-url "Click to copy link to url") * string The full URL you want to use in the request, including schema, host, port, path, and any additional query string parameters you might need. Example: `"http://localhost:8080/__debug/something"` ##### [`out`](#next-out "Click to copy link to out") * object The expected response from the server \* Required **any of**: ( `status_code` ) , or ( `status_code` + `body` ) , or ( `status_code` + `schema` ) ##### [`body`](#out-body "Click to copy link to body") string, object The expected returned body by the response as a string or JSON object. Remove this body field when you don’t want to check its contents, when the response does not have a body, or when you want to use the schema instead. Example: `"http://localhost:8080/__debug/something"` ##### [`header`](#out-header "Click to copy link to header") object Show properties Checks that all headers included in the response match the provided values. You only need to declare the relevant headers you want, as the rest are ignored. As headers, by RFC definition, can be multiple, you must always use an array to express the values you want to check. You can also check that **a specific header does not exist** in the response declaring an empty value `[""]`. Example: `{"Cache-Control":[""],"X-Krakend-Completed":["true"],"content-type":["application/json; charset=utf-8"]}` Properties of `header` can use a name matching the pattern `.*`, and their content is an `array` ##### [`schema`](#out-schema "Click to copy link to schema") object A JSON Schema object that validates the response. This option allows you to work with responses that the literal value is not that important and you want to check the structure of the returned document instead. If the response matches the schema definition, the test passes. If you define a `schema` and a `body` simultaneously only the schema is validated. ##### [`status_code`](#out-status_code "Click to copy link to status_code") integer The integer value of the HTTP status code returned by the server. Example: `200` ##### [`out`](https://www.krakend.io/docs/enterprise/developer/integration-tests/#out "Click to copy link to out") * object The expected response from the server \* Required **any of**: ( `status_code` ) , or ( `status_code` + `body` ) , or ( `status_code` + `schema` ) ##### [`body`](#out-body "Click to copy link to body") string, object The expected returned body by the response as a string or JSON object. Remove this body field when you don’t want to check its contents, when the response does not have a body, or when you want to use the schema instead. Example: `"http://localhost:8080/__debug/something"` ##### [`header`](#out-header "Click to copy link to header") object Checks that all headers included in the response match the provided values. You only need to declare the relevant headers you want, as the rest are ignored. As headers, by RFC definition, can be multiple, you must always use an array to express the values you want to check. You can also check that **a specific header does not exist** in the response declaring an empty value `[""]`. Example: `{"Cache-Control":[""],"X-Krakend-Completed":["true"],"content-type":["application/json; charset=utf-8"]}` Properties of `header` can use a name matching the pattern `.*`, and their content is an `array` ##### [`schema`](#out-schema "Click to copy link to schema") object A JSON Schema object that validates the response. This option allows you to work with responses that the literal value is not that important and you want to check the structure of the returned document instead. If the response matches the schema definition, the test passes. If you define a `schema` and a `body` simultaneously only the schema is validated. ##### [`status_code`](#out-status_code "Click to copy link to status_code") integer The integer value of the HTTP status code returned by the server. Example: `200` Schema: [https://www.krakend.io/schema/v2.13/e2e.json](https://www.krakend.io/schema/v2.13/e2e.json "JSON schema definition") ### Testing non-deterministic responses When the response of your backend is not deterministic, instead of checking the response against a `body` trying to find an exact payload, you can test it against a **JSON Schema**, using the `schema` attribute. For instance, our [health endpoint](/docs/service-settings/health/) returns the date after every request. It would be impossible to test it literally, as the date changes between executions. The following test would make sure that it works by comparing the response with a schema: ```json { "$schema": "https://www.krakend.io/schema/v2.13/e2e.json", "@comment": "Makes sure that the health endpoint contains three fields with the right types", "in": { "url": "http://localhost:8080/__health" }, "out": { "status_code": 200, "schema": { "$schema": "http://json-schema.org/draft-07/schema#", "required": ["agents","now","status"], "properties": { "agents": { "type": "object" }, "now": { "type": "string" }, "status": { "type": "string", "enum": ["ok"] } } } } } ``` The content inside the `schema` property is JSON Schema syntax up to a Draft-07 response. ## Running the e2e tests The `e2e` command can run without additional flags if you use the default naming, but it has several run options. It looks like this: Term  ```` $krakend e2e -h ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.6 Executes an end to end test for the gateway based on the configuration file and a set of specs. Usage: krakend e2e [flags] Examples: krakend e2e -c config.json -s specs Flags: -c, --config string Path to the KrakenD configuration file. (default "./krakend.json") -d, --delay duration The delay for the delayed backend endpoint. (default 200ms) -e, --envar string Comma separated list of patterns to use to filter the envars to pass (set to ".*" to pass everything). -g, --grpc-port int Port for the mocked grpc backend api (disabled if 0) -h, --help help for e2e --https-extra-port int Port for the mocked mTLS https extra backend api (disabled if 0) --https-port int Port for the mocked mTLS https backend api (disabled if 0) -l, --log string Path for storing the server logs. (default "./e2e.log") -r, --no-redirect Disable redirects at the http client. -p, --port int The port for the mocked backend api. (default 8081) --ready-url string Ready endpoint URL to check that the KrakenD system has started (expects a 200 Ok response: in case of doubt use the /__health endpoint) --ready-url-wait duration Maximum time to keep attempting to get an Ok response from the ready endpoint. (default 30s) -s, --specs string Path to the specs folder. (default "./specs") -w, --startup-wait duration Time to wait for services to startup before start testing. (default 1.5s) ```` When you run the tests, KrakenD will tell you the failing ones with a `[KO]` and the working ones with an `[OK]`. For instance: Launch e2e tests with default krakend.json file and a specs/ folder  ``` $krakend e2e [OK] test1 1 test(s) completed Total time: 1.102928274s ``` The `e2e` command starts and shuts down two services during the tests: - A KrakenD instance running on port `8080` or the port you have defined in your configuration, on which all test requests are sent. - An additional backend service on port `8081` (or the one you define with `krakend e2e -p`) with a few utility endpoints that you can use to complement your testing (see below) ## Waiting for readiness There are three options that are meant to deal with the startup times and the readiness of the testing KrakenD, because the integration tests could run before the setup is actually available, making tests to fail. By default the `e2e` command waits 1.5 seconds before running the tests which is suitable in almost all scenarios. Nevertheless, there might be complex setups that require more time to start and you can change this behaviour with three different flags: - `startup-wait` : The hardcoded time you will wait before launching the tests. By default it uses `1500ms` (1.5 seconds). - `ready-url`: An HTTP address that will return a `200` status code when ready. For instance, you could point to `http://localhost:8080/__health` (if you have the default health endpoint enabled) and the command would keep retrying until it gets a `200` or the `ready-url-wait` times out. - `ready-url-wait`: The time you want the tester to keep retrying the readiness. The `startup-wait` and the `ready-url` are mutually exclusive. In case of declaring both, only the latter is used. Example: Launch e2e tests with increased wait time  ``` $krakend e2e -w 2500ms ``` Or also: Launch e2e tests with ready URL  ``` $krakend e2e --ready-url-wait 2500ms --ready-url "http://localhost:8080/__health" ``` ## Skipping tests Only files ending with a `.json` extension are taken into account. If you want to skip a test temporarily, rename the test to a non `.json` extension. For instance, you can rename `test1.json` to `test1.json.skip`. ## Using mock data The main point of integration tests is to test KrakenD configurations (not necessarily the backend content itself). Therefore, all tests expect reproducible outputs. Part of a testing strategy is using mocked data. An easy way to have fake data is to create a `mock` folder with static JSON content and offer it via the [static-filesystem](/docs/enterprise/endpoints/serve-static-content/). By using static files, you could, for instance, create `/mock/` endpoints that return the static files inside your mock folder folder. ## E2E utility backend service In addition to the KrakenD service used to test the configuration, the command `e2e` will start on port `8081`(by default) an additional backend service with the following endpoints you can include in your tests. You can see the code of these endpoints below [here](https://github.com/krakend/krakend-ce/blob/master/tests/integration.go#L424-L429). In addition to the endpoints below, you can also use the [echo](/docs/endpoints/echo-endpoint/) and [debug](/docs/endpoints/debug-endpoint/) endpoints of KrakenD (not this service) ### Endpoint `/param_forwarding/*` An echo endpoint that returns an object containing a map with the request details: - `path`: The URL requested to the backend - `query`: The different query strings passed to the backend - `headers`: All the headers that reached the backend - `foo`: An additional object with a hardcoded value `42` - `body`: A string with the data passed in the request’s body. Only dumped when you call the backend with the query string `?dump_body=1`. For example, a call to `http://localhost:8081/param_forwarding/hey/yo?a=1&b=1&dumb_body=1` produces the response: ```json { "path": "/param_forwarding/hey/yo", "query": { "a": 1, "b": 2, "dump_body": 1 }, "headers": { "Accept-Encoding": [ "gzip" ], "User-Agent": [ "KrakenD Version 2.13.6" ], "X-Forwarded-Host": [ "localhost:8080" ] }, "foo": 42, "body": {} } ``` ### Endpoint `/xml` Returns hardcoded content in XML format. Useful to test a mix of JSON and XML encodings: ```xml Elliot https://facebook.com https://twitter.com https://youtube.com ``` ### Endpoint `/collection/*` Returns a collection of 10 objects (an array) with the number of iteration (`i`) and the `path` used. For instance, calling `http://localhost:8081/collection/hi-there` produces: ```json [ { "i": 0, "path": "/collection/hi-there" }, { "i": 1, "path": "/collection/hi-there" }, { "i": 2, "path": "/collection/hi-there" }, { "i": 3, "path": "/collection/hi-there" }, { "i": 4, "path": "/collection/hi-there" }, { "i": 5, "path": "/collection/hi-there" }, { "i": 6, "path": "/collection/hi-there" }, { "i": 7, "path": "/collection/hi-there" }, { "i": 8, "path": "/collection/hi-there" }, { "i": 9, "path": "/collection/hi-there" } ] ``` ### Endpoint `/delayed/*` Returns an echo endpoint (as in `/param_forwarding`), but it delays the response for `200ms` or any other value you pass using the `-d` flag when running the tests. ### Endpoint `/redirect/*` Returns an HTTP redirection to `/param_forwarding/` using the status code passed by query string with `?status=301`. For instance, http://localhost:8081/redirect/hi-there?status=302\`. ### Endpoint `/jwk/symmetric` Returns a fake signing key that validates demo JWT tokens. To be used when you set the `jwk_url` if you don’t want to issue real tokens ```json { "keys": [ { "kty": "oct", "alg": "A128KW", "k": "GawgguFyGrWKav7AX4VKUg", "kid": "sim1" }, { "kty": "oct", "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", "kid": "sim2", "alg": "HS256" } ] } ``` The key above validates the following `Authorization: Beaerer` demo token: ``` bearer eyJhbGciOiJIUzI1NiIsImtpZCI6InNpbTIifQ.eyJhdWQiOiJodHRwOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzM1Njg5NjAwLCJpc3MiOiJodHRwczovL2tyYWtlbmQuaW8iLCJqdGkiOiJtbmIyM3Zjc3J0NzU2eXVpb21uYnZjeDk4ZXJ0eXVpb3AiLCJyb2xlcyI6WyJyb2xlX2EiLCJyb2xlX2IiXSwic3ViIjoiMTIzNDU2Nzg5MHF3ZXJ0eXVpbyJ9.htgbhantGcv6zrN1i43Rl58q1sokh3lzuFgzfenI0Rk ``` --- # [Extending KrakenD with your code](https://www.krakend.io/docs/extending/) KrakenD is **highly extensible and flexible** and allows developers to extend its functionality through custom code when the built-in features are not enough. Whether you need to add custom logic, integrate specific business rules, or enhance features, KrakenD lets you add extensions coded by you. You don’t need to fork the source code to add your custom logic, as the plugin and scripting system allow you to add custom functionality that is not offered out of the box. These are the two major approaches: - Write a Lua script - Write a Go plugin ## Extending with Lua [Lua](/docs/endpoints/lua/) is an embedded scripting language designed for simplicity and speed. It’s perfect for **quick customizations**, such as: - Request and response manipulation - Custom validations and rules - Dynamic transformations ### Lua advantages - Simplicity: Lua is easy to learn and try. - No Compilation: Changes are applied by editing the Lua script, making it faster to iterate and test. - Runtime Flexibility: Scripts can be dynamically loaded and modified without restarting KrakenD. - Ideal for Small Tasks: like header manipulation, simple data, transformations, or basic validation rules - Portability: Lua scripts do not need modifications on KrakenD upgrades. ### Lua limitations - Limited performance: Lua is interpreted, making it slower for CPU-intensive tasks. - Lack of strong typing: Type safety and error handling are minimal, which could lead to runtime errors. - No user-contributed libraries: You cannot import external libraries. - Testing: Testing Lua scripts requires custom tooling or integration tests, as Lua doesn’t have built-in testing frameworks akin to Go’s tools. See the [Lua documentation](/docs/endpoints/lua/) ## Extending with Go plugins For more **advanced and performance-critical** requirements, KrakenD supports [plugins written in Go](/docs/extending/writing-plugins/). Using Go plugins ensures optimal performance for your extensions, and if you are fluent in Go, they are the best option for extensibility. With Go plugins, you can pretty much do anything you want, including integrating with external services, using databases, and anything you can code. ### Go plugins advantages - High performance: Compiled Go plugins execute at native speed, suitable for heavy processing tasks. - Extensive libraries: A world of Go libraries and an ecosystem for integrating with APIs, databases, and more. - Strong typing: Compile-time checks reduce runtime errors, ensuring predictable behavior. - Testable: - Write unit tests for your plugin logic using Go’s testing framework. - Use CI/CD pipelines for automated testing and validation. - Advanced capabilities with external system integration - **Very** complex data manipulation ### Go plugins limitations - Compilation Overhead: Each change requires recompilation, which is not suitable for “quick hacking” - Deployment Complexity: Plugins are platform-specific (.so files), requiring recompilation for different OS/architecture setups. When you upgrade the KrakenD version, you need to recompile using the builder. - KrakenD Restarts: Reloading the plugin requires restarting KrakenD. - Higher Barrier to Entry: Requires Go expertise and familiarity with KrakenD’s plugin contract. See the [Go plugins documentation](/docs/extending/writing-plugins/) ## Lua or Go? Both Lua and Go plugins allow you to extend KrakenD’s capabilities, but their suitability depends on your use case, **team expertise** (this is key), and performance requirements. Summarizing: - Lua is best for quick, simple, runtime modifications - Go is best for complex, performance-critical, or testable extensions [Get started with Lua](/docs/endpoints/lua/ "Get started with Lua") [Get started with Go Plugins](/docs/extending/writing-plugins/ " Get started with Go Plugins") ## What about forking? Users of the [KrakenD Community Edition](/open-source/) might be tempted to fork the source code to add modifications. Our recommended way to customize KrakenD is always through plugins or scripts, and you **should avoid forking the code** if you want to keep up to date with the product’s progress and security vulnerabilities. We have seen over and over forked projects that are left behind because companies don’t have the resources to keep up. --- # [Writing and building custom plugins](https://www.krakend.io/docs/extending/writing-plugins/) **Plugins are soft-linked libraries**, thus a separated `.so` file that can participate in the processing when running in conjunction with KrakenD. When we talk about plugins, we refer to [**Go plugins**](https://golang.org/pkg/plugin/). You can create custom code, inject it into different parts of KrakenD processing, and still use the official KrakenD software without forking the code. Do I need a plugin? In most cases, you don’t need a custom plugin. The combination of different functionalities offered by the built-in functionality might help you solve a myriad of scenarios, with special mention to [CEL](/docs/endpoints/common-expression-language-cel/), [Martian](/docs/backends/martian/), or even [Lua scripting](/docs/endpoints/lua/). If you’d like to introduce custom business logic, a plugin does not limit what you can do. Also, if you need a lot of performance, a Go plugin is much faster than a Lua script (generally speaking, at least x10). Plugins are **an independent binary** of your own and are not part of KrakenD itself. Plugins allow you to “*drag and drop*” (so to speak) custom functionality that interacts with KrakenD while still using the official binaries without needing to fork the code. Let’s get you started building custom code! ## Types of plugins In the journey of a request and response the data passess different stages (we call them *pipes*) and is validated and transformed back and forth. The different types of plugins available determine **WHEN** you want to inject a customization. Depending on the pipe you are in, you can use a specific type of plugin or another to accomplish the job. A **simplified version** of the [Execution Flow](/docs/design/execution-flow/#execution-flow-pipes) is: ![components-sequence-plugins.mmd diagram](/images/documentation/diagrams/components-sequence-plugins.mmd.svg) **Request:** 1. A user or machine sends an HTTP request to KrakenD. The initial hit is processed by the `router pipe` that decides what to do with it. 2. The `router pipe` **transforms** the HTTP request into one or many `proxy` internal requests -HTTP or not- through a handler function. 3. Each `proxy pipe` fetches the data through the selected `transport` layer. **Response:** 4. When the backend services return the data, the `proxy pipe` manipulates,merges, applies logic… and returns a single context to the `router pipe`. 5. The `router pipe` finally converts the internal proxy response into an HTTP response that is returned to the user **TL;DR**: The `Router` deals with HTTP/gRPC and determines how to map the incoming request to an endpoint, the `Proxy` what to do with it, and the `Transport` how to communicate with the involved service(s). There are **four different types of plugins** you can inject in these pipes, and most of the job is understanding which one is the right kind for the job you want to. Let’s see them all: | Plugin Type | Pipe | Purpose | |-------------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| | server | Router | [HTTP server plugins](/docs/extending/http-server-plugins/) modify the HTTP request and response between the end-user and KrakenD (the server) | | req/resp | Proxy | [Request/Response Modifier plugins](/docs/extending/plugin-modifiers/) modify data (such as headers, body, status code…) | | middleware | Proxy | [Middleware plugins](/docs/enterprise/extending/middleware-plugins/) (**Enterprise only**) inject custom code inside the internals of KrakenD. | | client | Transport | [HTTP client plugins](/docs/extending/http-client-plugins/) modify the request and response between KrakenD and the upstream services (internal HTTP client) | As you can see, each type of plugin belongs to a specific pipe where it is injected. Let’s see now the same execution flow with its possible type of plugins association: ![components-sequence-plugins-injected.mmd diagram](/images/documentation/diagrams/components-sequence-plugins-injected.mmd.svg) As we described, the flow involves both a request and a response, but the `Proxy` pipe is a complex piece that can make that a single request transforms into **multiple API queries** requiring to split and merge requests. Let’s zoom in now the pipes and see **all possible places where you can inject plugins** (you might want to open this image in a new tab): ![Diagram of plugin placement](/images/documentation/diagrams/plugin-types.mmd.svg) What are the capabilities of each plugin? - server : [HTTP server plugins](/docs/extending/http-server-plugins/) (or **http handlers**) belong to the **router layer** and let you **modify the HTTP request** as soon as it hits KrakenD and before the routing to an endpoint happens. They can also decorate the HTTP response before it is delivered to the consumer. For example, you can modify the request, block traffic, make validations, change the final response, connect to third-party services, databases, or anything else you imagine, scary or not, but it does not allow you to modify the internals of KrakenD. If you need multiple plugins, you can stack them. - client : [HTTP client plugins](/docs/extending/http-client-plugins/) (or proxy client plugins) belong to the **proxy layer** and let you change **how KrakenD interacts (as a client) with a specific backend service**. They are as powerful as server plugins, but their working influence is smaller. You can have only **one plugin for the connecting backend call**, because client plugins are **terminators**. When you set a client plugin, you are replacing the internal HTTP KrakenD client, which means you can lose instrumentation and other features. Despite being called HTTP, the only relationship with HTTP is their interface used to encapsulate the plugin. - reqresp : [Request/Response Modifier plugins](/docs/extending/plugin-modifiers/) are strictly **data modifiers** and let you change headers, paths, body, method, status code both in the request or response to and from your backends. A limitation is that request and response are isolated from each other and don’t share context, so you cannot correlate information between the request and the response. These are lighter than the rest but the most frequent ones. - middleware : [Middleware plugins](/docs/enterprise/extending/middleware-plugins/) (**Enterprise only**) allow you to inject any behavior in the **proxy layer** at the endpoint or backend levels, alter the native request or response, raise errors, do premature termination, or connect to third parties. This is the most powerful type of plugin and is the equivalent to forking the source code and adding your components. All different types of plugins let you freely implement your logic without restrictions. However, make sure to write them down, implement the correct interface, and compile them with respect to the requirements. ## Requirements to write plugins If you have gone through the different functionalities of KrakenD and think that a combination of components does not fulfill your needs, writing a plugin can be the solution. This document omits the initial parts of the development lifecycle (plan, analyze, design…) and jumps directly to the **implementation part**. ### System requirements To build custom plugins, you will need **Docker**, and you don’t need Go installed. Yep, you read it right: - **Docker** to run the Plugin builder and compile the code correctly (even if you don’t plan to run the gateway on Docker) - **You don’t need Go in your machine**, because the Plugin builder takes care of the compilation. ### Plugin requirements Writing plugins isn’t complicated per se, but **Go is very strict** with the environment where you compile and load them. When you use the Plugin builder, the complicated parts are taken care of for you. The following principles are essential: - **Right interface**: Your plugin must implement the proper interface for the type of plugin you are coding. - **Same Go version**: Your plugin and KrakenD are compiled with the same Go version. E.g., you cannot build a plugin on Go 1.19 and load it on a KrakenD assembled with Go 1.22. The `krakend version` tells you the Go and Glibc versions. - **Same architecture/platform**: KrakenD and plugins must have been compiled in the same architecture. E.g., you cannot compile a plugin in a Mac natively and use it in a Docker container - **Same shared library versions**: If the KrakenD core also uses external libraries, they must import identical versions. - **Injection in the configuration**: Besides coding and compiling your plugin, you must add it to the `krakend.json` configuration. Yes, it sounds rigorous, but fortunately, KrakenD has developed many tools, so you don’t have to spend time thinking about this. Let’s see them below. ## Writing your first plugin These are all the steps needed to create a plugin from scratch and successfully deploy it: ![plugin-sdlc.mmd diagram](/images/documentation/diagrams/plugin-sdlc.mmd.svg) 1. Choose the [type of plugin](/docs/extending/writing-plugins/#types-of-plugins) you want to create. 2. [Write the Go file](/docs/extending/writing-plugins/#write-the-go-file) with the right interface and custom logic 3. [Check the dependencies](/docs/extending/writing-plugins/#check-the-dependencies) are compatible with the binary 4. [Compile the plugin](/docs/extending/writing-plugins/#compile-the-plugin) for your architecture (not in your machine, but in the builder) 5. [Test the plugin is loadable](/docs/extending/writing-plugins/#test-the-plugin) 6. [Inject your plugin](/docs/extending/writing-plugins/#inject-your-plugin-and-run-krakend) and run KrakenD These steps are detailed below. ## Write the Go file Enterprise users In KrakenD Enterprise, you only need to run the command `krakend plugin init` to create all the boilerplate necessary to build a plugin. [See documentation](/docs/enterprise/extending/generating-plugins/) KrakenD [open-source](/open-source/) users need to create a Go file and implement the interface, as shown in every type of plugin. When the interface is correct, implement the rest of the custom logic you’d like to have. ## Check the dependencies Before compiling the plugin, you must ensure the libraries you use in your code are compatible with KrakenD. To do so, execute the command [`krakend check-plugin`](/docs/extending/check-plugin/) that analyzes your `go.sum` file and warns you about incompatibilities. This is a crucial step because your custom plugins need to match the Go and library versions used to build KrakenD Checking plugins  ``` $krakend check-plugin -v 1.17.0 -s ../myplugin/go.sum 1 incompatibility(ies) found... go have: 1.17.0 want: 1.16.4 ``` Once you have written your plugin with the interface you have chosen, compile it in the same architecture type as follows: Go compilation  ``` $go mod init myplugin go build -buildmode=plugin -o yourplugin.so . ``` ## Compile the plugin Do not compile locally, use the builder Regardless of where you want to use your plugin, compiling your plugins using the right builder is the way to go. The builder makes sure that the **system architecture and Go version** match the destination, making the plugin loadable. If you choose to compile locally without the builder, you will use a different architecture and underlying libc/musl libraries, making your plugin unusable. There are two builders you should use, depending on where you want to run the plugin: | Architecture | Alpine (Docker) | Non-Docker (on-premises) | |---------------------------------------------------------------------|----------------------------------------------------------|------------------------------------------------------------------------| | [AMD64](/docs/extending/writing-plugins/#compile-plugins-for-amd64) | `krakend/builder:2.13.8` | `krakend/builder:2.13.8-linux-generic` | | [ARM64](/docs/extending/writing-plugins/#compile-plugins-for-arm64) | `krakend/builder:2.13.8` with cross-compile instructions | `krakend/builder:2.13.8-linux-generic` with cross-compile instructions | When using Docker to deploy your gateway, our official KrakenD container uses [**Alpine**](https://hub.docker.com/_/alpine) as the base image. Therefore, to use your custom plugins, they must compile using the Alpine builder. ### Compile plugins for AMD64 To build your plugin for **Docker targets**, you only need to execute the following command inside the folder where your plugin is: Build your plugin for Alpine  ``` $docker run -it -v "$PWD:/app" -w /app krakend/builder:2.13.8 go build -buildmode=plugin -o yourplugin.so . ``` The command will generate a `yourplugin.so` file (name it as you please) that you can now copy into a `krakend:2.13.8` Docker image, and load it as described in [injecting plugins](/docs/extending/injecting-plugins/). **Never use a tag that mismatches the builder and KrakenD**. If you want to load the plugin in a KrakenD version `x.y.x` make sure to build it on a builder `x.y.z`. Using `.so` files that were compiled in a builder with a different version, will mostly fail. To build the plugin for **on-premises installations**, use the following command instead: Build your plugin for Non-Docker  ``` $docker run -it -v "$PWD:/app" -w /app krakend/builder:2.13.8-linux-generic go build -buildmode=plugin -o yourplugin.so . ``` ### Compile plugins for ARM64 Regardless of your host architecture when running the Docker builder, the **default plugin architecture target is AMD64**. Therefore, if you want to test the plugin on **ARM64** (e.g., a Macintosh, Raspberry, etc.), you must cross-compile it. This is because the plugin builder is available for AMD64 only, as emulation does not work well on Go compilation. To cross-compile a plugin for **Docker ARM64**, you need to add extra flags when compiling the plugin: Build your plugin for Alpine ARM64  ``` $docker run -it -v "$PWD:/app" -w /app \ -e "CGO_ENABLED=1" \ -e "CC=aarch64-linux-musl-gcc" \ -e "GOARCH=arm64" \ -e "GOHOSTARCH=amd64" \ krakend/builder:2.13.8 \ go build -ldflags='-extldflags=-fuse-ld=bfd -extld=aarch64-linux-musl-gcc' \ -buildmode=plugin -o yourplugin.so . ``` And the same command, changing the builder, when you need **on-premises** plugins for ARM64: Build your plugin for non-Alpine ARM64  ``` $docker run -it -v "$PWD:/app" -w /app \ -e "CGO_ENABLED=1" \ -e "CC=aarch64-linux-gnu-gcc" \ -e "GOARCH=arm64" \ -e "GOHOSTARCH=amd64" \ krakend/builder:2.13.8-linux-generic \ go build -ldflags='-extldflags=-fuse-ld=bfd -extld=aarch64-linux-gnu-gcc' \ -buildmode=plugin -o yourplugin.so . ``` Remember that the resulting plugin will only work on **ARM64** and that you cannot reuse plugins from one platform into another. ## Test the plugin Once your `.so` file is available, you must check that the plugin is loadable by KrakenD. You can test the plugin using the [test command](/docs/extending/test-plugin/) Here’s an example: Testing the plugin  ``` $krakend test-plugin -smc yourplugin.so [OK] MODIFIER yourplugin.so ``` ## Inject your plugin and run KrakenD The final step before running the plugin is including the configuration of the new plugin. To inject a plugin, you must copy it into a KrakenD directory and add it to the `plugin` configuration at the service level of your configuration. Then, add your plugin namespace where necessary across the configuration. When you run KrakenD, the plugin shows as loaded in the log. See the detailed information to [inject plugins](/docs/extending/injecting-plugins/)) --- # [Automatically generate boilerplate for Go plugins (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/extending/generating-plugins/) The command `krakend plugin init` generates the necessary boilerplate code to start working with plugins. This command provides a structured starting point for plugin development, ensuring that the required interfaces are included from the outset. By using this command, you can focus on writing the plugin logic without manually creating the files for each possible interface or setting up the build environment. The recommended workflow involves running KrakenD inside a container while mounting the directory where the plugin code will be generated. You must map the KrakenD **Enterprise license** into the container for successful execution. This is how the command looks like: Plugin init command  ```` $krakend plugin init -h ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.6-ee Generates the necessary boilerplate to develop a new plugin Usage: krakend plugin init [flags] Examples: krakend plugin init --type handler --name myplugin Flags: -h, --help help for init -n, --name string Plugin name -o, --out string Output path. Default value: - -t, --type string Plugin type. One of: handler, client, modifier, middleware (default "handler") ```` To build and compile a plugin, follow the steps below. ## 1. Generate the go code For instance, let’s build a plugin named `myplugin` of the `handler` type. Notice that this is a **KrakenD image**. Generate a plugin  ``` $docker run --rm -it \ -w /app \ -e "KRAKEND_LICENSE_PATH=/LICENSE"\ -v "$PWD/LICENSE:/LICENSE:ro" \ -v "$PWD/plugins:/app" \ krakend/krakend-ee:2.13.6 plugin init --name myplugin --type handler ``` The command above **assumes you have a `LICENSE` file in the current directory**. This is what you get: - A new folder `plugins/handler-myplugin` with the source code - Inside, a file `myplugin.go` that implements the complete interface of the chosen plugin type - A `Makefile` to compile your plugin (**not in this container**) ## 2. Initialize Go modules With the source files ready, you should start with the Go modules setup. The following command must be executed in the **KrakenD builder image**. Initialize go modules  ``` $cd plugins/handler-myplugin docker run -it -v $PWD:/app -w /app krakend/builder-ee:2.13.6 make go.mod ``` ## 3. Compile the plugin With the source files ready, you can compile the plugin. The following commands assume that you want to run this plugin inside the official image `krakend/krakend-ee:2.13.6` or in a Docker file that extends `FROM` this one. Compile the plugin  ``` $docker run -it -v $PWD:/app -w /app krakend/builder-ee:2.13.6 make amd64 ``` This will generate the compiled plugin binary `myplugin-amd64.so` compatible with KrakenD running on AMD64 Docker. Compilation for non-Docker Use the image `krakend/builder-ee::2.13.6-linux-generic` to compile your plugins that run in a non-Docker environment. In addition, if your architecture is ARM64 you must edit the `Makefile` and switch the commented `CROSSCOMPILER` variable. ## 4. Inject the plugin and run Now, you should [inject the plugin](/docs/enterprise/extending/injecting-plugins/) in your configuration and run KrakenD. --- # [Checking dependencies of plugins](https://www.krakend.io/docs/extending/check-plugin/) The Go plugin system requires you to compile the main application and its plugins using the same ecosystem. This means that KrakenD and your plugins must use the same Go version, the same version of any **imported libraries**, the same system architecture, and the same GLIBC/MUSL libraries. Therefore, knowing in advance that you are using libraries that are incompatible with KrakenD when [writing custom plugins](/docs/extending/writing-plugins/) is key. The `krakend check-plugin` command helps you validate the part of the **dependencies used by your plugins**, which will determine whether the plugin is compatible. Go programs define their dependencies in their `go.sum` file, and this is all you need to check the compatibility. ## Usage of the check command The command compares your plugin’s `go.sum` file with the libraries initially used to compile the running binary. A detailed list will be shown if there are any incompatibilities between your plugin and KrakenD. If you integrate this command as part of your CI/CD pipeline or `Dockerfile` build, it will exit with a status code `0` when your plugin’s libraries are compatible with KrakenD and with a status code `1` when they are not. The `krakend check-plugin` command accepts the following options: Usage of KrakenD check  ```` $krakend check-plugin -h ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.8 Checks your plugin dependencies are compatible and proposes commands to update your dependencies. Usage: krakend 2.13.8 [flags] Examples: krakend 2.13.8 -g 1.19.0 -s ./go.sum -f Flags: -f, --format Shows fix commands to update your dependencies -g, --go string The version of the go compiler used for your plugin (default "1.22.11") -h, --help help for check-plugin -l, --libc string Version of the libc library used -s, --sum string Path to the go.sum file to analyze (default "./go.sum") ```` ## Flags Use `krakend check-plugin` in combination with the following flags: - `-f` or `--format` to let KrakenD suggest you about the `go get` commands you should launch. - `-s` or `--sum` to specify the path to the `go.sum` file of your plugin. - `-g` or `--go` to specify the Go version you are using to compile the plugin - `-l` or `--libc` to specify the libc version installed in the system. The libc version must have the prefix `MUSL-`, `GLIBC-`, and `DARWIN-`. For instance, a plugin in Mac Monterrey might use `DARWIN-12.2.1`, an Alpine container will need something like `MUSL-1.2.2`, and a Linux box will have `GLIBC-2.32`. To know your glibc version execute the [Find GLIBC script](https://github.com/krakend/krakend-ce/blob/master/find_glibc.sh). When there are incompatibilities because the operating system is different, but the libraries (glibc or musl) are in the **exact same version**, it is safe to ignore them. The example below shows an example of a plugin that uses several libraries that are incompatible with KrakenD: Checking a failing plugin example  ``` $krakend check-plugin --go 1.17.7 --libc MUSL-1.2.2 --sum ../plugin-tools/go.sum 15 incompatibility(ies) found... go have: 1.17.0 want: 1.16.4 libc have: MUSL-1.2.2 want: GLIBC-2.31 github.com/gin-gonic/gin have: v1.6.3 want: v1.7.7 github.com/go-playground/locales have: v0.13.0 want: v0.14.0 github.com/go-playground/universal-translator have: v0.17.0 want: v0.18.0 github.com/go-playground/validator/v10 have: v10.2.0 want: v10.9.0 github.com/golang/protobuf have: v1.3.3 want: v1.5.2 github.com/json-iterator/go have: v1.1.9 want: v1.1.12 github.com/leodido/go-urn have: v1.2.0 want: v1.2.1 github.com/mattn/go-isatty have: v0.0.12 want: v0.0.14 github.com/modern-go/concurrent have: v0.0.0-20180228061459-e0a39a4cb421 want: v0.0.0-20180306012644-bacd9c7ef1dd github.com/modern-go/reflect2 have: v0.0.0-20180701023420-4b7aa43c6742 want: v1.0.2 github.com/ugorji/go/codec have: v1.1.7 want: v1.2.6 golang.org/x/sys have: v0.0.0-20200116001909-b77594299b42 want: v0.0.0-20211004093028-2c5d950f24ef golang.org/x/text have: v0.3.2 want: v0.3.7 ``` ## Fixing plugin dependencies A quick attempt to fix your dependencies is to run the command with the `-f` flag, which will suggest a series of `go get` commands that you can execute to solve the incompatibilities. For instance: Fixing dependencies  ``` $krakend check-plugin -s ~/Downloads/go.sum -f 12 incompatibility(ies) found... go get cloud.google.com/go/pubsub@v1.19.0 go get github.com/census-instrumentation/opencensus-proto@v0.3.0 go get github.com/google/martian@v2.1.1-0.20190517191504-25dcb96d9e51+incompatible go get github.com/googleapis/gax-go/v2@v2.2.0 go get github.com/hashicorp/golang-lru@v0.5.4 go get golang.org/x/mod@v0.6.0-dev.0.20211013180041-c96bc1413d57 go get golang.org/x/oauth2@v0.0.0-20220309155454-6242fa91716a go get golang.org/x/sys@v0.0.0-20220330033206-e17cdc41300f go get golang.org/x/time@v0.0.0-20220224211638-0e9765cccd65 go get google.golang.org/api@v0.74.0 go get google.golang.org/genproto@v0.0.0-20220502173005-c8bf987b8c21 go get google.golang.org/grpc@v1.46.0 ``` Copy and paste your terminal’s `go get` commands to update the dependencies. The commands are in alphabetical order. You might need to use the `-f` several times and use `go mod tidy` as well. --- # [Test plugins after compiling them](https://www.krakend.io/docs/extending/test-plugin/) Plugins are essential extensions to the KrakenD gateway, enhancing functionality without modifying the core codebase. Due to their reliance on specific versions of KrakenD, libraries, or system architecture, plugins can face compatibility issues following updates or modifications. So, when you have written a new plugin, and compiled it you still need to see that is loadable into KrakenD. Recompile plugins when you upgrade KrakenD When you upgrade KrakenD to another version you must recompile your plugins using the [builder matching the version](/docs/extending/writing-plugins/#plugin-builder). Even if a plugin compiles and passes initial tests on its own, it might fail when loaded into KrakenD. This could be due to various reasons such as compilation on a different architecture, mismatches in Go version, or discrepancies in library versions used by both the plugin and KrakenD. The `test-plugin` command offers a real-scenario opportunity to test a compiled binary (usually a `.so` file) and verify if KrakenD can successfully load it. ## Testing a compiled plugin before it goes live The `test-plugin` command requires you to pass the type of plugin you would like to test, and the path to the compiled binary. The command accepts the following options: Usage of test-plugin  ```` $krakend test-plugin -h ╓▄█ ▄▄▌ ╓██████▄µ ▐███ ▄███╨▐███▄██H╗██████▄ ║██▌ ,▄███╨ ▄██████▄ ▓██▌█████▄ ███▀╙╙▀▀███╕ ▐███▄███▀ ▐█████▀"╙▀▀"╙▀███ ║███▄███┘ ███▀""▀███ ████▀╙▀███H ███ ╙███ ▐██████▌ ▐███⌐ ,▄████████M║██████▄ ║██████████M███▌ ███H ███ ,███ ▐███╨▀███µ ▐███ ███▌ ,███M║███╙▀███ ███▄```▄▄` ███▌ ███H ███,,,╓▄███▀ ▐███ ╙███▄▐███ ╙█████████M║██▌ ╙███▄`▀███████╨ ███▌ ███H █████████▀ `` `'` Version: 2.13.8 Tests that one or more plugins are loadable into KrakenD. Usage: krakend test-plugin [flags] [artifacts] Examples: krakend test-plugin -scm ./plugins/my_plugin.so ./plugins/my_other_plugin.so Flags: -c, --client The artifact should contain a Client Plugin. -h, --help help for test -w, --middleware The artifact should contain a Middleware Plugin. -m, --modifier The artifact should contain a Req/Resp Modifier Plugin. -s, --server The artifact should contain a Server Plugin. ```` You can pass as many arguments as plugins you want to check at once. For instance, if you want to test that `plugin1.so` and `plugin2.so` are loadable as server plugins, you could execute `krakend test-plugin -s plugin1.so plugin2.so`. You should provide at least on of the flags `-c` (client plugins), `-m` (req/resp modifiers), or `-s` (server plugins). Here’s an output example: Checking a failing plugin example  ``` $krakend test-plugin -smc plugin1.so plugin2.so [KO] SERVER plugin1.so: The plugin does not contain a HandlerRegisterer. [KO] MODIFIER plugin1.so: The plugin does not contain a ModifierRegisterer. [OK] CLIENT plugin1.so [OK] CLIENT plugin2.so [OK] SERVER plugin2.so [OK] MODIFIER plugin2.so [KO] 2 tested plugin(s) in 13.498341ms. 1 plugin(s) failed. ``` The command will exit with a status code `1` when it fails, so if you integrate it in a CI/CD pipeline it will stop. When you find issues, use the `check-plugin` tool in development The command exits with a **code of `1`** upon failure, allowing for integration into CI/CD pipelines to halt progress. If you encounter issues, consider using the [check-plugin](/docs/extending/check-plugin/) tool during development to diagnose and resolve problems effectively. --- # [Enterprise plugins (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/overview/plugins/) Some of the functionality offered by KrakenD Enterprise is presented as plugins, and other as components. Although functionality offered as plugins is decreasing, it’s important to understand a few concepts and particularities of plugins before you start using them. For each functionality of Enterprise offered as a plugin, you will see in its documentation how to use it. What is a plugin? **A plugin is a soft-linked library**; thus, a **separated binary file** (`.so`), that when running in conjunction with KrakenD, can participate in the processing. Plugins and middlewares are close concepts, but while middlewares are part of the KrakenD binary, plugins are not. ## Understanding plugins KrakenD offers functionality using different strategies. Without entering in a lot of detail, we’ll say that both the Community Edition (CE) and the Enterprise Edition (EE) products are final binaries built through the composition of the Lura [framework](https://github.com/luraproject/lura), which brings the basic API Gateway functionality, and many other pieces to extend its capabilities. We refer to all these extra surrounding pieces as **middleware** or **components** and are usually configured using the (probably) already familiar `extra_config`. The “extra” comes from the “you are not part of the core functionality”. ## Available plugins and types All available Enterprise plugins, in any distribution form (Linux package or Docker), are copied in the folder `/opt/krakend/plugins`. If you do a `ls` on the folder, you’ll see many `.so` files. These are the binaries that KrakenD **could** soft-link and load during startup. All plugins have their documentation. You can see the list of plugins available today with a `ls /opt/krakend/plugins` or: Getting the plugins list from a terminal  ``` $docker run --rm --entrypoint=/bin/ls krakend/krakend-ee:2.13.6 /opt/krakend/plugins ``` There are four types of plugins in KrakenD: 1. **HTTP handler plugins** (router plugins): Let you modify the request of the user before KrakenD starts processing in the router layer 2. **HTTP client plugin** (proxy plugins): Let you change how KrakenD interacts (as a client) with your backend services. 3. **Response modifier plugins](/docs/extending/plugin-modifiers/)**: Let you modify the responses received from your backends 4. [**Request modififer plugins**](/docs/extending/plugin-modifiers/): Let you modify the requests sent to your backends The place where each plugin acts is depicted here: ![Types of plugins](/images/documentation/krakend-plugins.png) ## Enabling Enterprise plugins KrakenD registers plugins during startup according to its plugin configuration: ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" } } ``` You must add the `plugin` keyword at the root of your configuration to let KrakenD know the rules to register plugins during startup. - `folder`: The path in the filesystem where all the plugins you **want to load** are. **MUST END IN SLASH**. The folder can be a relative or absolute path. KrakenD Enterprise has in the path `/opt/krakend/plugins/` all plugins. - `pattern`: The pattern narrows down the contents of the folder. It represents the substring that must be present in the plugin name to load. In the example above, any plugin with the `.so` extension will be loaded. You could also use any prefix or suffix to match the content or even the full name of a single plugin. For instance, if you just want to load the rewrite plugin, use `"pattern":"krakend-rewrite.so"`, and prevent loading unwanted plugins. Avoid loading all plugins Through all the documentation, and for an easier reference when testing and copy-pasting, **all examples load all the Enterprise plugins** in the system as the folder `/opt/krakend/plugins` is passed. The best practice is either **copy, move, symlink, or delete** plugins in your Dockerfile to leave only what you need. At this point and with the previous configuration, you have registered plugins. The next step is to start using them. ### Declaring router plugins All router plugins are declared at the root level of the configuration file. You need to list all the plugins that will act in the `name` list. The order is important (we will see it in a while). The configuration looks like this: ```json { "version": 3, "plugin": { "pattern":".so", "folder": "./plugins-in-use/" }, "extra_config": { "plugin/http-server": { "name": ["plugin1", "plugin2", "..." ], "plugin1": { "settings-of-plugin1": "here" }, "plugin2": { "settings-of-plugin2": "here" } } } } ``` As you can see you need a top-level `extra_config` with the `plugin/http-server` key. An inside the following: - `name`: The list of all the plugins that have to be loaded - `plugin1`: Replace `plugin1`, `plugin2`, etc., by the name of the plugins you have declared in the `name` list. ### Declaring backend plugins Backend plugins need to declare inside the backends’ `extra_config` the namespace `plugin/http-client`. An inside the `name` of the plugin you want to load. Each backend can load only one plugin. For instance: ```json { "endpoint": "/redirect", "output_encoding": "no-op", "backend": [ { "url_pattern": "/__debug", "encoding": "no-op", "extra_config": { "plugin/http-client": { "name": "no-redirect" } } } ] } ``` ## Multiple plugins and execution order You can add only **one proxy plugin** for each backend, while you can have **multiple router plugins** as you have seen before. Router plugins modify the request of the user before KrakenD starts processing it. They can even prevent that the request reaches KrakenD at all, so make sure the order is as you expect. Let’s simplify the names of the plugins for better understanding. If we define a configuration like this one: ```json { "plugin/http-server": { "name": ["A", "B", "C" ] } } ``` Then KrakenD will wrap their execution in a function like this: `A(B(C()))`. The outcome is that the last plugin C is executed in the first place, then its result passes to B, and then to A. ## Verifying correct load of plugins When KrakenD starts, you can see which plugins are loaded in the log. This is an example of some lines: ``` virtualhost handler plugin loaded!!! url-rewrite handler plugin loaded!!! ... 2020/01/31 20:20:40 DEBUG: http-server-handler: injecting plugin url-rewrite ``` If the plugin failed to load for whatever reason, you can see it too: ``` 2020/01/31 20:23:50 WARNING: loading plugins: plugin loader found 10 error(s): opening plugin 0 (/opt/krakend/pluginsbasic-auth.so): plugin.Open("/opt/krakend/pluginsbasic-auth.so"): realpath failed ``` In the previous example, the `folder` configuration didn’t end in a slash. --- # [Injecting Plugins into KrakenD API Gateway - Extensibility Guide](https://www.krakend.io/docs/extending/injecting-plugins/) You have developed your plugin or are in the middle of it. There are two phases to take into account: - Loading the plugin - Injecting the plugin in a specific place ## Loading the plugin KrakenD registers plugins **during startup** according to its plugin configuration: ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" } } ``` Add the `plugin` keyword at the root of your configuration to let KrakenD know the rules to register plugins. The **mandatory** options you need to declare are: - `folder` (*string*): The directory path in the filesystem where all the plugins you **want to load** are. The folder can be a relative or absolute path!. E.g: KrakenD Enterprise stores the plugins in the path `/opt/krakend/plugins/`. - `pattern` (*string*): The pattern narrows down the folder’s contents and acts as a filter. It represents the **substring that must be present** in the plugin name to load. KrakenD will load any plugin with a `.so` extension in the example above. You could also use any prefix or suffix to match the content or even the full name of a single plugin. For instance, if you want to load the rewrite plugin, use `"pattern":"krakend-rewrite.so"`, or use `-prod.so` to load all safe production plugins ending with that suffix. The rules are up to you. Place the plugin in the folder you have declared in the configuration and start KrakenD. At this point and with the previous configuration, you have **registered plugins during startup**, and you should see a line early in the logs when starting KrakenD. The log lines depend on the type of plugin you have chosen. An example: ``` INFO [SERVICE: Handler Plugin] Total plugins loaded: 1 ``` The system loads the plugins according to their operative system directory scan order (e.g., alphabetically). ### Checking the plugin registration When the service starts, **KrakenD doesn’t know the type of plugin** you have coded until it tries to register it, and **it will try to register it as all known types** (even if it doesn’t match). You will see this activity in the logs when using a `DEBUG` log level. In most cases, you will create your plugin for a single type, but it doesn’t mean you cannot implement more than one type of plugin per file. The registration attempts are reflected in the logs, and you will see log lines that could look like errors, but they are not! Log line ‘symbol X not found in plugin Y’ This logline is not an error (it’s a DEBUG message). Instead, it tells you that your plugin cannot register itself as one of the other types of plugins you are not implementing. **It’s all good** (unless of course you were trying to register this type). For example, let’s see how the different plugins log into KrakenD: - `client-example.so` (An [HTTP client plugin](/docs/extending/http-client-plugins/)) - `server-example.so` (An [HTTP server plugin](/docs/extending/http-server-plugins/)) - `request-modifier.so` (A [request/response modifier plugin](/docs/extending/plugin-modifiers/)) - `middleware.so` (A [Middleware plugin](/docs/enterprise/extending/middleware-plugins/)) Enterprise In the logs, we will see how each plugin fails to register as the rest of the types they don’t implement: ```txt Parsing configuration file: krakend.json ▶ INFO Listening on port: 8080 ▶ DEBUG [PLUGIN: client-example] Logger loaded ▶ DEBUG [SERVICE: Executor Plugin] plugin #1 (request-modifier.so): plugin: symbol ClientRegisterer not found in plugin mytest ▶ DEBUG [SERVICE: Executor Plugin] plugin #2 (server-example.so): plugin: symbol ClientRegisterer not found in plugin myserver ▶ INFO [SERVICE: Executor Plugin] Total plugins loaded: 1 ▶ DEBUG [PLUGIN: server-example] Logger loaded ▶ DEBUG [SERVICE: Handler Plugin] plugin #0 (client-example.so): plugin: symbol HandlerRegisterer not found in plugin myclient ▶ DEBUG [SERVICE: Handler Plugin] plugin #1 (request-modifier.so): plugin: symbol HandlerRegisterer not found in plugin mytest ▶ INFO [SERVICE: Handler Plugin] Total plugins loaded: 1 ▶ DEBUG [PLUGIN: request-modifier] Logger loaded ▶ DEBUG [SERVICE: Modifier Plugin] plugin #0 (client-example.so): plugin: symbol ModifierRegisterer not found in plugin myclient ▶ DEBUG [SERVICE: Modifier Plugin] plugin #2 (server-example.so): plugin: symbol ModifierRegisterer not found in plugin myserver ▶ INFO [SERVICE: Modifier Plugin] Total plugins loaded: 1 ``` The `INFO` log level tells you what is going on, but notice how the highlighted `DEBUG` messages fail to register for the type they are not. This is expected. The Enterprise Edition will also print the same line for Middleware plugins. ## Injecting the plugin At this point, KrakenD has registered the plugin and is ready to use. The next step is to inject the plugin somewhere in the configuration. The configuration entry depends entirely on the type of plugin you are using and what you have coded. ### Injecting HTTP server plugins Below there is a sample configuration for an HTTP server fake plugin: ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "extra_config": { "plugin/http-server": { "name": ["my_plugin", "another_plugin_maybe" ], "my_plugin": { "some_flag_in_my_plugin": true } } } } ``` - `name` (*list*): The list of all the plugins you want to inject in reverse order. - After the name, you can include any key that your plugin requires to load configuration. In the example `my_plugin`. #### Plugin execution order Plugins **execute** according to the resulting order of **STACKING** when reading the `name` array. If you have under the plugin configuration `"name":["A","B","C"]`, then they wrap each other in this order, meaning that they will compute with `A(B(C()))`. So as you can see, `C` is executed in the first place, then `B`, then `A` (reverse order than declared) ### Injecting HTTP client plugins HTTP client plugins live in the backend’s `extra_config`, and you can declare one plugin per backend. ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/opt/krakend/plugins/" }, "endpoints":[ { "endpoint": "/foo", "backend": [ { "url_pattern": "/__debug", "extra_config": { "plugin/http-client": { "name": "your-plugin" } } } ] } ] } ``` - `name` (*string*): The name of the plugin you want to inject. ### Injecting request and response modifier plugins You can place the request/modifier plugins at the `endpoint` or the `backend` level. In both cases, you can inject several plugins used in the order you declare them. ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/path/to/your/plugin/folder/" }, "endpoints": [ { "endpoint": "/github/orgs/{org}", "extra_config":{ "plugin/req-resp-modifier":{ "name":["your-plugin"] } }, "backend":[ { "url_pattern": "/orgs/{org}", "extra_config":{ "plugin/req-resp-modifier":{ "name":["your-plugin"] } } } ] } ] } ``` ### Injecting middleware plugins This is an Enterprise feature only. You can place the middleware plugins at the `endpoint` or the `backend` level. In both cases, you can inject several plugins used in the order you declare them. ```json { "version": 3, "plugin": { "pattern":".so", "folder": "/path/to/your/plugin/folder/" }, "endpoints": [ { "endpoint": "/foo", "extra_config":{ "plugin/middleware":{ "name":["your-plugin-A"], "your-plugin-A": { "your-A-setting": true } } }, "backend":[ { "url_pattern": "/bar", "extra_config":{ "plugin/middleware":{ "name":["your-plugin-A","your-plugin-B"], "your-plugin-A": { "your-A-setting": true }, "your-plugin-B": { "your-B-setting": true } } } } ] } ] } ``` --- # [HTTP Server Plugins for KrakenD API Gateway](https://www.krakend.io/docs/extending/http-server-plugins/) The HTTP server plugins (codenamed as *handler plugins*) belong to the **router layer** and let you modify the requests before KrakenD starts processing them or modify the final response back to the user. The HTTP handler plugin lets you write your servers and HTTP middlewares right in KrakenD and enables you to implement anything you can imagine. This plugin type is so powerful that you can use it to implement custom monetization, tracking, tenant control, protocol conversion, and heavy modifications, for example. What is a Handler? A Handler responds to an HTTP request and is an interface modeling an HTTP processor. From KrakenD’s perspective, **your handler plugins are black boxes** that expose an `http.Handler`, and you can do anything you want inside them. Each plugin is wrapping the next element in the pipe, meaning that for some operations, **it must deal with an HTTP request and response writer**. If you chain several plugins, you will add **two extra cycles** of decoding and encoding the body. From a performance perspective is better having one plugin doing two things, than two plugins doing one thing: ![](/images/documentation/http-handler-plugin.png "HTTP handler plugin") ## HTTP handler interface Writing plugins Read the introduction to [writing plugins](/docs/extending/writing-plugins/) for compilation and configuration options if you haven’t done it yet. To start with a *hello world* for your first plugin, you have to implement the plugin server interface provided in the [Go documentation](https://godoc.org/github.com/luraproject/lura/transport/http/server/plugin). A step-by-step example follows below. ### Example: Building your first server plugin The easiest way to demonstrate how HTTP server plugins work is with a hello world plugin. So let’s start by creating a new Go project named `krakend-server-example`: ``` mkdir krakend-server-example cd krakend-server-example go mod init krakend-server-example ``` Now we have to create a file `main.go` with the content below: ```go // SPDX-License-Identifier: Apache-2.0 package main import ( "context" "errors" "fmt" "html" "net/http" ) // pluginName is the plugin name var pluginName = "krakend-server-example" // HandlerRegisterer is the symbol the plugin loader will try to load. It must implement the Registerer interface var HandlerRegisterer = registerer(pluginName) type registerer string func (r registerer) RegisterHandlers(f func( name string, handler func(context.Context, map[string]interface{}, http.Handler) (http.Handler, error), )) { f(string(r), r.registerHandlers) } func (r registerer) registerHandlers(_ context.Context, extra map[string]interface{}, h http.Handler) (http.Handler, error) { // If the plugin requires some configuration, it should be under the name of the plugin. E.g.: /* "extra_config":{ "plugin/http-server":{ "name":["krakend-server-example"], "krakend-server-example":{ "path": "/some-path" } } } */ // The config variable contains all the keys you have defined in the configuration // if the key doesn't exists or is not a map the plugin returns an error and the default handler config, ok := extra[pluginName].(map[string]interface{}) if !ok { return h, errors.New("configuration not found") } // The plugin will look for this path: path, _ := config["path"].(string) logger.Debug(fmt.Sprintf("The plugin is now hijacking the path %s", path)) // return the actual handler wrapping or your custom logic so it can be used as a replacement for the default http handler return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { // If the requested path is not what we defined, continue. if req.URL.Path != path { h.ServeHTTP(w, req) return } // The path has to be hijacked: fmt.Fprintf(w, "Hello, %q", html.EscapeString(req.URL.Path)) logger.Debug("request:", html.EscapeString(req.URL.Path)) }), nil } func main() {} // This logger is replaced by the RegisterLogger method to load the one from KrakenD var logger Logger = noopLogger{} func (registerer) RegisterLogger(v interface{}) { l, ok := v.(Logger) if !ok { return } logger = l logger.Debug(fmt.Sprintf("[PLUGIN: %s] Logger loaded", HandlerRegisterer)) } type Logger interface { Debug(v ...interface{}) Info(v ...interface{}) Warning(v ...interface{}) Error(v ...interface{}) Critical(v ...interface{}) Fatal(v ...interface{}) } // Empty logger implementation type noopLogger struct{} func (n noopLogger) Debug(_ ...interface{}) {} func (n noopLogger) Info(_ ...interface{}) {} func (n noopLogger) Warning(_ ...interface{}) {} func (n noopLogger) Error(_ ...interface{}) {} func (n noopLogger) Critical(_ ...interface{}) {} func (n noopLogger) Fatal(_ ...interface{}) {} ``` The plugin above aborts the request and replies itself printing a `Hello, %q` without actually passing the request to the endpoint. It is a simple example, but it shows the necessary structure to start working with plugins. If you look now closely at the plugin code, notice that the loader looks for the symbol `HandlerRegisterer` and, if found, the loader checks if the symbol implements the `plugin.Registerer` interface. Once the plugin is validated, the loader registers handlers from the plugin by calling the exposed `RegisterHandlers` method. With the `main.go` file saved, it’s time to build and test the plugin. If you added more code and external dependencies, you must run a `go mod tidy` before the compilation, but it is unnecessary for this example. For compiling Go plugins, the flag `-buildmode=plugin` is required. The command is:   ``` $go build -buildmode=plugin -o krakend-server-example.so . ``` If you are using Docker and wanting to load your plugin on Docker, compile it in the [Plugin Builder](/docs/extending/writing-plugins/#plugin-builder) for an easier integration. Build your plugin  ``` $docker run -it -v "$PWD:/app" -w /app \ krakend/builder:2.13.8 \ go build -buildmode=plugin -o krakend-server-example.so . ``` There is no output for this command. Now you have a file `krakend-server-example.so`, the binary that KrakenD has to side load. Remember that you cannot use this binary in a different architecture (e.g., compiling the binary in Mac and loading it in a Docker container). The plugin is ready to use! You can now load your plugin in the configuration. Add the `plugin` and `extra_config` entries in your configuration. Here’s an example of `krakend.json`: ```json { "version": 3, "plugin": { "pattern": ".so", "folder": "./krakend-server-example/" }, "endpoints": [ { "endpoint": "/test/{id}", "backend": [ { "host": [ "http://localhost:8080" ], "url_pattern": "/__health" } ] } ], "extra_config": { "plugin/http-server": { "name": ["krakend-server-example"], "krakend-server-example": { "path": "/hijack-me" } } } } ``` Start the server with `krakend run -dc krakend.json`. When you run the server, the expected output (with `DEBUG` log level) is: ``` yyyy/mm/dd hh:mm:ss KRAKEND ERROR: [SERVICE: Logging] Unable to create the logger: getting the extra config for the krakend-gologging module yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: Plugin Loader] Starting loading process yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: Executor Plugin] plugin #0 (krakend-server-example/krakend-server-example.so): plugin: symbol ClientRegisterer not found in plugin krakend-server-example yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [PLUGIN: krakend-server-example] Logger loaded yyyy/mm/dd hh:mm:ss KRAKEND INFO: [SERVICE: Handler Plugin] Total plugins loaded: 1 yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: Modifier Plugin] plugin #0 (krakend-server-example/krakend-server-example.so): plugin: symbol ModifierRegisterer not found in plugin krakend-server-example yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: Plugin Loader] Loading process completed yyyy/mm/dd hh:mm:ss KRAKEND INFO: Starting the KrakenD instance yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [ENDPOINT: /test/:id] Building the proxy pipe yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [BACKEND: /__health] Building the backend pipe yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [ENDPOINT: /test/:id] Building the http handler yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [ENDPOINT: /test/:id][JWTSigner] Signer disabled yyyy/mm/dd hh:mm:ss KRAKEND INFO: [ENDPOINT: /test/:id][JWTValidator] Validator disabled for this endpoint yyyy/mm/dd hh:mm:ss KRAKEND INFO: [SERVICE: Gin] Listening on port: 8080 yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: The plugin is now hijacking the path /hijack-me yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [PLUGIN: Server] Injecting plugin krakend-server-example ... ``` Let’s take a closer look at the log. First, notice that the plugin tried registering itself for each plugin type (`[SERVICE: Executor Plugin]`, `[SERVICE: Handler Plugin]`, and `[SERVICE: Modifier Plugin]`), but we are only building a Handler Plugin in this case. As we are implementing only one of the types, the other two types will fail to load (`symbol not found`). The logline is expected and is not an error but just an informational `DEBUG` message. The essential lines are: - `[PLUGIN: krakend-server-example] Logger loaded` printed by the plugin logger we introduced in our code telling us that the plugin is loaded - The `[SERVICE: Handler Plugin] Total plugins loaded: 1` telling us there is one type of plugin for this type - `[PLUGIN: Server] Injecting plugin krakend-server-example` telling us that the plugin is loaded AND injected by the configuration. If you see these lines, you did great! Your plugin is working. To test the plugin, request an endpoint. If you request a path not declared in the configuration like `/__health` the plugin will do nothing about it, and will delegate the request to the router to continue its execution. If you request to `/hijack-me` then the plugin will respond itself with the content `Hello, /` Delegate the request  ``` $curl http://localhost:8080/__health {"agents":{},"now":"2022-06-14 16:53:41.845285857 +0200 CEST m=+459.332136249","status":"ok"}% ``` Hijack the request  ``` $curl http://localhost:8080/hijack-me Hello, "/hijack-me" ``` The plugin is now working. --- # [HTTP Client Plugins for KrakenD API Gateway](https://www.krakend.io/docs/extending/http-client-plugins/) The **HTTP client** plugins execute in the proxy layer when KrakenD tries to reach your backends for content. They allow you to intercept, transform, and manipulate the requests **before they hit your backend services**, and their way back. It is the perfect time to modify the request before it reaches the backend. ![](/images/documentation/http-client-plugin.png "HTTP client plugins") You **cannot chain HTTP client plugins**, limiting them to one plugin per backend connection, and **replace the default KrakenD’s HTTP client**. HTTP client components will stop working An HTTP client is a terminator. It means that it is the last executor in the KrakenD pipe. When you add an HTTP client plugin, **you replace KrakenD’s default client** with your own. It means some built-in functionality in the default HTTP client won’t exist unless you code it. More specifically, if you inject your plugin, you don’t have [Client Credentials](/docs/authorization/client-credentials/), [Backend Cache](/docs/backends/caching/), or [Backend Telemetry](/docs/telemetry/opentelemetry-layers-metrics/#data-exposed-in-the-backend-layer) unless you add this logic to your plugin. ## HTTP client interface Writing plugins Read the introduction to [writing plugins](/docs/extending/writing-plugins/) for compilation and configuration options if you haven’t done it yet. To start with a *hello world* for your first plugin, you have to implement the plugin client interface by copying the example provided in the [Go documentation](https://godoc.org/github.com/luraproject/lura/transport/http/client/plugin) ### Example: Building your first client plugin The easiest way to demonstrate how HTTP client plugins work is with a Hello World plugin. So let’s start by creating a new Go project named `krakend-client-example`: ``` mkdir krakend-client-example cd krakend-client-example go mod init krakend-client-example ``` Now we have to create a file `main.go` with the content below: ```go // SPDX-License-Identifier: Apache-2.0 package main import ( "context" "encoding/json" "errors" "fmt" "html" "io" "net/http" ) // ClientRegisterer is the symbol the plugin loader will try to load. It must implement the RegisterClient interface var ClientRegisterer = registerer("krakend-client-example") type registerer string var logger Logger = nil func (registerer) RegisterLogger(v interface{}) { l, ok := v.(Logger) if !ok { return } logger = l logger.Debug(fmt.Sprintf("[PLUGIN: %s] Logger loaded", ClientRegisterer)) } func (r registerer) RegisterClients(f func( name string, handler func(context.Context, map[string]interface{}) (http.Handler, error), )) { f(string(r), r.registerClients) } func (r registerer) registerClients(_ context.Context, extra map[string]interface{}) (http.Handler, error) { // check the passed configuration and initialize the plugin name, ok := extra["name"].(string) if !ok { return nil, errors.New("wrong config") } if name != string(r) { return nil, fmt.Errorf("unknown register %s", name) } // check the cfg. If the modifier requires some configuration, // it should be under the name of the plugin. E.g.: /* "extra_config":{ "plugin/http-client":{ "name":"krakend-client-example", "krakend-client-example":{ "path": "/some-path" } } } */ // The config variable contains all the keys you have defined in the configuration: config, _ := extra["krakend-client-example"].(map[string]interface{}) // The plugin will look for this path: path, _ := config["path"].(string) logger.Debug(fmt.Sprintf("The plugin is now hijacking the path %s", path)) // return the actual handler wrapping or your custom logic so it can be used as a replacement for the default http handler return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { // The path matches, it has to be hijacked and no call to the backend happens. // The path is the the call to the backend, not the original request by the user. if req.URL.Path == path { w.Header().Add("Content-Type", "application/json") // Return a custom JSON object: res := map[string]string{"message": html.EscapeString(req.URL.Path)} b, _ := json.Marshal(res) w.Write(b) logger.Debug("request:", html.EscapeString(req.URL.Path)) return } // If the requested path is not what we defined, continue. resp, err := http.DefaultClient.Do(req) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Copy headers, status codes, and body from the backend to the response writer for k, hs := range resp.Header { for _, h := range hs { w.Header().Add(k, h) } } w.WriteHeader(resp.StatusCode) if resp.Body == nil { return } io.Copy(w, resp.Body) resp.Body.Close() }), nil } func main() {} type Logger interface { Debug(v ...interface{}) Info(v ...interface{}) Warning(v ...interface{}) Error(v ...interface{}) Critical(v ...interface{}) Fatal(v ...interface{}) } ``` The plugin above aborts the request and replies by printing a `Hello, %q` without passing the request to the backend. It is a simple example, but it shows the necessary structure to start working with plugins. If you look now closely at the plugin code, notice that the loader looks for the symbol `ClientRegisterer` and, if found, the loader checks if the symbol implements the `plugin.Registerer` interface. Once the plugin is validated, the loader registers handlers from the plugin by calling the exposed `RegisterClients` method. With the `main.go` file saved, it’s time to build and test the plugin. If you added more code and external dependencies, you must run a `go mod tidy` before the compilation, which is unnecessary for this example. For compiling Go plugins, the flag `-buildmode=plugin` is required. The command is:   ``` $go build -buildmode=plugin -o krakend-client-example.so . ``` If you are using Docker and want to load your plugin on Docker, compile it in the [Plugin Builder](/docs/extending/writing-plugins/#plugin-builder) for easier integration. Build your plugin  ``` $docker run -it -v "$PWD:/app" -w /app \ krakend/builder:2.13.8 \ go build -buildmode=plugin -o krakend-client-example.so . ``` There is no output for this command. Now you have a file `krakend-client-example.so`, the KrakenD binary has to side load. Remember that you cannot use this binary in a different architecture (e.g., compiling the binary in Mac and loading it in a Docker container). The plugin is ready to use! You can now load your plugin in the configuration. Add the `plugin` and `extra_config` entries in your configuration. Here’s an example of `krakend.json`: ```json { "version": 3, "plugin": { "pattern": ".so", "folder": "./krakend-client-example/" }, "endpoints": [ { "endpoint": "/test/{id}", "backend": [ { "host": [ "http://localhost:8080" ], "url_pattern": "/__debug/{id}", "extra_config": { "plugin/http-client": { "name": "krakend-client-example", "krakend-client-example": { "path": "/__debug/hijack-me" } } } } ] } ] } ``` Start the server with `krakend run -dc krakend.json`. When you run the server, the expected output (with `DEBUG` log level) is: ``` Parsing configuration file: krakend.json yyyy/mm/dd hh:mm:ss KRAKEND ERROR: [SERVICE: Logging] Unable to create the logger: getting the extra config for the krakend-gologging module yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: Plugin Loader] Starting loading process yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [PLUGIN: krakend-client-example] Logger loaded yyyy/mm/dd hh:mm:ss KRAKEND INFO: [SERVICE: Executor Plugin] Total plugins loaded: 1 yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: Handler Plugin] plugin #0 (krakend-client-example/krakend-client-example.so): plugin: symbol HandlerRegisterer not found in plugin krakend-client-example yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: Modifier Plugin] plugin #0 (krakend-client-example/krakend-client-example.so): plugin: symbol ModifierRegisterer not found in plugin krakend-client-example yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [SERVICE: Plugin Loader] Loading process completed yyyy/mm/dd hh:mm:ss KRAKEND INFO: Starting the KrakenD instance yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [ENDPOINT: /test/:id] Building the proxy pipe yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [BACKEND: /__health] Building the backend pipe yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: The plugin is now hijacking the path /hijack-me yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [BACKEND: /__health] Injecting plugin krakend-client-example yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [ENDPOINT: /test/:id] Building the http handler yyyy/mm/dd hh:mm:ss KRAKEND DEBUG: [ENDPOINT: /test/:id][JWTSigner] Signer disabled yyyy/mm/dd hh:mm:ss KRAKEND INFO: [ENDPOINT: /test/:id][JWTValidator] Validator disabled for this endpoint yyyy/mm/dd hh:mm:ss KRAKEND INFO: [SERVICE: Gin] Listening on port: 8080 ... ``` Let’s take a closer look at the log. First, notice that the plugin tried registering itself for each plugin type (`[SERVICE: Executor Plugin]`, `[SERVICE: Handler Plugin]`, and `[SERVICE: Modifier Plugin]`), but we are only building an Executor Plugin in this case. As we are implementing only one of the types, the other two types will fail to load (`symbol not found`). The logline is expected and is not an error but just an informational `DEBUG` message. The essential lines are: - `[PLUGIN: krakend-client-example] Logger loaded` printed by the plugin logger we introduced in our code telling us that the plugin is loaded - The `[SERVICE: Executor Plugin] Total plugins loaded: 1` telling us there is one type of plugin for this type - `[BACKEND: /__health] Injecting plugin krakend-client-example` telling us that the plugin is loaded AND injected by the configuration. If you see these lines, you did great! Your plugin is working. To test the plugin, request the test endpoint `/test/{id}`. If you request a path not declared in the configuration, like `/test/normal,` the plugin will execute the request. If you request to `/test/hijack-me,`, then the plugin will respond with the content `Hello, /hijack-me.` Delegate the request  ``` $curl http://localhost:8080/test/normal {"message":"pong"} ``` Hijack the request  ``` $curl http://localhost:8080/test/hijack-me {"message":"/__debug/hijack-me"} ``` The plugin is now working. --- # [Extending KrakenD with Request and Response Plugin Modifiers](https://www.krakend.io/docs/extending/plugin-modifiers/) The **request and response modifier plugins** are a type of KrakenD customization that allow you to code your own business logic directly on requests and responses in a simple and extensible way. These plugins complement the **handler plugins**, and the **client executor plugins** and avoid their limitations and **extra overload**. The injecting of the modifiers is placed at the beginning of the proxy pipe (just after the router layer) and before the request executor (where the executor plugins are injected). The first one can modify the incoming request and the final response, and it’s configured at the endpoint level; the second one can modify the request and responses concerning the backend that declares it. Since all the modifiers are executed at the proxy pipe, no extra encoding/decoding cycles are required. The **request and response modifier plugins are not middlewares, but modifier functions** that you can call **sequentially from a new middleware**. Request modifiers can only inspect and modify requests (and other cool things) and response modifiers, only responses. KrakenD **executes the request and response modifiers in the order they are declared at the configuration**. See the [example](/docs/extending/plugin-modifiers/#example) below. ![http handler plugin](/images/documentation/request-response-plugin.png) ## Possibilities of req/resp modifiers The following table shows what you can do with modifiers: | Request modifiers | Response modifiers | |----------------------------------------------------|--------------------------------------------------------------------------| | 1\. Request validation and complex checks | 1\. Response validation and complex checks | | 2\. Request manipulation | 2\. Response manipulation | | 3\. Request filtering | 3\. Response filtering | | 4\. Request debugging | 4\. Response debugging | | 5\. Complex workflows | 5\. Complex workflows | | 6\. Coordinated rate-limiting and quota management | 6\. Coordinated quota control (response size, service consumption, etc.) | The encoding marks what you can manipulate in responses If your endpoint uses an `output_encoding` other than `no-op` you can work with `ResponseWrapper.Data()` and `ResponseWrapper.IsComplete()`. If you use `no-op` you can work with `ResponseWrapper.Io()`, `ResponseWrapper.StatusCode()`, and `ResponseWrapper.Headers()`. If you set data in a different place than specified above, **it will be ignored**. ## Example Compatibility note Go plugins are supported on Linux, FreeBSD, and macOS The easiest way to demonstrate how the modifier plugins work is with the debugger plugin, so let’s start with a new Go project by creating a new module with `go mod init your_package_name`, and adding a single `main.go` file with the minimal boilerplate. ```go package main import ( "errors" "fmt" "io" "net/url" "github.com/luraproject/lura/v2/proxy" ) func main() {} func init() { fmt.Println(string(ModifierRegisterer), "loaded!!!") } // ModifierRegisterer is the symbol the plugin loader will be looking for. It must // implement the plugin.Registerer interface // https://github.com/luraproject/lura/blob/master/proxy/plugin/modifier.go#L71 var ModifierRegisterer = registerer("krakend-debugger") type registerer string // RegisterModifiers is the function the plugin loader will call to register the // modifier(s) contained in the plugin using the function passed as argument. // f will register the factoryFunc under the name and mark it as a request // and/or response modifier. func (r registerer) RegisterModifiers(f func( name string, factoryFunc func(map[string]interface{}) func(interface{}) (interface{}, error), appliesToRequest bool, appliesToResponse bool, )) { f(string(r)+"-request", r.requestDump, true, false) f(string(r)+"-response", r.responseDump, false, true) fmt.Println(string(r), "registered!!!") } ``` As with other KrakenD plugins, the loader looks for a given symbol (in this case, “ModifierRegisterer”) and, if found, the loader checks if the symbol implements the `plugin.Registerer` interface. Once the plugin is validated, the loader registers the modifiers from the plugin by calling the exposed `RegisterModifiers` method. For the debugger plugin we’ll register two different modifiers: `requestDump` and `responseDump`. The modifiers are registered under the same namespace so both will be injected with a single config line. In order to avoid weird dependency collisions, the `modifierFactory` signature uses basic types and `interface{}`, so some type assertion against the interfaces declared at the [`proxy` package](https://github.com/luraproject/lura/blob/master/proxy/plugin.go#L187-L209) are required. Include the interfaces into your plugin by adding the following lines: ```go // RequestWrapper is an interface for passing proxy request between the krakend pipe // and the loaded plugins type RequestWrapper interface { Params() map[string]string Headers() map[string][]string Body() io.ReadCloser Method() string URL() *url.URL Query() url.Values Path() string } // ResponseWrapper is an interface for passing proxy response between the krakend pipe // and the loaded plugins type ResponseWrapper interface { Data() map[string]interface{} Io() io.Reader IsComplete() bool StatusCode() int Headers() map[string][]string } ``` After this minimal code repetition, implementing the modifier factories is an easy task. The factory must accept a configuration as a map and return the final modifier once it’s ready. Depending on your requirements, the factory could access its dedicated configuration and do whatever logic is required by your scenario. This configuration section should use the plugin name as namespace (check the comments below). ```go var unknownTypeErr = errors.New("unknown request type") func (r registerer) requestDump( cfg map[string]interface{}, ) func(interface{}) (interface{}, error) { // check the cfg. If the modifier requires some configuration, // it should be under the name of the plugin. // ex: if this modifier required some A and B config params /* "extra_config":{ "plugin/req-resp-modifier":{ "name":["krakend-debugger"], "krakend-debugger":{ "A":"foo", "B":42 } } } */ // return the modifier fmt.Println("request dumper injected!!!") return func(input interface{}) (interface{}, error) { req, ok := input.(RequestWrapper) if !ok { return nil, unknownTypeErr } fmt.Println("params:", req.Params()) fmt.Println("headers:", req.Headers()) fmt.Println("method:", req.Method()) fmt.Println("url:", req.URL()) fmt.Println("query:", req.Query()) fmt.Println("path:", req.Path()) return input, nil } } func (r registerer) responseDump( cfg map[string]interface{}, ) func(interface{}) (interface{}, error) { // check the cfg. If the modifier requires some configuration, // it should be under the name of the plugin. // ex: if this modifier required some A and B config params /* "extra_config":{ "plugin/req-resp-modifier":{ "name":["krakend-debugger"], "krakend-debugger":{ "A":"foo", "B":42 } } } */ // return the modifier fmt.Println("response dumper injected!!!") return func(input interface{}) (interface{}, error) { resp, ok := input.(ResponseWrapper) if !ok { return nil, unknownTypeErr } fmt.Println("data:", resp.Data()) fmt.Println("is complete:", resp.IsComplete()) fmt.Println("headers:", resp.Headers()) fmt.Println("status code:", resp.StatusCode()) return input, nil } } ``` You can also refer [this example](https://github.com/luraproject/lura/blob/v2.0.1/proxy/plugin/tests/main.go) on how to update the request and [this example](https://github.com/luraproject/lura/blob/master/proxy/plugin.go#L130) on how to update the response. ### Building the plugin With the `main.go` file complete, it’s time to build and test the plugin. For compiling Go plugins, the flag `-buildmode=plugin` is required:   ``` $go build -buildmode=plugin -o krakend-debugger.so . ``` If you are using Docker and wanting to load your plugin on Docker, compile it in the [Plugin Builder](/docs/extending/writing-plugins/#plugin-builder) for an easier integration. Build your plugin  ``` $docker run -it -v "$PWD:/app" -w /app \ krakend/builder:2.13.8 \ go build -buildmode=plugin -o krakend-debugger.so . ``` For the test, we’ll build a small gateway with a single endpoint merging the responses from two different backends. ```json { "version": 3, "port": 8080, "name": "KrakenD request and response modifier demo", "host": ["https://api.github.com"], "plugin": { "pattern":".so", "folder": "/path/to/your/plugin/folder/" }, "endpoints": [ { "endpoint": "/github/orgs/{org}", "backend":[ { "url_pattern": "/orgs/{org}", "allow": [ "avatar_url", "blog", "followers" ], "mapping": { "blog": "website" }, "group": "org", "extra_config":{ "plugin/req-resp-modifier":{ "name":["krakend-debugger-request"] } } }, { "url_pattern": "/orgs/{org}/repos", "mapping": { "collection": "repos" }, "is_collection": true, "extra_config":{ "plugin/req-resp-modifier":{ "name":["krakend-debugger-response"] } } } ], "extra_config":{ "plugin/req-resp-modifier":{ "name": [ "krakend-debugger-request", "krakend-debugger-response" ] } } } ] } ``` Notice the modifier names which needs to be combination of the modifier name and the string used while registering it in `RegisterModifiers`. These must be unique for the request and response modifier. If we send a request to the generated endpoint, we’ll see the dumps for the three pairs of requests and responses at the console: Test the code  ``` $curl -i http://localhost:8080/github/orgs/krakend ``` ## Returning errors Your custom plugin can set errors using two different approaches: - Return an error message only - Return an error message and a status code - Return an error message, status code, and `Content-Type`. Client should see the error? As gateway errors are not propagated to the end-user, if you want them to see the defined error you must enable [`return_error_message`](/docs/service-settings/router-options/#returning-the-gateway-error-message) as [router options](/docs/service-settings/router-options/). ### Plugin returning an error message only The simple example could be something like (as seen in the code above): ```go func(input interface{}) (interface{}, error) { // your plugin code return nil, errors.New("Something went really wrong") } ``` ### Plugin returning an error message and a status code You should implement in this case the HTTP error interface below, which provides more control: ```go type responseError interface { error StatusCode() int } ``` This option allows you to set the status code that you want to return to the client, along with the error message. An example of the code your plugin will need: ```go // HTTPResponseError is the error to be returned by the ErrorHTTPStatusHandler type HTTPResponseError struct { Code int `json:"http_status_code"` Msg string `json:"http_body,omitempty"` } // Error returns the error message func (r HTTPResponseError) Error() string { return r.Msg } // StatusCode returns the status code returned by the backend func (r HTTPResponseError) StatusCode() int { return r.Code } func(input interface{}) (interface{}, error) { // ... return nil, HTTPResponseError{Code:429,Msg:"Something went really wrong"} } ``` ### Return an error message, status code, and `Content-Type` To add the `Content-Type` header in the response, use the following interface: ```go type encodedResponseError interface { responseError Encoding() string } ``` An example of the code your plugin will need: ```go type HTTPResponseError struct { Code int `json:"http_status_code"` Msg string `json:"http_body,omitempty"` HTTPEncoding string `json:"http_encoding"` } // Error returns the error message func (r HTTPResponseError) Error() string { return r.Msg } // StatusCode returns the status code returned by the backend func (r HTTPResponseError) StatusCode() int { return r.Code } // Encoding returns the HTTP output encoding func (r HTTPResponseError) Encoding() string { return r.HTTPEncoding } func(input interface{}) (interface{}, error) { // your plugin code return nil, HTTPResponseError{ Code:429, Msg:"{\"status\": \"ko\"}", HTTPEncoding:"application/json", } } ``` --- # [Middleware Plugins for KrakenD API Gateway (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/extending/middleware-plugins/) Middleware plugins are designed to provide the maximum level of customization. While an open-source user could fork the source code of KrakenD and implement their own middleware, an enterprise user can use this plugin to change the internals of KrakenD. Middleware plugins allow you to add functionality to the remaining parts, whereas the other plugins do not allow you to do so. The primary purpose of Middleware plugins is: - Intercept the request and response - Delegate the execution to custom code - Abort the execution (e.g., you implemented a custom usage policy) - Transform data at your will ## Proxy interface When you add a Middelware plugin, it receives a context and a request, works with it, and passes the execution to the `next` proxy component. It can live both in the `endpoint` or the `backend`. The Middleware executes before the request and response modifier plugins (if any), before instrumentation, and after the rest of enterprise and FOSS functionality in that pipe zone. When placed in a `backend`, it executes between the rate limit and JMESpath executions, and when placed at the `endpoint` level, it executes between the Workflows and the Response Json Schema ### Example: Building your first middleware plugin To build a middleware plugin execute the following command: Generate a middleware plugin  ``` $docker run --rm -it \ -w /app \ -e "KRAKEND_LICENSE_PATH=/LICENSE"\ -v "$PWD/LICENSE:/LICENSE:ro" -v "$PWD/plugins:/app" \ krakend/krakend-ee:2.13.6 plugin init --name my-mw --type middleware ``` See the following `main.go` that modifies the data adding an `acme` field and modifying the path. ```go package main import ( "context" "encoding/json" "errors" "fmt" "io" "net/url" ) // MiddlewareRegisterer is the symbol the plugin loader will be looking for. It must // implement the MiddlewareRegisterer interface // type MiddlewareRegisterer interface { // RegisterMiddlewares(func( // string, // func(map[string]interface{}, func(context.Context, interface{}) (interface{}, error)) func(context.Context, interface{}) (interface{}, error), // )) // } var MiddlewareRegisterer = registerer("mw-example") var ( logger Logger = nil ctx context.Context = context.Background() errRegistererNotFound = fmt.Errorf("%s plugin disabled: config not found", MiddlewareRegisterer) errUnkownRequestType = errors.New("unknown request type") errUnkownResponseType = errors.New("unknown response type") ) type registerer string // RegisterMiddlewares is the function the plugin loader will call to register the // middleware(s) contained in the plugin using the function passed as argument. // f will register the factoryFunc under the name. func (r registerer) RegisterMiddlewares(f func( name string, middlewareFactory func(map[string]interface{}, func(context.Context, interface{}) (interface{}, error)) func(context.Context, interface{}) (interface{}, error), ), ) { f(string(r), r.middlewareFactory) } func (r registerer) middlewareFactory(cfg map[string]interface{}, next func(context.Context, interface{}) (interface{}, error)) func(context.Context, interface{}) (interface{}, error) { // If the middleware requires configuration add it under the extra_config, // it should be under the name of the plugin. // ex: the following example adds a paramater "option" that the plugin can use. /* "extra_config":{ "plugin/middleware":{ "name":["mw-example"], "mw-example":{ "option": "value" } } } // Uncomment if you have configuration: config, err := r.parseConfig(cfg) if err != nil { logger.Debug(fmt.Sprintf("[PLUGIN: %s] Cannot initialize configuration: %s", r, err)) return nil } */ // This is an example of registered ctx usage, it can be used to, for example, connect to external services. select { case <-ctx.Done(): logger.Debug(fmt.Sprintf("[PLUGIN: %s] Context is done, skipping middleware injection", r)) return nil default: } logger.Debug(fmt.Sprintf("[PLUGIN: %s] Middleware injected", r)) return func(ctx context.Context, req interface{}) (interface{}, error) { reqw, ok := req.(RequestWrapper) if !ok { return nil, errUnkownRequestType } resp, err := next(ctx, requestWrapper{ params: reqw.Params(), headers: reqw.Headers(), body: reqw.Body(), method: reqw.Method(), url: reqw.URL(), query: reqw.Query(), // Modifies the path: path: reqw.Path() + "/fooo", }) respw, ok := resp.(ResponseWrapper) if !ok { return nil, errUnkownResponseType } // Add an "acme" field in the response. data := respw.Data() data["acme"] = true return responseWrapper{ ctx: respw.Context(), request: respw.Request(), data: data, isComplete: respw.IsComplete(), metadata: metadataWrapper{ headers: respw.Headers(), statusCode: respw.StatusCode(), }, io: respw.Io(), }, err } } type config struct { Opt string `json:"option"` } // parseConfig parses the configuration marshaling and unmarshaling into a struct. // you can also manually check for fields in the extra config map func (r registerer) parseConfig(extra map[string]interface{}) (*config, error) { cfgRaw, ok := extra[string(r)].(map[string]interface{}) if !ok { return nil, errRegistererNotFound } cfg := config{} b, err := json.Marshal(cfgRaw) if err != nil { return nil, err } if err = json.Unmarshal(b, &cfg); err != nil { return nil, err } return &cfg, nil } // This logger is replaced by the RegisterLogger method to load the one from KrakenD func (r registerer) RegisterLogger(in interface{}) { l, ok := in.(Logger) if !ok { return } logger = l logger.Debug(fmt.Sprintf("[PLUGIN: %s] Logger loaded", r)) } type Logger interface { Debug(v ...interface{}) Info(v ...interface{}) Warning(v ...interface{}) Error(v ...interface{}) Critical(v ...interface{}) Fatal(v ...interface{}) } // Empty logger implementation type noopLogger struct{} func (n noopLogger) Debug(_ ...interface{}) {} func (n noopLogger) Info(_ ...interface{}) {} func (n noopLogger) Warning(_ ...interface{}) {} func (n noopLogger) Error(_ ...interface{}) {} func (n noopLogger) Critical(_ ...interface{}) {} func (n noopLogger) Fatal(_ ...interface{}) {} // RegisterContext saves the KrakenD application context so KrakenD can inject // the context like we do with the logger func (r registerer) RegisterContext(c context.Context) { ctx = c logger.Debug(fmt.Sprintf("[PLUGIN: %s] Context loaded", r)) } func main() {} // RequestWrapper is an interface for passing proxy request between the krakend pipe // and the loaded plugins type RequestWrapper interface { Context() context.Context Params() map[string]string Headers() map[string][]string Body() io.ReadCloser Method() string URL() *url.URL Query() url.Values Path() string } type requestWrapper struct { ctx context.Context method string url *url.URL query url.Values path string body io.ReadCloser params map[string]string headers map[string][]string } func (r requestWrapper) Context() context.Context { return r.ctx } func (r requestWrapper) Method() string { return r.method } func (r requestWrapper) URL() *url.URL { return r.url } func (r requestWrapper) Query() url.Values { return r.query } func (r requestWrapper) Path() string { return r.path } func (r requestWrapper) Body() io.ReadCloser { return r.body } func (r requestWrapper) Params() map[string]string { return r.params } func (r requestWrapper) Headers() map[string][]string { return r.headers } type metadataWrapper struct { headers map[string][]string statusCode int } func (m metadataWrapper) Headers() map[string][]string { return m.headers } func (m metadataWrapper) StatusCode() int { return m.statusCode } // ResponseWrapper is an interface for passing proxy response between the krakend pipe // and the loaded plugins type ResponseWrapper interface { Context() context.Context Request() interface{} Data() map[string]interface{} IsComplete() bool Headers() map[string][]string StatusCode() int Io() io.Reader } type responseWrapper struct { ctx context.Context request interface{} data map[string]interface{} isComplete bool metadata metadataWrapper io io.Reader } func (r responseWrapper) Context() context.Context { return r.ctx } func (r responseWrapper) Request() interface{} { return r.request } func (r responseWrapper) Data() map[string]interface{} { return r.data } func (r responseWrapper) IsComplete() bool { return r.isComplete } func (r responseWrapper) Io() io.Reader { return r.io } func (r responseWrapper) Headers() map[string][]string { return r.metadata.headers } func (r responseWrapper) StatusCode() int { return r.metadata.statusCode } ``` Do not forget to [inject the middleware](/docs/enterprise/extending/injecting-plugins/) plugin into your configuration. --- # [Injecting Redis into KrakenD Plugins (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/extending/injecting-redis-in-plugins/) **Redis injection** lets plugins access KrakenD’s managed Redis connections directly, without writing any connection management code. When your plugin’s custom business logic requires a shared persistence layer, such as counters, caching, or distributed state, you can use the connections KrakenD already manages instead of creating your own. KrakenD opens and closes connections as needed and exposes them to your plugin through selector functions at startup. The only requirement is that your plugin imports the same `github.com/redis/go-redis/v9` version that KrakenD uses internally. ## Prerequisites The interaction with a Redis pool or cluster happens through the `github.com/redis/go-redis/v9` library. Add it as a dependency in your plugin: Add the go-redis dependency to your plugin  ``` $cd plugin-folder go get github.com/redis/go-redis/v9 ```   Dependency version must match KrakenD’s The `github.com/redis/go-redis/v9` version must match the version KrakenD uses. Run a [plugin dependency check](/docs/enterprise/extending/check-plugin/) to detect compatibility issues before compiling. ## Implementing the RedisRegisterer Interface During startup, KrakenD injects Redis client selectors into any plugin that implements the `RedisRegisterer` interface: ```go type RedisRegisterer interface { RegisterRedisSelectors(func(string) *redis.Client, func(context.Context, string, string) *redis.Client) } ``` The first argument is the pool selector and the second is the cluster selector. The simplest implementation saves both into plugin-level variables for use at request time: ```go var ( redisClientSelector func(string) *redis.Client redisClusterClientSelector func(context.Context, string, string) *redis.Client ) func (r registerer) RegisterRedisSelectors(cs func(string) *redis.Client, ccs func(context.Context, string, string) *redis.Client) { redisClientSelector = cs redisClusterClientSelector = ccs logger.Debug(fmt.Sprintf("[PLUGIN: %s] Redis selectors loaded", r)) } ``` ## Redis Selectors A selector is a helper function that looks up a registered Redis pool or cluster by name and returns it as a `*redis.Client`. Once `RegisterRedisSelectors` runs at startup, the selectors are available throughout your plugin. For instance, given this [Redis connection pool configuration at the service level](/docs/enterprise/service-settings/redis-connection-pools/): ```json { "extra_config": { "redis": { "connection_pools": [ { "name": "redis_at_eight", "address": "localhost:6379", "db": 8 }, { "name": "redis_at_seven", "address": "localhost:6379", "db": 7 } ] } } } ``` Retrieve a specific pool by passing its name to the selector: ```go redisClient := redisClientSelector("redis_at_seven") ``` The same pattern applies to [Redis cluster configuration](/docs/enterprise/service-settings/redis-connection-pools/#configuration-of-redis-clusters), using `redisClusterClientSelector` instead. ## Example: Using Redis in a Modifier Plugin The following example shows a modifier plugin that increments a Redis counter on each response, keyed by the value of the `X-Id` response header: ```go func (r registerer) response( extra map[string]interface{}, ) func(interface{}) (interface{}, error) { // retrieve the client once, outside the request handler, to avoid per-request overhead. redisClient := redisClientSelector("redis_at_eight") if redisClient == nil { // handle the missing pool according to your business logic: // return an error to abort initialization, or log a warning and continue. } return func(input interface{}) (interface{}, error) { resp, ok := input.(ResponseWrapper) if !ok { return nil, unknownTypeErr } // increment a counter keyed by the X-Id response header value. redisClient.Incr(resp.Context(), "test:from-resp-modifier:"+resp.Headers()["X-Id"][0]) return input, nil } } var ( redisClientSelector func(string) *redis.Client redisClusterClientSelector func(context.Context, string, string) *redis.Client ) func (r registerer) RegisterRedisSelectors(cs func(string) *redis.Client, ccs func(context.Context, string, string) *redis.Client) { redisClientSelector = cs redisClusterClientSelector = ccs logger.Debug(fmt.Sprintf("[PLUGIN: %s] Redis selectors loaded", r)) } ``` The critical detail is retrieving the Redis client outside the inner handler function. Calling the selector inside the handler adds lookup overhead to every request. ## Example: Using Redis in a Middleware Plugin The `examples` repository contains a full working stack that demonstrates Redis injection in a middleware plugin: [View the middleware plugin example](https://github.com/krakend/examples/tree/main/11.plugins-and-redis "View the middleware plugin example") --- # [Injecting Quota Processors in Plugins (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/extending/injecting-quota-processors-in-plugins/) **Quota Processor injection** lets you use KrakenD’s built-in quota enforcement system inside any custom plugin. Instead of building your own rate-limiting logic, your plugin delegates quota state, time windows, and storage management to KrakenD and focuses on business logic. Use this when your plugin needs to enforce usage limits that align with or share counters from the service-level quota configuration. KrakenD injects a Quota Processor selector into any plugin that implements the `QuotaProcessorRegisterer` interface. Once registered, the selector gives you access to any named quota processor declared in your configuration. The main advantages of this approach are: - No need to manage quota state, time windows, or storage, KrakenD handles the processor lifecycle - A performant, production-ready solution - A simple interface to check and consume quotas - Plug-and-play: select the processor by name and start enforcing limits ## Registering the Selector During startup, KrakenD injects the Quota Processor selector into plugins that implement the `QuotaProcessorRegisterer` interface: ```go type QuotaProcessorRegisterer interface { RegisterQuotaProcessorSelector(func(string) (interface{}, error)) } ``` The simplest implementation saves the incoming selector into a plugin-level variable: ```go var quotaSelector func(string) (interface{}, error) func (r registerer) RegisterQuotaProcessorSelector(s func(string) (interface{}, error)) { quotaSelector = s logger.Debug(fmt.Sprintf("[PLUGIN: %s] Quota Processor selector loaded", r)) } ``` ## Quota Selector After startup, the selector is ready to use. A selector is a helper function that retrieves a registered Quota Processor by name and returns it as an `interface{}`. Retrieve a quota processor by name as follows: ```go p, err := quotaSelector("my-quota-processor") if err != nil { // handle error: the processor was not found or could not be loaded } ``` Cast the returned `interface{}` to the `QuotaProcessor` interface before use: ```go type QuotaProcessor interface { Close() WithRules(rules ...[2]string) error Process(tierValue string, key string, when time.Time, weight int64) (interface{}, error) } ``` For example: ```go quotaProcessor, ok := p.(QuotaProcessor) if !ok { // handle error: the processor could not be properly loaded } ``` ## Interface Breakdown The `QuotaProcessor` interface exposes three methods: `WithRules`, `Close`, and `Process`. Call `WithRules` during plugin registration and `Process` on every request. ### `WithRules` Call `WithRules` during plugin registration to attach existing rulesets, defined in the configuration at `governance/processors.quotas`, to a tier value extracted from each request. This association determines which limit applies. ```go errs := quotaProcessor.WithRules( [2]string{"gold", "admin"}, [2]string{"gold", "developer"}, ) if errs != nil { quotaProcessor.Close() return nil, errs } ``` ### `Close` Call `Close()` if registration fails after a successful quota selection. The call shuts down the processor only when no other component is using it. ### `Process` Call `Process` inside your plugin’s handler or proxy on every request. The parameters are: - **`tierValue`** : The tier value to match against the rules registered with `WithRules` - **`key`** : A unique key identifying the consumer (e.g., user ID, API key, JWT sub claim) - **`when`** : The time of the request (`time.Now()`) - **`weight`** : The cost of the request. Pass `0` to check the quota without consuming it (pre-check), or a positive value to consume quota. `Process` returns an `interface{}` with the quota status for the current hit. Cast it to `processResponseWrapper` to read the result: ```go type processResponseWrapper interface { Allowed() bool BlockedFor() time.Duration Details() []interface{} } ``` - **`Allowed()`** : Whether the request is within the quota limits - **`BlockedFor()`** : How long until the quota resets if the request is blocked - **`Details()`** : The status of each time window in the ruleset Each element of the `Details()` slice is castable to: ```go type windowStatusWrapper interface { Remaining() int64 Consumed() int64 NextRefill() time.Duration LevelValue() string WindowName() string Allowed() bool } ``` - **`LevelValue()`** : The current rule name, e.g., `gold` - **`WindowName()`** : A string representing the rule window, e.g., `hour` or `day` - **`Allowed()`** : Whether the request is within the rule limits - **`Consumed()`** : How much quota has been consumed for the current rule window - **`Remaining()`** : How much quota remains for the current rule window - **`NextRefill()`** : If the quota is exhausted, a `time.Duration` representing how long until the next reset ## Sharing Counters with Existing Quota Configurations Quota Processor injection is compatible with existing service, endpoint, and backend quota configurations, so you can share quota counters and thresholds between plugin logic and built-in quota enforcement. Consider the following quota configuration: ```json { "extra_config": { "redis": { "connection_pools": [ { "name": "shared_redis_pool", "address": "192.168.1.45:6379" } ] }, "governance/processors": { "quotas": [ { "name": "public_plans", "connection_name": "shared_redis_pool", "hash_keys": true, "on_failure_allow": false, "rejecter_cache": { "N": 10000000, "P": 1e-8, "hash_name": "optimal" }, "rules": [ { "name": "rule_gold", "limits": [ { "amount": 10, "unit": "hour" }, { "amount": 200, "unit": "day" } ] }, { "name": "rule_bronze", "limits": [ { "amount": 5, "unit": "hour" }, { "amount": 100, "unit": "day" } ] } ] } ] }, "governance/quota": { "quota_name": "public_plans", "on_unmatched_tier_allow": false, "weight_key": "credits_consumed", "weight_strategy": "body", "tier_key": "X-Level", "disable_quota_headers": false, "tiers": [ { "rule_name": "rule_gold", "tier_value": "gold", "tier_value_as": "literal", "strategy": "header", "key": "X-User-Id" }, { "@comment": "Wildcard tier that catches all requests not matched by tiers above", "rule_name": "rule_bronze", "tier_value_as": "*", "strategy": "ip" } ] } } } ``` This configuration enables quota management for all endpoints using different thresholds and rules based on the `X-Level` header. From a plugin, you are not restricted to header values as selectors, you can apply any business logic as long as you enable the correct rules and pass the matching `tier_value` to `quota.Process()`. Counter sharing limitation As of v2.13, counter sharing only works when `tier_value_as` is set to `literal`. The wildcard matcher `*` is not currently supported. The following handler plugin reuses the configured quota processor and rules: ```go func (r registerer) registerHandlers(_ context.Context, extra map[string]interface{}, h http.Handler) (http.Handler, error) { p, err := quotaSelector("my-quota-processor") if err != nil { return nil, err } quotaProcessor, ok := p.(QuotaProcessor) if !ok { return nil, fmt.Errorf("invalid quota processor type") } quotaProcessor.WithRules( [2]string{"rule_gold", "gold"}, // matches "rule_name" and "tier_value" of the first tier in "governance/quota", shares counters [2]string{"rule_gold", "admin"}, [2]string{"rule_bronze", "guest"}, ) return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { if req.URL.Path != "/" { h.ServeHTTP(w, req) return } n := time.Now() // extract these values from the current request tierValue := "gold" userId := "user-id" preCheck, _ := quotaProcessor.Process(tierValue, userId, n, 0) s, ok := preCheck.(processResponseWrapper) if !ok { w.WriteHeader(http.StatusInternalServerError) return } if !s.Allowed() { w.WriteHeader(http.StatusTooManyRequests) w.Write([]byte("Quota exceeded\n")) return } h.ServeHTTP(w, req) quotaProcessor.Process(tierValue, userId, n, 1) }), nil } ``` ## Example: Using Quota Processors in a Handler Plugin The following complete handler plugin enforces quotas using a pre-check (with `weight` set to `0`) before serving the request, then consumes quota (with `weight` set to `2`) after serving: ```go func (r registerer) registerHandlers(_ context.Context, extra map[string]interface{}, h http.Handler) (http.Handler, error) { p, err := quotaSelector("my-quota-processor") if err != nil { return nil, err } quotaProcessor, ok := p.(QuotaProcessor) if !ok { return nil, fmt.Errorf("invalid quota processor type") } errs := quotaProcessor.WithRules( [2]string{"gold", "admin"}, [2]string{"gold", "developer"}, ) if errs != nil { quotaProcessor.Close() return nil, errs } return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { if req.URL.Path != "/" { h.ServeHTTP(w, req) return } n := time.Now() // extract these values from the current request tierValue := "admin" userId := "user-id" preCheck, _ := quotaProcessor.Process(tierValue, userId, n, 0) s, ok := preCheck.(processResponseWrapper) if !ok { w.WriteHeader(http.StatusInternalServerError) return } if !s.Allowed() { w.WriteHeader(http.StatusTooManyRequests) w.Write([]byte("Quota exceeded\n")) return } h.ServeHTTP(w, req) quotaProcessor.Process(tierValue, userId, n, 2) }), nil } var quotaSelector func(string) (interface{}, error) type QuotaProcessor interface { Close() WithRules(rules ...[2]string) error Process(tierValue string, key string, when time.Time, weight int64) (interface{}, error) } func (r registerer) RegisterQuotaProcessorSelector(s func(string) (interface{}, error)) { quotaSelector = s } type processResponseWrapper interface { Allowed() bool BlockedFor() time.Duration Details() []interface{} } type windowStatusWrapper interface { Remaining() int64 Consumed() int64 NextRefill() time.Duration LevelValue() string WindowName() string Allowed() bool } ``` ## Example: Using Quota Processors in a Real Use Case The `examples` repository contains a full stack showcasing quota processor injection at work. It focuses on applying quotas to `no-op` streaming responses. [Check example](https://github.com/krakend/examples/tree/main/12.plugins-and-quotas "Check example") --- # [Lua Scripting](https://www.krakend.io/docs/endpoints/lua/) Scripting with Lua allows you to extend your business logic and make **transformations on requests and responses**. The Lua module is compatible with the rest of components such as [CEL](/docs/endpoints/common-expression-language-cel/), [Martian](/docs/backends/martian/), or other [Go plugins](/docs/extending/) and middlewares. The introduction of Lua scripts in your Gateway does not require recompiling KrakenD, but unlike Go, Lua scripts are interpreted in real-time. If you are new to Lua, see [Lua Documentation](https://www.lua.org/). Lua vs Go Plugins A [Go plugin](/docs/extending/) delivers much more speed and power than a Lua script for performance-first seeking users, but requires a little bit more work as you need to compile your plugins and side-load them on KrakenD. ## Configuration You can add your Lua scripts under the `extra_config` at the service level, the `endpoint` level or the `backend` level. You can choose **three different namespaces** (explained below): - `"modifier/lua-endpoint"` (endpoint and service level) - `"modifier/lua-proxy"` (endpoint level) - `"modifier/lua-backend"` (backend level) The configuration options are: ```json { "extra_config": { "modifier/lua-proxy": { "sources": [ "file1.lua", "./relative/path/file2.lua", "/etc/krakend/absolute/path.lua" ], "md5": { "file1.lua": "49ae50f58e35f4821ad4550e1a4d1de0" }, "pre": "print('Hi from pre!'); my_file1_function()", "post": "print('Hi from post!'); my_file1_function()", "live": false, "allow_open_libs": false, "skip_next": false } } } ``` ##### Fields of Lua modifier \* required fields ##### [`allow_open_libs`](https://www.krakend.io/docs/endpoints/lua/#allow_open_libs "Click to copy link to allow_open_libs") boolean As an efficiency point the Lua component does not load the standard libraries by default. If you need to import Lua libraries (e.g, the I/O, String, etc.), then you must set this flag to true. Defaults to `false` ##### [`live`](https://www.krakend.io/docs/endpoints/lua/#live "Click to copy link to live") boolean For security and efficiency, the Lua script is loaded once into memory and not reloaded even if the file contents change. Set this flag to `true` if you want to modify the Lua script while KrakenD is running and apply the changes live (mostly during development to avoid the snippet being cached). Defaults to `false` ##### [`md5`](https://www.krakend.io/docs/endpoints/lua/#md5 "Click to copy link to md5") object The md5sum is an extra security feature to make sure that once you have coded the Lua script, the MD5 of what is loaded into memory matches what you expect and has not been tampered by a malicious 3rd party. The key of the object must match **exactly** the filename under sources, **including all the path**. Example: `{"./path/to/file1.lua":"49ae50f58e35f4821ad4550e1a4d1de0"}` ##### [`post`](https://www.krakend.io/docs/endpoints/lua/#post "Click to copy link to post") string The Lua code that is executed **after** performing the request. Available when used in the `backend` section. You can write all the Lua code inline (e.g., `print('Hi'); print('there!')` but you can also call functions that live inside one of the files under `sources` (e.g., `my_function()`). Example: `"local r = response.load(); r:headers('Set-Cookie', 'key1='.. r:data('response'));"` ##### [`pre`](https://www.krakend.io/docs/endpoints/lua/#pre "Click to copy link to pre") string The Lua code that is executed **before** performing the request. Unlike `post`, it’s available in all sections. You can write all the Lua code inline (e.g., `print('Hi'); print('there!')` but you can also call functions that live inside one of the files under `sources` (e.g., `my_function()`). Example: `"print('Backend response, pre-logic:'); local r = request.load(); print(r:body());"` ##### [`skip_next`](https://www.krakend.io/docs/endpoints/lua/#skip_next "Click to copy link to skip_next") boolean Available on the `backend` section only. Instead of connecting to next backend in the pipe, returns an empty response and executes the `post` lua function. Defaults to `false` ##### [`sources`](https://www.krakend.io/docs/endpoints/lua/#sources "Click to copy link to sources") array of strings An array with all the Lua files that will be processed. If no path is provided (e.g., `myfile.lua`) the file loads from the [working directory](https://www.krakend.io/docs/configuration/working-directory/). Schema: [https://www.krakend.io/schema/v2.13/modifier/lua.json](https://www.krakend.io/schema/v2.13/modifier/lua.json "JSON schema definition") ## Configuration placement and sequence of execution When running Lua scripts, you can place them at the `proxy` level, or the `router` level: ![lua namespaces](/images/documentation/diagrams/lua.mmd.svg) These two places have the following considerations: - **Router** (at `endpoint`’s `extra_config` or service level): Communication between the end-user and KrakenD. You can inspect and modify the **request** of the user. - With `"modifier/lua-endpoint"`you can modify the **HTTP request context** early in the transport layer. However, KrakenD has not converted the request into an internal request just yet. - With `"modifier/lua-proxy"`you can modify the internal KrakenD request before reaching all backends in the endpoint and modify the response **AFTER the merge** of all backends. - **Proxy** (at `backend`’s `extra_config`): Communication between KrakenD and your services. For both the **request** and the **response**. - With `"modifier/lua-backend"`you can modify the internal KrakenD request before reaching a particular backend and change its response **BEFORE is passed for the merge** of backends at the endpoint level. In a request/response execution, this is how the different namespaces for Lua placement work: ![Lua - Sequence of execution](/images/documentation/diagrams/lua-2.seq.mmd.svg) ## Functions for Proxy You can use the following Lua functions to access and manipulate requests and responses in `"modifier/lua-proxy"` and `"modifier/lua-backend"` namespaces. ### Request functions (`request`) If you have a script that needs access to the request, use the `request` object in Lua. The request is set when KrakenD is about to do a call to the backend services. Using client headers and querystrings When **client headers** or **query strings** are needed in a script, remember to add them under [`input_headers`](/docs/endpoints/parameter-forwarding/#headers-forwarding) or [`input_query_strings`](/docs/endpoints/parameter-forwarding/#query-string-forwarding) accordingly. The `request` functions are: - `load()` (*Static*): The constructor to view and manipulate requests. E.g.: `local r = request.load()`. **Notice that the rest of the functions rely on this one**. - `method()` (*Dynamic*): Getter that retrieves the method of the request. E.g.: `r:method()` could return a string `GET`. - `method(value)` (*Dynamic*): Setter that changes the method of the request. E.g.: `r:method('POST')`. - `path()` (*Dynamic*): Getter that retrieves the path of the request. E.g.: `r:path()` could return a string `/foo/var`. - `path(value)` (*Dynamic*): Setter that changes the path of the request. E.g.: `r:path('/foo/var')`. It does not have any effect when you use `modifier/lua-backend`. - `query()` (*Dynamic*): Getter that retrieves the query string of the request, URL encoded. E.g.: `r:query()` could return a string `?foo=var&vaz=42`. - `query(value)` (*Dynamic*): Setter that changes the query of the request. E.g.: `r:query('?foo=var&vaz=42')`. It does not have any effect when you use `modifier/lua-backend`, but you can still set the `url()` without query strings. - `url()` (*Dynamic*): Getter that retrieves the full URL string of the request, including the host and path. E.g.: `r:url()` could return a string `http://domain.com/api/test`. The URL might be empty depending on the step where this information is requested, as the URL is a calculated field just before performing the request to the backend. - `url(value)` (*Dynamic*): Setter that changes the URL of the request. E.g.: `r:url('http://domain.com/api/test')`. Changing the value before the `url` is calculated will result in KrakenD overwriting its value. Although available, it does not have any effect when you use it `modifier/lua-proxy`. - `params()` (*Dynamic*) : Getter returning a `luaTable` type with all the parameters defined in the endpoint. For instance `c:params():get('Foo')` when you have a `{foo}` in the endpoint. Notice that the parameters capitalize the first letter. - `params(param)` (*Dynamic*): Getter that retrieves the `{params}` of the request as defined in the endpoint. E.g.: For an endpoint `/users/{user}` the function `r:params('User')` could return a string `alice`. **The parameters must have the first letter capitalized**. - `params(param,value)` (*Dynamic*): Setter that changes the params of the request. It does not have any effect when you use `modifier/lua-backend`. E.g.: `r:params('User','bob')`. **The parameters must have the first letter capitalized**. - `headers()` (*Dynamic*) : Getter returning a `luaTable` type with all the headers in the request that survived the `input_headers`. For instance `r:headers():get('Content-Type'):get(1)` Only available on EE v2.10, CE v2.11 will have it too. - `headers(header)` (*Dynamic*): Getter that retrieves the headers of the request as allowed to pass (by `input_headers`) in the endpoint. E.g.: `r:headers('Accept')` could return a string `*/*`. - `headers(header,value)` (*Dynamic*): Setter that changes the headers of the request. E.g.: `r:headers('Accept','*/*')`. If the value is `nil`, the header is removed from the request. - `headerList(header)` (*Dynamic*): Getter that returns a `luaList` with the multiple values of a request header. E.g.: `req:headerList("X-Added")`. - `headerList(header,list)` (*Dynamic*): Setter that changes the headers with multiple values of the request. E.g.: `local n = luaList.new();n:set(0, "first"); n:set(1, "second");req:headerList("X-Added", n)`. - `body()` (*Dynamic*): Getter that retrieves the body of the request sent by the user. E.g.: `r:body()` could return a string `{"foo": "bar"}`. - `body(value)` (*Dynamic*): Setter that changes the body of the request. E.g.: `r:body('{"foo": "bar"}')`. ### Response functions (`response`) Scripts that need to modify a request that KrakenD that just got from the backend service. - `load()` (*Static*): The constructor to view and manipulate responses. E.g.: `local r = response.load()`. **Notice that the rest of the functions rely on this one**. - `isComplete()` (*Dynamic*): Getter that returns a boolean if the response from the backend (or a merge of backends) succeeded with a `20x` code, and completed successfully before the timeout. E.g.: `r:isComplete()` returns `true` or `false`. - `isComplete(bool)` (*Dynamic*): Setter that allows you to mark a response as completed. It will change the internal `X-KrakenD-Complete: true` header. E.g.: `r:isComplete(true)` tells KrakenD everything went OK (even not true). - `statusCode()` (*Dynamic*): Getter that retrieves the response status code when you use `no-op` encoding. You will always get a `0` in the other cases. E.g.: `r:statusCode()` returns an integer `200`. - `statusCode(integer)` (*Dynamic*): Setter that allows you to set a new status for the response. E.g.: `r:statusCode(301)`. It only works in `no-op` endpoints. - `data()` (*Dynamic*): Getter that returns a Lua table with all the parsed data from the response. It only works if you don’t use `no-op` encoding. - `data(table)` (*Dynamic*): While **there is no setter for the `data` function** you can set individual items of the data. For instance, do a `local responseData = r:data()` first, and then set individual items with `responseData:set("key", value)` instead. It only works if you don’t use `no-op` encoding. - `headers()` (*Dynamic*) : Getter returning a `luaTable` type with all the headers in the response when you use `no-op` encoding. For instance `r:headers():get('Cookie'):get(1)` Only available on EE v2.10, CE v2.11 will have it too. - `headers(header)` (*Dynamic*): Getter that retrieves one header from the response when you use `no-op` encoding. In the rest of the responses, you will always get an empty string `''`. E.g.: `r:headers('Content-Type')` returns an integer `application/json`. - `headers(header,value)` (*Dynamic*): Setter that allows you to replace or set a new header for the response when you use `no-op` encoding. E.g.: `r:headers('Content-Type', 'application/json')`. If the value is `nil`, the header is removed from the response. - `headerList(header)` (*Dynamic*): Getter that returns a `luaList` with the multiple values of a response header. E.g.: `r:headerList("X-Added")`. - `headerList(header,list)` (*Dynamic*): Setter that changes the headers with multiple values of the response. E.g.: `local n = luaList.new();n:set(0, "first"); n:set(1, "second");resp:headerList("X-Added", n)`. - `body()` (*Dynamic*): Getter that retrieves the body of the response when you use encoding `no-op`. E.g.: `r:body()` could return a string `{"foo": "bar"}`. - `body(value)` (*Dynamic*): Setter that changes the body of the response when you use encoding `no-op`. E.g.: `r:body('{"foo": "bar"}')`. ## Functions for Router Use this type when you need to script the router layer, traffic between end-users, and KrakenD with the `"modifier/lua-endpoint"` namespace. ### Context functions (`ctx`) - `load()` (*Static*): The constructor to view and manipulate requests. E.g.: `local c = ctx.load()`. **Notice that the rest of the functions rely on this one**. - `method()` (*Dynamic*): Getter that retrieves the method of the request. E.g.: `c:method()` could return a string `GET`. - `method(value)` (*Dynamic*): Setter that changes the method of the request. E.g.: `c:method('POST')`. - `query(key)` (*Dynamic*): Getter that retrieves the query string of the request, URL encoded. E.g.: `c:query('foo')` could return a string `var` for `?foo=var&vaz=42`. - `query(key,value)` (*Dynamic*): Setter that changes the query of the request. E.g.: `c:query('foo','var')`. - `url()` (*Dynamic*): Getter that retrieves the URL string of the request (path and parameters). E.g.: `c:url()` could return a string `/api/test?foo=bar`. The URL might be empty depending on the step where this information is requested, as the URL is a calculated field just before performing the request to the backend. - `url(value)` (*Dynamic*): Setter that changes the url of the request. E.g.: `c:url('/api/test?foo=bar')`. Changing the value before the `url` is calculated will result in KrakenD overwriting its value. - `params()` (*Dynamic*) : Getter returning a `luaTable` type with all the parameters defined in the endpoint. For instance `c:params():get('foo')` when you have a `{foo}` in the endpoint. Only available on EE v2.10, CE v2.11 will have it too. - `params(param)` (*Dynamic*): Getter that retrieves the `{params}` of the request as defined in the endpoint. E.g.: For an endpoint `/users/{user}` the function `c:params('User')` could return a string `alice`. **The parameters must have the first letter capitalized**. - `params(param,value)` (*Dynamic*): Setter that changes the params of the request. E.g.: `c:params('User','bob')`. **The parameters must have the first letter capitalized**. - `headers()` (*Dynamic*) : Getter returning a `luaTable` type with all the headers in the request that survived the `input_headers`. For instance `c:headers():get('Content-Type'):get(1)` Only available on EE v2.10, CE v2.11 will have it too. - `headers(header)` (*Dynamic*): Getter that retrieves the headers of the request as allowed to pass (by `input_headers`) in the endpoint. E.g.: `c:headers('Accept')` could return a string `*/*`. - `headers(header,value)` (*Dynamic*): Setter that changes the headers of the request. E.g.: `c:headers('Accept','*/*')`. - `headerList(header)` (*Dynamic*): Getter that returns a `luaList` with the multiple values of a context header. E.g.: `c:headerList("X-Added")`. - `headerList(header,list)` (*Dynamic*): Setter that changes the headers with multiple values of the request. E.g.: `local n = luaList.new();n:set(0, "first"); n:set(1, "second");ctx:headerList("X-Added", n)`. - `body()` (*Dynamic*): Getter that retrieves the body of the request sent by the user. E.g.: `c:body()` could return a string `{"foo": "bar"}`. - `body(value)` (*Dynamic*): Setter that changes the body of the request. E.g.: `c:body('{"foo": "bar"}')`. - `host()` (*Dynamic*): Getter that retrieves the `Host` header of the request sent by the user. E.g.: `c:host()` could return a string `api.domain.com`. - `host(value)` (*Dynamic*): Setter that changes the host header of the request. E.g.: `c:host('api.domain.com')`. ## Lua helpers Now you know where to put the Lua code according to what you want to do, and how to access and modify the requests and responses. In addition, the following helper functions are brought by KrakenD to extend the possibilities of your scripts without using third parties: ### Nil helper The `nil` in Lua is destructive in tables, because it is used to **remove elements**. For instance, if you have a table with an index `foo`, and you set it to `nil`, the element from the table is destroyed. When you want to preserve this `foo` index but emptying it’s value you must use the Go `nil` value instead. - `luaNil.new()`: Returns a native Go `nil` value. This is useful if you want to write `nil` values in a `luaTable` or a `luaList`. ### Tables helper (`luaTable`) To work with associative arrays on Lua you have the following functions: - `new()` (*Static*): Returns a new table. E.g.: `local t = luaTable.new()` - `keys()` (*Dynamic*): Returns the sorted key names of a table. E.g.: `local k = t:keys()` - `keyExists(key)`(*Dynamic*): Returns a boolean. True when `key` is a member of the table or false otherwise. E.g.: `local r = response.load(); local responseData = r:data(); responseData:keyExists('some_key')` - `get(key)` (*Dynamic*): Retrieves the value of a key inside the table. E.g.: `local r = response.load(); local responseData = r:data(); responseData:get('key')` - `set(key,value)` (*Dynamic*): Adds or replaces a key in the table. E.g.: `local r = response.load(); local responseData = r:data(); responseData:set('key',value)` - `len()` (*Dynamic*): Returns the length of the whole table so you can iterate over it. E.g.: `local r = response.load(); local responseData = r:data(); local length = responseData:len()` - `del(key)` (*Dynamic*): Deletes a key from a table. E.g.: `local r = response.load(); local responseData = r:data(); responseData:del('key')` An example of Lua script that gets a field `source_result` from a table and sets a new key `result` accordingly by reading the response text (decorator pattern): ```lua function post_proxy_decorator( resp ) local responseData = resp:data() local responseContent = responseData:get("source_result") local message = responseContent:get("message") local c = string.match(message, "Successfully") if not not c then responseData:set("result", "success") else responseData:set("result", "failed") end end ``` Another example that iterates all fields in the response and filters out everything but a specific field would be: ```lua -- This function filters out all keys in the response data except the 'keyToPreserve'. function filter(req, resp) -- Get the data from the response object local responseData = resp:data() -- Define the key that should not be removed local keyToPreserve = 'KeepThisField' -- Get all keys from the response data local allKeys = responseData:keys() -- Loop through all the keys for i = 0, allKeys:len() - 1 do local currentKey = allKeys:get(i) print("Found key:", currentKey) -- If the current key is not the one to preserve, remove it if currentKey ~= keyToPreserve then responseData:del(currentKey) print("Removing key:", currentKey) end end end ``` The `filter` function would be called as follows in the configuration: ```json { "modifier/lua-backend": { "allow_open_libs": true, "sources": ["./filter.lua"], "post": "filter(request.load(), response.load())" } } ``` ### Collections helper (`list`) - `new()` (*Static*): Returns a new list. E.g.: `local t = luaList.new()` - `get(key)` (*Dynamic*): Retrieves the value of a key inside the list. E.g.: `local r = response.load(); local responseData = r:data(); local l = responseData:get('collection'); l:get(1)` gets the item at position 1 from the list. - `set(key,value)` (*Dynamic*): Adds or replaces a key in the list. E.g.: `local r = response.load(); local responseData = r:data(); local l = responseData:get('collection'); l:set(1,value)` sets the value of position `1`. - `len()` (*Dynamic*): Returns the length of the whole list so you can iterate over it. E.g.: `local r = response.load(); local responseData = r:data(); local l = responseData:get('collection'); l:len()` - `del(key)` (*Dynamic*): Deletes an offset from a list. E.g.: `local r = response.load(); local responseData = r:data(); local l = responseData:get('collection'); l:del(1)` Example of Lua code that iterates the items under the array `collection` and also uses sets and deletes tables: ```lua -- A function that receives a response object through response.load() function post_proxy( resp ) local data = {} local responseData = resp:data() local col = responseData:get("collection") local size = col:len() -- Sets a new attribute "total" in the response with the number of elements in the array responseData:set("total", size) local paths = {} for i=0,size-1 do local element = col:get(i) local t = element:get("path") table.insert(paths, t) end responseData:set("paths", paths) responseData:del("collection") end ``` ### Advanced helpers The [Lua advanced helpers](/docs/enterprise/endpoints/lua-advanced-helpers/) (Enterprise ) provide a powerful way to extend the capabilities of your Lua scripts, enabling advanced functionality with optimal performance. These helpers, which run natively in Go, significantly enhance Lua’s efficiency by offloading heavy-lifting operations to Go and passing the results back to Lua. This architecture ensures both speed and reliability while empowering developers with advanced tools. The helpers cover a wide range of functionalities, from **debugging** to encoding and decoding JSON, YAML, XML, and CSV formats. Advanced features such as base64 encoding/decoding, hashing (e.g., SHA, MD5, FNV), and time manipulation expand the scope of Lua scripting, making it easier to handle complex scenarios. By leveraging these native helpers, you can process data, transform backend responses, and implement custom logic with minimal overhead and maximum performance. [Enterprise Lua Helpers](/docs/enterprise/endpoints/lua-advanced-helpers/ "Enterprise Lua Helpers") ### Making additional requests (`http_response`) The `http_response` helper allows you to make an additional HTTP request and access its response. Is is available on: - `"modifier/lua-proxy"` (endpoint level) - `"modifier/lua-backend"` (backend level) - `"modifier/lua-endpoint"` (endpoint level) The functions you can use for this helper are: - `new(url)` (*Static*): Constructor. Sets the URL you want to call and makes the request. E.g.: `local r = http_response.new('http://api.domain.com/test')`. **Notice that the rest of the functions rely on this one**. The constructor accepts 1, 3, or 4 arguments, respectively. See examples below. - `statusCode()` (*Dynamic*): Getter for the status code of the response. E.g.: `r:statusCode()` could return `200` - `headers(header)` (*Dynamic*) : Getter for a specific header of the response. E.g.: `r:headers('Content-Type')` could return `application/json` - `headerList(header)` (*Dynamic*): Getter that returns a `luaList` with the multiple values of a header. E.g.: `r:headerList("X-Added")`. - `body()` (*Dynamic*): Getter for the full response body. - `close()` (*Dynamic*): Closes the HTTP connection to free resources. Although it will be done automatically later by KrakenD, a better approach is to close the resource as soon as you don’t need it anymore. ```lua local url = 'http://api.domain.com/test' -- Constructor with 1 parameter local r = http_response.new(url) print(r:statusCode()) print(r:headers('Content-Type')) print(r:body()) r:close() -- Constructor with 3 parameters local r = http_response.new(url, "POST", '{"foo":"bar"}') print(r:statusCode()) print(r:headers('Content-Type')) print(r:body()) r:close() -- Constructor with 4 parameters local r = http_response.new(url, "POST", '{"foo":"bar"}', {["foo"] = "bar", ["123"] = "456"}) print(r:statusCode()) print(r:headers('Content-Type')) print(r:body()) r:close() ``` ### Set custom HTTP status codes (`custom_error`) A generic helper in pre and post-scripts that allows you to set **custom HTTP status codes**. For instance, when you want to send an immediate response to the client from within a Lua script without further querying the backend, or after evaluating the response of the backend. It stops the script and the pipe execution. Its signature is as follows: ```lua custom_error(message, status_code, content_type) ``` The parameters of the function are: - `message` (*string*): The message you want to send - `status_code` (*integer*): Optional. The HTTP status code - `content_type` (*string*): Optional. The `Content-Type` to return Example of throwing a generic error (`500` status code ) with a message: ```lua custom_error("Something weird happened") ``` Or even changing the HTTP status code (`418 I'm a teapot`) ```lua custom_error("I refuse to make any coffee, I'm a teapot!", 418) ``` ## Debugging scripts Because Lua scripts evaluate during the request execution, you won’t see any problems until a request triggers the scripts you have configured. While developing, the errors will be decorated so you can find easily which file and line number are the responsible ones for an error. For instance, have a look at the following log: ```log attempt to index a non-table object(function) with key 'really_bad' (bad-code.lua:5) ``` The ending `(bad-code.lua:5)` tells you that this error was initiated at the script `bad-code.lua` on **line number** `5`. To understand the errors, check the Lua language as these come from the Lua engine directly. ## Language limitations Because Lua runs sandboxed in a **virtual machine**, there is certain stuff you cannot do. When you include files, you should limit your logic to creating functions and referencing them in different places. In addition to that, the usage of modules would require KrakenD to push them in advance to the virtual machine, so user-created modules are not possible. **Package managers like LuaRocks are not supported** either, because they inject modules globally in the system, not in the virtual machine. Modules not supported You **cannot create your modules** neither use `require` to import them. Finally, if you need to use the standard library, you will need to add in the configuration the flag `allow_open_libs`. ## Lua examples in different pipes The following snippets show how to add Lua code in different sections. ### Lua in the service for all endpoints An example setting a common header in the request to all endpoints. ```json { "version": 3, "extra_config": { "modifier/lua-endpoint": { "pre": "print('Lua service!'); local c = ctx.load(); c:headers('X-from-lua', '1234');" } } } ``` ### Lua in the endpoint An example of setting lua scripts in three different stages that modify headers. ```json { "endpoint": "/set-a-header", "extra_config": { "modifier/lua-endpoint": { "pre": "print('Modifies the HTTP request'); local c = ctx.load(); c:headers('X-from-lua-endpoint', '1234');" }, "modifier/lua-proxy": { "@comment-pre": "Modifies the internal proxy request before the split to all backends", "pre": "print('Lua proxy pre modifier'); local r = request.load(); r:headers('X-from-lua-proxy', '1234');", "@comment-post": "Modifies the response after merging all backends", "post": "print('Lua proxy post modifier'); local r = response.load(); r:headers('X-from-lua-proxy', '1234');", } } } ``` - The `modifier/lua-endpoint` hits first as it modifies the HTTP request directly. Can’t modify the response (`post`). - The `pre` in `modifier/lua-proxy` modifies the internal request before it’s sent to each of the backends you have configured in the endpoint. Backends see an extra header. - The `post` in `modifier/lua-proxy` modifies the response after having merged all the contents from all backends. ### Lua in the backend An example showing how to **print the backend response** in the console. ```json { "extra_config": { "modifier/lua-backend": { "pre": "print('Backend response, pre-logic:'); local r = request.load(); print(r:body());" } } } ``` Another example **setting a cookie from Lua**: ```json { "extra_config": { "modifier/lua-backend": { "post": "local r = response.load(); r:headers('Set-Cookie', 'key1='.. r:data('response'));", "allow_open_libs": true } } } ``` --- # [Lua advanced helpers (available in KrakenD Enterprise)](https://www.krakend.io/docs/enterprise/endpoints/lua-advanced-helpers/) When running Lua scripts on the Enterprise Edition, a collection of helpers extends functionality and improves the speed of Lua execution. Because these helpers run natively on Go and their output is passed to the Lua virtual machine, their performance is optimal and much better than any behavior you can code within Lua libraries. ## Debug helpers The debug package contains a single method that allows you to dump into the console objects for debugging: - `debug.dump(anything, [comment=""])`: Prints in the console the value and type of the variable passed to the function. ### Dump example ```lua -- Dump full request debug.dump(request.load()) -- Dump full response with a comment debug.dump(response.load(), "My response comment") -- Dump context -- debug.dump(ctx.load()) -- Dump the request body -- debug.dump(json.unmarshal(request.load():body())) ``` The uncommented lines above could print in the console something like this: ``` DEBUG: [Lua] Dumping (map) headers: (map) X-Correlation-Id: [something] ([]string) X-Some-List: [i1 i2] ([]string) method: GET (string) params: (map) user: john (string) path: / (string) query: (string) url: https://example.com/ (string) DEBUG: [Lua] Dumping "My response comment" (map) data: (map) f1: v1 (string) f2: v2 (string) headers: (map) X-Multi-Value: [v1 v2] ([]string) X-Response: [a] ([]string) isComplete: true (bool) statusCode: 200 (float64) ``` As you can see, both objects are present in the console and contain details about their inner types. ## JSON encoding/decoding helpers They allow you to convert to JSON back and forth. - `json.marshal(object)`: Converts an object into a JSON string - `json.unmarshal(string)`: Converts a JSON string into a KrakenD `luaTable` ### JSON helper example Given a backend that responds with a JSON payload like the one below, we want to round balances: ```json [ { "id": 1, "name": "Mike", "balance": 10.3 }, { "id": 2, "name": "Jane", "balance": 11.15 }, { "id": 4, "name": "Jack", "balance": 56.05 } ] ``` The following Lua function does this job: ```lua -- Our response comes in JSON format. We want to unmarshal, tweak the balance, and set it as JSON again function json_marshal_test() local res = response.load() local report = json.unmarshal(res:data():get("content")) -- Round the balance for i=0,report:len()-1 do local r = report:get(i) report:get(i):set("balance", math.floor(r:get("balance"))) end -- end of rounding awesomeness res:data():del("content") res:data():set("content", json.marshal(report)) end ``` Note that when accessing the content of the backend we use `:get("content")` as our backend `encoding` used the `string` type (see [automated wrappers](/docs/enterprise/backends/supported-encodings/#automatic-content-wrappers-collection-and-content)). Also, when configuring our Lua component, we will need to set the flag `"allow_open_libs": true` so we can access the function `math.floor()`. ## YAML encoding/decoding helpers Similary, you can work natively with YAML content: - `yaml.marshal(object)` : Converts an object into a YAML string - `yaml.unmarshal(string)`: Converts a YAML string into a KrakenD `luaTable` ### YAML helper example Provided a backend that returns the following yaml content: ```yaml - id: 1 name: Mike balance: 10.3 - id: 2 name: Jane balance: 11.15 - id: 4 name: Jack balance: 56.05 ``` The following Lua function rounds the balances: ```lua function yaml_unmarshal_test() local res = response.load() local report = yaml.unmarshal(res:data():get("content")) -- Round the balance for i=0,report:len()-1 do local r = report:get(i) report:get(i):set("balance", math.floor(r:get("balance"))) end -- end of rounding awesomeness res:data():del("content") res:data():set("report", report) end ``` ## XML encoding/decoding helpers XML helpers work slightly different in their usage, but the signature is common to the others: - `xml.marshal(object)` : Converts a Lua native object into a XML string - `xml.unmarshal(string)`: Converts a XML string into a KrakenD `luaTable` ### XML helper example Provided a backend that returns the following XML content: ```xml 1 Mike 10.3 2 Jane 11.15 4 Jack 56.05 ``` This is its rounding balances function: ```lua function xml_marshal_test() local res = response.load() local t = xml.unmarshal(res:data():get("content")) local report = t:get("customers"):get("customer") round_balance(report) local r = {} r.customers = {} r.customers.customer = report res:data():del("content") res:data():set("content", xml.marshal(r)) end ``` ## CSV data helpers Sometimes we want to convert a CSV file into a JSON object, convert a JSON response into a CSV, or just manipulate data in the CSV and return it modified. The specific functions to work with CSV are: - `csv.marshal(object, [delimiter=","])` : Converts a Lua native object into a CSV string - `csv.unmarshal(string, [delimiter=","])`: Converts a CSV string into a KrakenD `luaTable` ### CSV transformation example Let’s say our backend returns a CSV file with the following content: ```csv id,name,balance 1,Mike,10.3 2,Jane,11.15 4,Jack,56.05 ``` The following Lua function rounds the balances from the CSV and returns the modified content: ```lua function csv_marshal_test() local res = response.load() local report = csv.unmarshal(res:data():get("content")) --- Round the balance for i=0,report:len()-1 do local r = report:get(i) report:get(i):set("balance", math.floor(r:get("balance"))) end -- end of rounding awesomeness res:data():set("content", csv.marshal(report)) -- Or return as JSON instead: --- res:data():set("content", json.marshal(report)) end ``` ## Base64 helpers The base64 helpers allow you to work with base64 content. The available helpers are: - `base64.encode(string|number)`: Converts a string or a number into a base64-encoded string - `base64.decode(string)`: Converts a base64-encoded string into its decoded text - `base64.urlencode(string|number)`: Converts a string or number into an URL-encoded string - `base64.urldecode(string)`: Decodes a URL-encoded string into its represented string ## Hashing functions helpers Hashing functions can help you with data integrity and privacy. There are quite a few hashing algorithms you can use: - `hashing.fnv1_32(string)`: Converts a string to its Fowler–Noll–Vo 32-bit hash representation (FNV-1) - `hashing.fnv1_64(string)`: Converts a string to its Fowler–Noll–Vo 64-bit hash representation (FNV-1) - `hashing.fnv1_32a(string)`: Converts a string to its Fowler–Noll–Vo 32-bit hash representation (FNV-1a) - `hashing.fnv1_64a(string)`: Converts a string to its Fowler–Noll–Vo 64-bit hash representation (FNV-1a) - `hashing.sha1(string)`: Converts a string to its 160-bit Secure Hash Algorithm (SHA-160) - `hashing.sha224(string)`: Converts a string to its 224-bit Secure Hash Algorithm (SHA-224) - `hashing.sha256(string)`: Converts a string to its 256-bit Secure Hash Algorithm (SHA-256) - `hashing.sha384(string)`: Converts a string to its 384-bit Secure Hash Algorithm (SHA-384) - `hashing.sha512(string)`: Converts a string to its 512-bit Secure Hash Algorithm (SHA-512) - `hashing.md5(string)`: Converts a string into its 128-bit Message-Digest Algorithm 5 (MD5) ## Time helpers The final group of helpers allows you to work natively with dates: - `time.parse(layout, string)`: Converts a string into a time variable according to the given layout. See [Go layout](https://pkg.go.dev/time#Layout) - `time.now()`: Returns a time variable with the current time according to the system’s clock. Given a `t` variable with any of the methods above, the following functions are available: - `t:unix()`: Returns the associated UNIX timestamp in seconds - `t:format(layout)`: Retuns the string representing the time in the [desired layout](https://pkg.go.dev/time#Layout) In addition, there is a parse duration package (which does not accept the functions above): - `time.parseduration(string|number)`: Given a KrakenD duration (e.g., `2s`, `1h`), it parses the string as a time variable. It also accepts a number as an argument representing nanoseconds. Given a `d` variable using the parse duration above, you can use the following functions: - `d:nanoseconds()`: Returns the nanoseconds contained within the duration - `d:string()`: Returns a string representing the duration (e.g., `1h30m`) --- # [Clustering Deployment Guide](https://www.krakend.io/docs/deploying/clustering/) KrakenD nodes **are stateless** and they don’t store data or application state to a persistent storage. Instead, any configuration data and application state exist within the configuration file. Nodes are expendable and replaceable at any time, as they do not hold anything. A KrakenD cluster consists of multiple KrakenD instances running simultaneously and working together to provide increased reliability, higher throughput, scalability, and fail-over. There is no special software that you need to run a cluster other than KrakenD and the hardware or software that will balance the connections. ![Load balancing KrakenD](/images/documentation/diagrams/load-balancing-to-krakend.mmd.svg) ## KrakenD cluster benefits Having a KrakenD cluster provides these immediate **benefits**: - **Increased throughput and capacity**: Having more KrakenD nodes expands the number of requests the API can handle. - **Plug-and-play nodes**: Adding more nodes does not require any coordination or process, spin more servers on the go without interruption of the service. - **Infinite scalability and zero coordination**: Unlike other gateways with shared state (and centralized coordination), every KrakenD node is stateless. Expect distributed, autonomous and independent nodes that do not communicate or coordinate between themselves, providing you infinite scalability. - **High-Availability and Failover**: If for any reason a server dies or the instance fails, the rest of the members in the cluster continue providing the service without affecting the global availability. - **Monitoring**: All KrakenD nodes individually report to an InfluxDB, Prometheus or any other of the available integrations you choose. Your metric collection systems will have the aggregated metrics of all the nodes. - **No single point of failure**: A completely distributed cluster without any external dependencies that can shut down the gateway (e.g., a database failure) - **Simple provision and maintenance**: A cluster that only requires spinning the servers with a copy of the same configuration file, it’s impossible to have a simpler solution. ## Running the cluster Running a cluster of machines is a straightforward process that only requires two conditions: - Having a [balancer in front of the machines](/docs/throttling/load-balancing/) (e.g., ELB, Haproxy, Kubernetes…) - Run two or more KrakenD services with the same configuration file If you are in the cloud, something like an [ELB](https://aws.amazon.com/elasticloadbalancing) or equivalent does the job. On-premises users can use [HAProxy](http://www.haproxy.org/). When you have your load balancer in place, register all the KrakenD instances so they can start receiving traffic. When all the desired nodes of KrakenD run, every instance honors its config and reports the traces and metrics to the services of your choice. For best practices deploying KrakenD, see [Deploying KrakenD](/docs/deploying/) --- # [Deploying KrakenD API Gateway - Best Practices and Guidelines](https://www.krakend.io/docs/deploying/) Setting up KrakenD is a straightforward process, but here are some not-so-obvious recommendations to get a good start when going live. This section has generalistic advice, even though every KrakenD installation is different. So let’s dip our toe into the deployment waters! ## Architecture recommendations ### High Availability Hardware can fail anytime, and a Gateway is critical enough to have redundancy. Having a cluster of machines operating the service assures high availability. It would be best if you always planned to have at least a couple of KrakenD servers/containers running in case one of them gets in trouble, even when you have low traffic. KrakenD can run in different regions and data centers transparently without any problem, as its nodes do not need to communicate with each other. [Setup a cluster of machines](/docs/deploying/clustering/) ### Place a balancer in front of KrakenD Put a [load balancer](/docs/throttling/load-balancing/) in front of KrakenD to distribute traffic between the different nodes of the cluster (Kubernetes already does this for you). Use always at least two KrakenD instances for High Availability. ### Server dimensioning Dimension KrakenD nodes according to your expected needs and throughput. See [server requirements](/docs/deploying/server-dimensioning/) ### Use several gateways The API gateway doesn’t need to be unique. We recommend using an independent KrakenD installation per consumer type. For instance, your iOS development team might need a gateway with different views of the consumed content compared to the Web Team. Needs and content in each team differ in each endpoint, and every team could optimize the contract for each case. ### Use HTTP2 Enable HTTP2 between your balancer and KrakenD API gateway for the best performance. There is nothing additional you need to configure in KrakenD. ### SSL Certificates Even that you can start KrakenD with SSL, you can add your public SSL certificate in the load balancer or PaaS and use **internal certificates**, or even no certificates at all (termination), between the [load balancer](/docs/throttling/load-balancing/) and KrakenD. ### Prepare for failure Add a [circuit breaker](/docs/backends/circuit-breaker/) to your backends to prevent KrakenD from pushing to a failing system. In addition, if you know that a particular backend does not support more than a number of requests, add a maximum number of requests using the [proxy rate limit](/docs/backends/rate-limit/). ## Configuration recommendations KrakenD will behave according to the configuration file, here are some recommendations: ### Add logging and suppress unnecessary information If you don’t add any logging, KrakenD will spit on stdout all the activity of the gateway. This behavior is not recommended for production. Enable the [logging](/docs/logging/) with `CRITICAL`, `ERROR`, or `WARNING` levels at most. Avoid `INFO` and `DEBUG` levels in production at all times. Below there is a **recommended configuration** in production for a good performance: ```json { "version": 3, "extra_config": { "telemetry/logging": { "level": "ERROR", "syslog": false, "stdout": true } } } ``` If you send the logs out to an [ELK](/docs/logging/logstash/) or a [GELF server](/docs/logging/graylog-gelf/), use `"stdout": false` to avoid having double output. Redirect output to /dev/null for maximum performance **When the output of KrakenD stdout is not important to you**, set the logging level to `CRITICAL` and redirect its output to `/dev/null` to have even more performance. To do that, start KrakenD with: ``` krakend run -c krakend.json >/dev/null 2>&1 ``` ### Remove access logs Removing the access log increases the number of requests per second the gateway can serve on high concurrency.[**Disable the access log**](/docs/service-settings/router-options/#disable_access_log) to gain more speed. You will still have the problems logged during runtime, but the requests won’t be outputted. ```json { "version": 3, "extra_config": { "router": { "disable_access_log": true } } } ``` ### Cache JWT keys If you use token validation and use a `jwk_url` (instead of a `jwk_local_path`), enable the caching. Otherwise, each endpoint will require to download the JWK over HTTP for every request, e.g.: ```json { "endpoint": "/protected/resource", "extra_config": { "auth/validator": { "alg": "RS256", "jwk_url": "https://some/.well-known/jwks.json", "cache": true } } } ``` ## Monitoring ### Enable traces and metrics Make sure you have visibility of what is going on. Choose any of the systems where you can send the metrics and enable them. There are many choices, but choose wisely and do not enable them all!. If you don’t use a SaaS provider, **a good self-hosted start** would be: - A [Grafana Dashboard](/docs/telemetry/grafana/) - Fueled by [InfluxDB](/docs/telemetry/influxdb/) - And full traces by [Jaeger](/docs/telemetry/jaeger/) Pay attention to the cardinality of the metrics. Logs and metrics might produce a lot of data and CPU activity. Aggregate and consolidate data in InfluxDB (e.g., when looking at the past year’s metrics, you don’t need minute resolution, and days will be enough). ## Deployment recommendations ### Release through a CI/CD pipeline Automate the go-live process through a [CI/CD pipeline](/docs/deploying/ci-cd/) that builds and checks KrakenD configuration before deploying. At least your pipeline should have: - `krakend check -d -t -c krakend.[tmpl|json|yml]` - `krakend check -c krakend.json --lint` If you don’t use flexible configuration, you can do it all in one line: `krakend check -d - t -c krakend.json --lint` It is also important adding the [audit command](/docs/configuration/audit/), excluding any rules that do not apply to you: - `krakend audit -c krakend.json` ### Use Docker and immutable containers Creating an immutable Docker image with your desired configuration takes a few seconds in your CI/CD pipeline on Docker deployments. Create a `Dockerfile` with at least the following code and deploy the resulting image in production: ```Dockerfile FROM krakend:2.13 COPY krakend.json /etc/krakend/krakend.json ``` Read more on [Docker artifacts](/docs/deploying/docker/) ### Use blue/green or similar deployment strategy As with Apache, Nginx, Varnish, and other stateless services, changing the configuration require a restart. When deploying new changes, use a technique like blue/green deployment or similar to make the deployment transparent for the user. This scenario can be automated and is available in Kubernetes and in all major cloud providers. The idea is that you spin up new machines with the latest configuration and then shift the traffic from the old instances to the new ones. This methodology ensures that **there is no downtime when applying changes**. Of course, on-premises installations can also make a similar approach, but the implementations depend on the underlying infrastructure. ## Code organization ### Name your configurations Add a `name` key in the configuration file with helpful information to identify your cluster’s specific version. Whatever type of information you write inside the `name` is open to your imagination. Any value you write is **available in the metrics** for inspection. ```json { "version": 3, "name": "Production Cluster rev-db6a182" } ``` **During the build in the pipeline**, it might be a good idea to **replace the content** of the `name` attribute with content showing the deployed version (the short SHA from the commit, maybe). ### Add comments and metadata (`@`) During startup, KrakenD **ignores from the configuration anything that it doesn’t recognize**, which means that your `krakend.json` (or whatever format you use) allows you to include additional metadata and fields that make sense to your company. Use it to add your meta language, tags, comments, bot integrations, etc., for better integration with your CI/CD system, deployment process, or a better future comprehension of the file. Validating KrakenD’s schema If you use the KrakenD `$schema` to validate your configuration, unknown attributes will trigger a warning during validation. To add your configurations schema-compatible, and have them ignored by KrakenD, prefix them with one of the following characters: `@`, `$`, `_` or `#`. For instance, you could add `@comment` fields. KrakenD does not use the field, and it passes the JSON schema validation. Finding it might be fresh air for the developer next to you. ```json { "endpoint": "/cookies", "input_headers": ["Cookie" ], "@comment": "At this early stage of the implementation, we still need to send cookies to the backend.", "backend": [{ "url_pattern": "/srv/legacy" }] } ``` ### Split the configuration in multiple repos or folders In large organizations with several teams using a shared gateway, you can split the endpoints into groups using folders or even different repositories. With the [flexible configuration](/docs/configuration/flexible-config/), you can have teams working in its dedicated space and aggregate all endpoints during build time without conflicts touching the same files. Most KrakenD configurations tend to be large and with repetitive blocks. Define a basic skeleton of configurations that will be used across all teams. --- # [Server Dimensioning for KrakenD API Gateway Deployment](https://www.krakend.io/docs/deploying/server-dimensioning/) When comparing KrakenD with other solutions, a key difference is that its **Total Cost of Ownership** is actually **lower**, and you can save a lot of money in infrastructure. On the hardware side, KrakenD is **very light** and consumes very low resources. For instance, the **consumption pattern** of the *baseline* (we will see this definition below) is around `100-200MB` of RAM and can work on production with `0.5 vCPU`. This *baseline* can process thousands of requests per second. ## Hardware specifications So, what are the server specifications (or container limits) I need to set for KrakenD? *It depends*. For starters, there is **no minimum size** needed to operate KrakenD, and there is no certain type of hardware that you need to have. You can use anything from bare-metal to virtualized environments and Docker containers. In fact, you can run KrakenD in the **small** virtual servers with very good results. Determining the server size Your final **server size** or **container limit** depends on many factors, like your chosen configuration and enabled components, the performance of your backends, the timeout settings, the network, the concurrency, and other factors. The proper right way to determine this size is **load testing the gateway**. Whatever is the server size you choose, the recommended approach is having **more small machines rather than one or two big ones**. Keep an eye on **network usage**, as it is the first limit that KrakenD hits on public-cloud instances with limited throughput. Your KrakenD machines can be at a 10% of CPU/memory usage and still you can reach the maximum amount of traffic your provider allows you to use. ### Guide to server sizing Let’s put some generalistic examples so you can quickly get an approximation in the following table. The recommended size is **per KrakenD node**, and assuming you have **high throughput**. And try to scale horizontally rather than vertically. You can use these numbers to start with sizing, albeit they are not written in stone: | vCPUs | Memory | Gateway features and purpose | |----------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `0.5CPU` | `512MB` | **Baseline** gateway (see definition below) with high throughput. No token validation, nor memory/cpu intensive operations and transformations. | | `1CPU` | `512MB` | **Baseline** gateway plus light cryptography algorithms in token validation, and moderate logging. | | `2vCPU` | `2GB` | **All purpose gateway** | | `4vCPU` | `4GB` | **Massive traffic** and **several memory/cpu intensive operations, complex cryptography**. | | `+4vCPU` | `+4GB` | Numbers above this will be when you use KrakenD to cache content, make use of high-memory or CPU functionalities massively, or your environment pushes the gateway to consume more resources. **Machines like this might indicate that you have a hidden problem**. | What is the baseline in the table? The baseline is a gateway with the following specifications enabled: - Routing - Encoding - Filters and basic manipulations - Circuit breaker - High throughput - No logging, neither token validation ### Top 10 Memory and CPU consumption patterns When extending the baseline, there are a few usual suspects responsible for memory or CPU consumption **when the throughput is high**. Computationally complex components put the gateway under more pressure, and a downsized machine will show symptoms of suffering on CPU/Memory. This is the **TOP 10** usages that make the gateway work more: 01. [High, misconfigured, or absent timeouts](/docs/throttling/timeouts/) (*memory*): Let’s say your backend is delaying its response while KrakenD keeps adding more and more new connections. In this scenario KrakenD will increase its memory consumption to hold those waiting for response users. Set low timeouts when possible (e.g: `3s`) and what is more important, that **KrakenD timeouts are larger than backend timeouts**. You don’t want KrakenD cutting connections while your backend keeps working on something the end-user will never see. 02. [Lua scripts](/docs/endpoints/lua/#supported-lua-types-cheatsheet) (*memory and CPU*) : They run in their own virtual context and need to be compiled in every execution. They also open the door to any code you can write that can lead to blocking operations. It’s up to you if you add an implementation *O(1)* or *O(N)*. If used in endpoints with a high hit-rate, consider moving the logic to a Go plugin: it won’t change any complexity you might have added but it will perform several times faster. 03. [Backend caching](/docs/backends/caching/) (*memory*): KrakenD does not limit the cache size you can use in any way. Your backend is in charge (through the `Cache-Control` header) of deciding how much memory KrakenD will need, the cardinality, and for how long!. Make sure you do not let the KrakenD cache go over its memory limit to see failures in its execution. 04. [Per-user rate limiting](/docs/endpoints/rate-limit/) (*memory*): User rate limiting requires KrakenD to store one unique counter per connected user, and per endpoint. The number of users and endpoints you have impacts on the total memory needed to store these limits. Pay attention to memory if you expect thousands of users or endpoints concurrently, and the length of the key you want to tokenize, as you will need to reserve memory to those counters. 05. [Token validation](/docs/authorization/jwt-validation/) or [signing](/docs/authorization/jwt-signing/) (*CPU*): Signing and validating JWT are in the top 5 operations with bigger computational cost. Try to choose least expensive algorithms (like `HS256`) over very expensive algorithms like the `RS256`. A special emphasis on high SHA algorithms like `RS512` as they can make you dimension your CPU to **x2.5** or more compared to `RS256`. If extra security is a must, consider using `ES256` instead of `RS512` to avoid a big impact. The more cryptographic complexity you add, the more CPU you are going to need. 06. [Flatmap](/docs/backends/flatmap/#flatmap-configuration) (*CPU*): If you have large responses of endless arrays and you need to traverse this large number of elements one by one to apply transformations, then prepare for extra time processing those manipulations. 07. [Over-inspection in Telemetry](/docs/telemetry/) (*CPU and memory*): Enabling several systems of Telemetry at the same time makes KrakenD work harder to report the metrics to all systems. Or when you have just one, but you have a lot of throughput and your metrics have a high cardinality, with all instrumentation layers enabled and you choose not to do sampling. 08. [Inadequate logging level](/docs/logging/) (*CPU and memory*): Having a `DEBUG` logging level in production versus not having logging at all has a huge difference in throughput (between x2 and x5 vs the *baseline*). Choose a level that provides you sufficient information to make decisions on production, and avoid having KrakenD writing every single detail of the interaction, and dedicate those resources to your end-users. 09. The [Bloomfilter](/docs/authorization/revoking-tokens/) (*memory*): Although it is very effective and space efficient, yet it can consume a lot of memory depending on its configuration, as you decide the maximum dataset there. 10. **Regular expressions** in general (*CPU*): For instance when you need to validate requests with the [Common Expression Language](/docs/endpoints/common-expression-language-cel/), or when you need to [detect bots](/docs/throttling/botdetector/). All the components above are resource-efficient but they offer full flexibility. Depending on the configuration you choose they can lead to an increased consumption of resources when compared with the baseline. --- # [CI/CD Deployment on the API Gateway](https://www.krakend.io/docs/deploying/ci-cd/) KrakenD operates with its single binary and your associated configuration. Therefore, your build process or CI/CD pipeline only needs to ensure that the configuration file is correct. These are a few recommendations to a safer KrakenD deployment: 1. Make sure the configuration file is valid. When using Flexible Configuration, generate the final `krakend.json` using `FC_OUT` as the final artifact 2. Optional - Ensure there are no severe security problems using the [`audit` command](/docs/configuration/audit/). 3. Optional - [Generate an immutable docker image](/docs/deploying/docker/) 4. Optional - [Run integration tests](/docs/developer/integration-tests/) 5. Deploy the new configuration There are several ways to automate KrakenD deployments, but **you must always test your configuration** before applying it in production. You’ll find a few notes that might help you automate this process in this document. For the first step, the `check` command is a must in any **CI/CD pipeline** or pre-deploy process to ensure you don’t put a broken setup in production that results in downtime. The `check` command lets you find broken configurations before going live. Add a line like the following in your release process: Recommended file check for CI/CD  ``` $krakend check --lint -t -d -c /path/to/krakend.json ``` The command above will stop the pipeline (`exit 1`) if it fails or continue if the configuration is correct. Make sure to always place it in your build/deploy process. [Read more about the `check` command](/docs/configuration/check/) ## Gitlab pipeline example Here you have an example pipeline for Gitlab. You can add more steps like the audit command, but it serves as an example that you can start with. You can also adapt this workflow to other CI/CD systems by looking at the actions performed: ```yaml # This file is a template, and needs editing before it works on your project. # In your first run you should check in what # Build a Docker image with CI/CD and push to the GitLab registry. # Docker-in-Docker documentation: https://docs.gitlab.com/ee/ci/docker/using_docker_build.html # # This template uses one generic job with conditional builds # for the default branch and all other (MR) branches. stages: - license - test - build # This step is only needed in Enterprise check-license: stage: license image: krakend:2.13.8 script: # Checks if the LICENSE file (must exist in the path) is valid for the next 90 days. # If it isn't, the deployment will fail, just to draw your attention. Lower the value afterwards. - krakend license valid-for 90d # Example to check the configuration using flexible configuration check_config: stage: test image: krakend:2.13.8 variables: FC_ENABLE: 1 FC_PARTIALS: $CI_PROJECT_DIR/config/partials FC_SETTINGS: $CI_PROJECT_DIR/config/settings/prod FC_TEMPLATES: $CI_PROJECT_DIR/config/templates FC_OUT: /tmp/krakend.json KRAKEND_FILE: $CI_PROJECT_DIR/config/krakend.tmpl KRAKEND_AUDIT_IGNORE: $CI_PROJECT_DIR/.krakend_audit_ignore script: - echo "FC_ENABLE is set to $FC_ENABLE" - echo "Runner working on path $(pwd)" - krakend check -tdc $KRAKEND_FILE - krakend check --lint -c $FC_OUT - krakend audit -c $FC_OUT --ignore-file=$KRAKEND_AUDIT_IGNORE --severity CRITICAL,HIGH - echo "--------------------------------------------------" - echo "------ YOU ROCK! KrakenD config looks good! ------" - echo "--------------------------------------------------" needs: - job: check-license optional: true # Create an immutable Docker image docker-build: image: docker:cli stage: build services: - docker:dind variables: DOCKER_IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY # All branches are tagged with $DOCKER_IMAGE_NAME (defaults to commit ref slug) # Default branch is also tagged with `latest` script: - docker build --pull -t "$DOCKER_IMAGE_NAME" . - docker push "$DOCKER_IMAGE_NAME" - | if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then docker tag "$DOCKER_IMAGE_NAME" "$CI_REGISTRY_IMAGE:latest" docker push "$CI_REGISTRY_IMAGE:latest" fi # Run this job in a branch where a Dockerfile exists rules: - if: $CI_COMMIT_BRANCH exists: - Dockerfile ``` --- # [Deploying KrakenD API Gateway with Docker](https://www.krakend.io/docs/deploying/docker/) If you use containers, the recommended approach is to write your own `Dockerfile` and deploy an **immutable artifact** (embedding the config). In its simplified form would be: ```Dockerfile FROM krakend:2.13 COPY krakend.json /etc/krakend/krakend.json # Uncomment with Enterprise image: # COPY LICENSE /etc/krakend/LICENSE ``` Volume or copy? Even though you can use the official container directly and attach the configuration mounting an external volume (or ConfigMap in Kubernetes), a custom image with your configuration copied inside has benefits. It guarantees that you can do safe rollbacks and have effective testing and debugging. If you break something at any point, you only need to deploy the previous container, while if you use a volume, you are exposed to downtime or impossible scaling until you fix it. A more real-life example illustrates below a combination of the `check` command with a multi-stage build to compile a [flexible configuration](/docs/configuration/flexible-config/) in a `Dockerfile`: ```docker FROM krakend:2.13 as builder ARG ENV=prod COPY krakend.tmpl . COPY config . # Save temporary file to /tmp to avoid permission errors RUN FC_ENABLE=1 \ FC_OUT=/tmp/krakend.json \ FC_PARTIALS="/etc/krakend/partials" \ FC_SETTINGS="/etc/krakend/settings/$ENV" \ FC_TEMPLATES="/etc/krakend/templates" \ krakend check -d -t -c krakend.tmpl --lint FROM krakend:2.13 # Keep operating system updated with security fixes between releases RUN apk upgrade --no-cache --no-interactive COPY --from=builder --chown=krakend:nogroup /tmp/krakend.json . # Uncomment with Enterprise image: # COPY LICENSE /etc/krakend/LICENSE # Uncomment if you have certificates issued by a custom CA # e.g., tls: failed to verify certificate: x509: certificate signed by unknown authority # # COPY yourca.pem /usr/local/share/ca-certificates/ # RUN update-ca-certificates ``` The `Dockerfile` above has two stages: 1. The copy of all templates and intermediate files to end with a `check` command that compiles the template `krakend.tmpl` and any included sub-template inside. The command outputs (thanks to `FC_OUT`) the result into a `/tmp/krakend.json` file. 2. The `krakend.json` file from the previous build is the only addition to the final Docker image. The example `Dockerfile` above assumes that you have a file structure like this: ``` . ├── config │ ├── partials │ ├── settings │ │ ├── prod │ │ │ └── env.json │ │ └── test │ │ └── env.json │ └── templates │ └── some.tmpl ├── Dockerfile └── krakend.tmpl ``` If you want to try this code, you can either download a [working Flexible Config example](https://github.com/krakend/examples/tree/main/3.flexible-configuration), or generate an **empty skeleton** like this: ```bash mkdir -p config/{partials,settings,templates} mkdir -p config/settings/{prod,test} touch config/settings/{prod,test}/env.json touch Dockerfile touch krakend.tmpl ``` Now the only missing step to generate the image, is to build it, making sure that the environment argument matches our folder inside the `settings` folder: Docker build  ``` $docker build --build-arg ENV=prod -t mykrakend . ``` The resulting image, including your configuration, weighs around `80MB`. --- # [Deploying KrakenD API Gateway on Kubernetes](https://www.krakend.io/docs/deploying/kubernetes/) Deploying KrakenD in Kubernetes requires a straightforward configuration. Create a `Dockerfile` that includes the configuration of the service. Read how to generate a [Docker artifact](/docs/deploying/docker/) for detailed instructions. You could also use a ConfigMap, although the recommendation is to use immutable artifacts. From here you need to create a `NodePort` and send all the traffic to KrakenD. Run as user 1000 Whether you run KrakenD on Kubernetes, OpenShift, or any other platform with the capability to run the container as a specific user UID, make sure you use the **UID 1000** ## Deployment definition YAML The KrakenD `deployment` definition, in a file called `deployment-definition.yaml`: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: krakend-deployment spec: selector: matchLabels: app: krakend replicas: 2 template: metadata: labels: app: krakend spec: containers: - name: krakend image: YOUR-KRAKEND-IMAGE:1.0.0 ports: - containerPort: 8080 imagePullPolicy: Never command: [ "/usr/bin/krakend" ] args: [ "run", "-d", "-c", "/etc/krakend/krakend.json", "-p", "8080" ] securityContext: allowPrivilegeEscalation: false runAsNonRoot: true runAsUser: 1000 readOnlyRootFilesystem: true capabilities: drop: - ALL add: - NET_BIND_SERVICE env: - name: KRAKEND_PORT value: "8080" ``` ## Service definition yaml The KrakenD `service` definition, in a file called `service-definition.yaml`: ```yaml apiVersion: v1 kind: Service metadata: name: krakend-service spec: type: NodePort ports: - name: http port: 8000 targetPort: 8080 protocol: TCP selector: app: krakend ``` ## Registering the service Using the `kubectl` command: Register deployment  ``` $kubectl create -f deployment-definition.yaml ``` Register service  ``` $kubectl create -f service-definition.yaml ``` For a more step by step process see [this blog entry](/blog/krakend-on-kubernetes/). ## Helm Chart There is no official Helm chart for KrakenD. However, there is a Helm chart here tracking Krakend releases maintained by [Equinix Metal](https://github.com/equinixmetal-helm/krakend). --- # [API Gateway Performance Benchmarks with KrakenD](https://www.krakend.io/docs/benchmarks/) ## KrakenD, the **ultra performer** API Gateway An API Gateway is a component that needs to deliver really fast, as it is an added layer in the infrastructure. KrakenD was built with performance in mind. In this page and inner pages, you’ll find several tests we did to measure the performance. We also invite you to do them for yourself! ## TL;DR: **Benchmark results** **~18,000 requests/second** on an ordinary laptop. The following table summarizes different performance tests using Amazon EC2 virtual instances and an example with a laptop. | \# | Hardware specs | Requests second | Average response | |-----|----------------------------------------------|-------------------|------------------| | 1\. | Amazon EC2 (c4.2xlarge) | 10126.1613 reqs/s | 9.8ms | | 2\. | Amazon EC2 (c4.xlarge) | 8465.4012 reqs/s | 11.7ms | | 3\. | Amazon EC2 (m4.large) | 3634.1247 reqs/s | 27.3ms | | 4\. | Amazon EC2 (t2.medium) | 2781.8611 reqs/s | 351.3ms | | 5\. | Amazon EC2 (t2.micro) | 2757.6407 reqs/s | 35.8ms | | 6\. | MacBook Pro (Aug 2015) 2,2 GHz Intel Core i7 | 18157.4274 reqs/s | 5.5ms | ### Benchmark in a MacBook Pro [Here you will find the results of the benchmarks](/docs/benchmarks/local/) ### Benchmark in Amazon AWS EC2 Instances [Here you will find the results of the benchmarks](/docs/benchmarks/aws/) ### API Gateway Benchmark [Here you will find the results of the comparisons](/docs/benchmarks/api-gateway-benchmark/) ## Tooling ### hey Some local benchmarks used the [hey](https://github.com/rakyll/hey) tool, which is an Apache Benchmark (ab) replacement tool. ### api-gateway-benchmark > This project aims to provide a complete set of tools needed to do simple performance comparisons in the API manager/gateway space. It is inspired by the great Framework Benchmarks project by TechEmpower. Check the [varnish/api-gateway-benchmarks](https://github.com/varnish/api-gateway-benchmarks) project for more info. ### Lwan [LWAN](https://lwan.ws/) is a high performance web server used to build the backends REST APIs for KrakenD to load the data during the benchmarks. --- # [API Gateway Benchmarking with KrakenD](https://www.krakend.io/docs/benchmarks/api-gateway-benchmark/) We wanted to compare our own product with other similar products in the market. In order to do so we used the same environment and conditions and tested the following products: - Kong - Vulcand - Tyk - KrakenD For the benchmarks, we based the tests on the benchmarking project [varnish/api-gateway-benchmarks](https://github.com/varnish/api-gateway-benchmarks). **At the time of writing, KrakenD does not support auth features, so we just did the benchmark with *test01*** ## Hardware hardware specs  ``` $Model MacBook Pro (MacBookPro11,4) - August 2015 Processor: Intel Core i7 2,2 GHz ``` ## Setup For this test, we stored this configuration at `krakend.json` ```json { "version": 1, "host": [ "http://webserver:8888" ], "endpoints": [ { "endpoint": "/test01", "method": "GET", "concurrent_calls": "1", "backend": [ { "url_pattern": "/test01" } ] }, { "endpoint": "/test03", "method": "GET", "backend": [ { "url_pattern": "/test03" } ], "max_rate": "1000000" }, { "endpoint": "/test04", "method": "GET", "max_rate": "1", "backend": [ { "url_pattern": "/test04" } ] } ], "oauth": { "disable": true }, "cache_ttl": "5m", "timeout": "5s" } ``` And we started the environment as described in the [README](https://github.com/varnish/api-gateway-benchmarks/blob/master/README.md#deployment-example) Start the environment  ``` $cd deployment/vagrant $ vagrant up $ vagrant ssh webserver [vagrant@webserver ~]$ cd /opt/benchmarks/webservers/dummy-api [vagrant@webserver dummy-api]$ sudo ./deploy [vagrant@webserver dummy-api]$ exit $ vagrant ssh gateway [vagrant@gateway ~]$ cd /opt/benchmarks/gateways/krakend [vagrant@gateway krakend]$ sudo ./deploy [vagrant@gateway krakend]$ exit $ vagrant ssh consumer [vagrant@consumer ~]$ cd /opt/benchmarks/consumers/boom [vagrant@consumer boom]$ sudo ./deploy [vagrant@consumer boom]$ /usr/local/bin/test00 [vagrant@consumer boom]$ /usr/local/bin/test01 ``` ## Results ### Test 00 ``` [vagrant@consumer ~]$ /usr/local/bin/test00 100000 / 100000 Booooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00 % Summary: Total: 13.2722 secs. Slowest: 0.1720 secs. Fastest: 0.0002 secs. Average: 0.0132 secs. Requests/sec: 7534.5524 Total Data Received: 10800000 bytes. Response Size per Request: 108 bytes. Status code distribution: [200] 100000 responses Response time histogram: 0.000 [1] | 0.017 [72298] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 0.035 [26760] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 0.052 [522] | 0.069 [227] | 0.086 [93] | 0.103 [28] | 0.120 [4] | 0.138 [10] | 0.155 [24] | 0.172 [33] | Latency distribution: 10% in 0.0039 secs. 25% in 0.0076 secs. 50% in 0.0129 secs. 75% in 0.0180 secs. 90% in 0.0218 secs. 95% in 0.0233 secs. 99% in 0.0333 secs. ``` ### Test 01 #### KrakenD ``` [vagrant@consumer ~]$ /usr/local/bin/test01 100000 / 100000 Booooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00 % Summary: Total: 28.7424 secs. Slowest: 0.2781 secs. Fastest: 0.0009 secs. Average: 0.0287 secs. Requests/sec: 3479.1863 Total Data Received: 10900000 bytes. Response Size per Request: 109 bytes. Status code distribution: [200] 100000 responses Response time histogram: 0.001 [1] | 0.029 [71546] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 0.056 [26536] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 0.084 [1061] | 0.112 [392] | 0.140 [248] | 0.167 [93] | 0.195 [51] | 0.223 [12] | 0.250 [27] | 0.278 [33] | Latency distribution: 10% in 0.0213 secs. 25% in 0.0231 secs. 50% in 0.0245 secs. 75% in 0.0295 secs. 90% in 0.0432 secs. 95% in 0.0469 secs. 99% in 0.0771 secs. ``` #### Vulcand ``` [vagrant@consumer ~]$ /usr/local/bin/test01 100000 / 100000 Booooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00 % Summary: Total: 50.5294 secs. Slowest: 0.3426 secs. Fastest: 0.0010 secs. Average: 0.0505 secs. Requests/sec: 1979.0451 Total Data Received: 10800000 bytes. Response Size per Request: 108 bytes. Status code distribution: [200] 100000 responses Response time histogram: 0.001 [1] | 0.035 [21120] |∎∎∎∎∎∎∎∎∎∎∎ 0.069 [71365] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 0.103 [5946] |∎∎∎ 0.138 [168] | 0.172 [74] | 0.206 [329] | 0.240 [496] | 0.274 [388] | 0.308 [88] | 0.343 [25] | Latency distribution: 10% in 0.0290 secs. 25% in 0.0378 secs. 50% in 0.0490 secs. 75% in 0.0571 secs. 90% in 0.0665 secs. 95% in 0.0733 secs. 99% in 0.2057 secs. ``` #### Kong ``` [vagrant@consumer ~]$ /usr/local/bin/test01 100000 / 100000 Booooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00 % Summary: Total: 57.0194 secs. Slowest: 1.5978 secs. Fastest: 0.0076 secs. Average: 0.0569 secs. Requests/sec: 1753.7883 Total Data Received: 13600000 bytes. Response Size per Request: 136 bytes. Status code distribution: [200] 100000 responses Response time histogram: 0.008 [1] | 0.167 [97506] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 0.326 [2290] | 0.485 [103] | 0.644 [0] | 0.803 [0] | 0.962 [0] | 1.121 [0] | 1.280 [0] | 1.439 [17] | 1.598 [83] | Latency distribution: 10% in 0.0407 secs. 25% in 0.0435 secs. 50% in 0.0461 secs. 75% in 0.0497 secs. 90% in 0.0816 secs. 95% in 0.1076 secs. 99% in 0.2355 secs. ``` #### Tyk ``` [vagrant@consumer ~]$ /usr/local/bin/test01 100000 / 100000 Booooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00 % Summary: Total: 221.5803 secs. Slowest: 5.6482 secs. Fastest: 0.0012 secs. Average: 0.2215 secs. Requests/sec: 451.3037 Total Data Received: 10800000 bytes. Response Size per Request: 108 bytes. Status code distribution: [200] 100000 responses Response time histogram: 0.001 [1] | 0.566 [90838] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 1.131 [4890] |∎∎ 1.695 [2208] | 2.260 [1380] | 2.825 [383] | 3.389 [161] | 3.954 [86] | 4.519 [40] | 5.084 [6] | 5.648 [7] | Latency distribution: 10% in 0.0355 secs. 25% in 0.0521 secs. 50% in 0.0823 secs. 75% in 0.1785 secs. 90% in 0.5263 secs. 95% in 0.9231 secs. 99% in 2.1054 secs. ``` ## Summary ### Requests per second | Gateway | RPS | |---------|-----------| | None | 7534.5524 | | KrakenD | 3479.1863 | | Vulcand | 1979.0451 | | Kong | 1753.7883 | | Tyk | 451.3037 | ### Response time distribution *Time in milliseconds* | Gateway | 10% | 25% | 50% | 75% | 90% | 95% | 99% | Min | Max | Avg | |---------|------|------|------|-------|-------|-------|--------|-----|--------|-------| | KrakenD | 21.3 | 23.1 | 24.5 | 29.5 | 43.2 | 46.9 | 77.1 | 0.9 | 278.1 | 28.7 | | Vulcand | 29.0 | 37.8 | 49.0 | 57.1 | 66.5 | 73.3 | 205.7 | 1.0 | 342.6 | 50.5 | | Kong | 40.7 | 43.5 | 46.1 | 49.7 | 81.6 | 107.6 | 235.5 | 7.6 | 1597.8 | 56.9 | | Tyk | 35.5 | 52.1 | 82.3 | 178.5 | 526.3 | 923.1 | 2105.4 | 1.2 | 5648.2 | 221.5 | --- # [Benchmarking KrakenD API Gateway on AWS](https://www.krakend.io/docs/benchmarks/aws/) The following numbers show the execution results for the KrakenD benchmarks on [Amazon EC2](https://aws.amazon.com/ec2/) machines. ## Benchmark Setup This set of benchmarks have been running on different AWS EC2 instances. Each individual test consists of spinning up 3 different machines, being: - **A web server**: A [LWAN](https://lwan.ws/) web server using an instance `c4.xlarge`. This is the “fake API” where KrakenD will take the data - **The HTTP load generator**: The machine actually running the load test. Uses [**hey**](https://github.com/rakyll/hey), and runs in a `t2.medium`. - **KrakenD**: Each different test uses a different instance type in Amazon: The test consists in running `hey` against a KrakenD endpoint. The KrakenD endpoint uses as the backend an URL in (`LWAN`). After running the test, the `hey` output is [parsed and converted to CSV](https://github.com/devopsfaith/hey-to-csv) in order to generate the graphs. For each instance type there are 2 different tests: - **Proxy**: When the KrakenD is just used as a gateway and calls to a single endpoint to the web server (`/foo` endpoint in the configuration). - **Aggregate**: When the KrakenD calls to 3 different endpoints in the web server and aggregates the results (`/social` endpoint in the configuration). The instance types we tested are: | Instance Type | Number of vCPU | Memory | |---------------|----------------|--------| | t2.micro | 1 | 1 GB | | t2.medium | 2 | 4 GB | | m4.large | 2 | 8 GB | | c4.xlarge | 4 | 7.5 GB | | c4.2xlarge | 8 | 15 GB | ## KrakenD Configuration for all tests The configuration for the load test was stored in the `krakend.json` file, as follows: ``` { "version": 1, "host": [ "http://lwan:8080" ], "endpoints": [ { "endpoint": "/foo", "method": "GET", "backend": [ { "url_pattern": "/bar" } ], "concurrent_calls": "1", "max_rate": 100000 }, { "endpoint": "/social", "method": "GET", "backend": [ { "url_pattern": "/fb", "group": "fb" }, { "url_pattern": "/youtube", "target": "data", "group": "youtube" }, { "url_pattern": "/twitter", "group": "twitter" } ], "concurrent_calls": "1", "timeout": "500ms", "cache_ttl": "12h" } ], "oauth": { "disable": true }, "cache_ttl": "5m", "timeout": "5s" } ``` Notice that `Lwan` is the backend running at `lwan:8080`. And we started the KrakenD with this cmd (debug mode):   ``` $krakend run --config krakend.json -d > /dev/null ``` ## Results ### Proxy test on `t2.micro` ### Aggregate test on `t2.micro` ### Proxy test on `t2.medium` ### Aggregate test on `t2.medium` ### Proxy test on `m4.large` ### Aggregate test on `m4.large` ### Proxy test on `c4.xlarge` ### Aggregate test on `c4.xlarge` ### Proxy test on `c4.2xlarge` ### Aggregate test on `c4.2xlarge` ### Conclusions During all the tests we did, the instances of type `c4` always showed a stable behavior while the `m4` types didn’t offer a proportional increase in the performance and the variance of the responses is too high. The instances `micro` provide nice figures of rps and latency for a good money. It looks like they suffer a little bit more in the aggregated tests but in general it is a good choice. To be taken into account that this type of service is CPU intensive so when using `t2` instances once you spend your CPU credit the instance will perform worst. **In general terms**: - Use `micro` instances by default. - If you expect high and continued load with complex use cases (intensive aggregation and manipulation) `c4.2xlarge` is worth it - If you want to maintain quality of service with high load but a relative simple app, `c4.xlarge` - For low to moderate loads use `micro` or a cluster of micros. - We wouldn’t choose `m4` in any scenario for the money/performance. Look at the numbers and the use case you’ll have in order to choose the right solution for you. And more importantly, do the tests using your own data. This is a reference to contrast your own tests. --- # [Benchmarking KrakenD API Gateway on a local machine](https://www.krakend.io/docs/benchmarks/local/) ## Hardware ``` Model MacBook Pro (MacBookPro11,4) - August 2015 Processor: Intel Core i7 2,2 GHz ``` ## Setup For this test, we stored this configuration at `krakend.json` ``` { "version": 1, "endpoints": [ { "endpoint": "/foo", "method": "GET", "backend": [ { "url_pattern": "/__debug/bar", "host": [ "http://127.0.0.1:8080" ] } ], "concurrent_calls": "1", "max_rate": 100000 } ], "oauth": { "disable": true }, "cache_ttl": "5m", "timeout": "5s" } ``` And we started the KrakenD with this cmd: ``` $ ./krakend run --config krakend.json -d > /dev/null ``` Response from the ‘debug backend’: ``` $ curl -i http://127.0.0.1:8080/__debug/bar HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Mon, 28 Nov 2016 15:42:16 GMT Content-Length: 19 {"message":"pong"} ``` Response from the KrakenD: ``` $ curl -i http://127.0.0.1:8080/foo HTTP/1.1 200 OK Cache-Control: public, max-age=300 Content-Type: application/json; charset=utf-8 X-Krakend: Version 0.3.8 Date: Mon, 28 Nov 2016 15:42:20 GMT Content-Length: 19 {"message":"pong"} ``` ## Results ``` $ hey -c 200 -n 100000 http://127.0.0.1:8080/foo 8217 requests done. 17379 requests done. 25928 requests done. 34664 requests done. 43290 requests done. 52069 requests done. 61057 requests done. 69954 requests done. 78815 requests done. 87565 requests done. 96615 requests done. All requests done. Summary: Total: 5.6980 secs Slowest: 0.0494 secs Fastest: 0.0003 secs Average: 0.0113 secs Requests/sec: 17549.8782 Total data: 1900000 bytes Size/request: 19 bytes Status code distribution: [200] 100000 responses Response time histogram: 0.000 [1] | 0.005 [974] |∎ 0.010 [42346] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 0.015 [44698] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 0.020 [10393] |∎∎∎∎∎∎∎∎∎ 0.025 [1242] |∎ 0.030 [180] | 0.035 [121] | 0.040 [22] | 0.045 [15] | 0.049 [8] | Latency distribution: 10% in 0.0082 secs 25% in 0.0091 secs 50% in 0.0106 secs 75% in 0.0130 secs 90% in 0.0155 secs 95% in 0.0173 secs 99% in 0.0212 secs ``` ## Summary --- # [Design principles. The big picture.](https://www.krakend.io/docs/design/) Before starting to dive into the KrakenD code, you should spend a few minutes understanding the big pieces of the system, how they work, and the philosophy behind it. ## The design rules Let’s start with the rules followed to code KrakenD (shared with [The Lura Project](https://luraproject.org)), as they answer architectural design questions: - [Reactive is key](http://www.reactivemanifesto.org/) - Failing fast is better than succeeding slow - The simpler, the better - Everything is pluggable - Each request must be processed in its request-scoped context ## KrakenD internal states When you start KrakenD, the system goes through two internal states: **building** and **working**. So let’s see what happens in every state. ### Building state The building state administers the service start-up and prepares the system before it can start receiving traffic. During the building state, three things happen: - Parsing of the configuration to fix the system behavior - Preparation of the middlewares (all components in KrakenD) - Construction of the pipes (see below) A `pipe` is a function that receives a request message, processes it, and produces the response message and an error. The KrakenD router binds the pipes to the selected transport layer (e.g., HTTP, gRPC). When the building state finishes, the KrakenD service **will not need to calculate any route** or lookup for the associated handler function, as all the mapping is direct in-memory. This is a crucial difference with any other system and leads to a significant performance. [Read more on pipes](/docs/design/execution-flow/) ### Working state The working state is when the system is ready and can process the requests. When they arrive, the `router` already has the request mapping with the handler function and triggers the pipe execution. The `proxy` is the step of the pipe that manipulates, aggregates, and does other data handling for the rest of the process. As the handler functions are in the previous step, **KrakenD doesn’t penalize the performance depending on the number of endpoints or the possible cardinality** of the URIs requested by the users. ## The important packages The Lura Project (KrakenD’s engine) is composed of a set of packages designed as building blocks for creating pipes and processors between an exposed endpoint and one or several API resources served by your backends. ![Lura packages](/images/documentation/config-router-proxy-packages.png) The most important packages are: 1. the `config` package defines the service. 2. the `router` package sets up the endpoints exposed to the clients. 3. the `proxy` package adds the required middlewares and components for further processing of the requests and responses of the backends. It also manages the connections against those backends. The rest of the framework packages contain some helpers and adapters for additional tasks, like encoding, logging, or service discovery. Additionally, KrakenD bundles a lot of middleware and components that are in its scope and package. These packages and others are listed in our [KrakenD Contrib](https://github.com/krakend/krakend-contrib) repository. ### The `config` package The `config` package contains the structs required for the service description. The `ServiceConfig` struct defines the entire service. Initialize it before using it to ensure that all parameters are normalized and that default values are applied. The `config` package also defines an interface for a file config parser and a parser based on the [Viper](https://github.com/spf13/viper) library. ### The `router` package The `router` package contains an interface and several implementations for the KrakenD router layer using the `mux` router from the `net/http` and the `httprouter` wrapped in the `gin` framework. The router layer is responsible for setting up the HTTP(S) services, binding the endpoints defined at the `ServiceConfig` struct, and transforming the HTTP request into proxy requests before delegating the task to the inner layer (proxy). Once the internal proxy layer returns a proxy response, the router layer converts it into a proper HTTP response and sends it to the user. This layer can be easily extended to use any HTTP router, framework, or middleware of your choice. ### The `proxy` package The `proxy` package is where most of the KrakenD components and features are. It defines two necessary interfaces designed to be stacked: - *Proxy* is a function that converts a given context and request into a response. - *Middleware* is a function that accepts one or more proxies and returns a single proxy wrapping them. This layer transforms the request received from the outer layer (router) into a single or several requests to your backend services, processes the responses, and returns a single response. Middlewares generate chained custom proxies depending on the workflow defined in the configuration until each possible branch ends in a transport-related proxy. These generated proxies can transform the input or clone it several times and pass it to the next element in the chain. Finally, they can modify the received response or responses, adding all kinds of features to the generated pipe. The Lura Project provides a default implementation of the proxy stack factory. #### Middlewares available - The `balancing` middleware uses some strategy for selecting a backend host to query. - The `concurrent` middleware improves the QoS by sending several concurrent requests to the next step of the chain and returning the first successful response using a timeout for canceling the generated workload. - The `logging` middleware logs the received request and response and the segment execution duration. - The `merging` middleware is a fork-and-join middleware. It is intended to split the request process into several concurrent processes, each against a different backend, and to merge all the received responses from those created pipes into a single one. It applies a timeout, as the `concurrent` one does. - The `http` middleware completes the received proxy request by replacing the parameters extracted from the user request in the defined `URLPattern`. #### Proxies available - The `http` proxy translates a proxy request into an HTTP one, sends it to the backend API using an `HTTPClientFactory`, decodes the returned HTTP response with a `Decoder`, manipulates the response data with an `EntityFormatter`, and returns it to the caller. #### Other components of the `proxy` package The `proxy` package also defines the `EntityFormatter`, the block responsible for enabling a powerful and fast response manipulation. --- # [KrakenD Execution Flow Design](https://www.krakend.io/docs/design/execution-flow/) To truly master KrakenD, you should get familiar with **the concept of “pipes”** and how these pipes define the execution flow of KrakenD from request to response. A recurring question we hear from developers: ***Is the configuration X executed before the configuration Y?***. If you are unfamiliar with KrakenD, it’s hard to tell, as **the declaration order does not matter** (with a few sequential exceptions). Instead, each piece acts in a specific part(s) of the request and response journey. ## What is a pipe? **A pipe is a function** that receives a request message, processes it, and produces the response message and an error. A pipe binds to a specific transport layer (e.g., HTTP, gRPC). Each pipe loads the different factories of the components of KrakenD, what you could call its features. For instance, you might want to connect KrakenD to a Lambda function, a Kafka server or rate limit its interaction with your API, etc. All these features are part of the `Backend` pipe. ## Execution flow (pipes) There are six differentiated parts on KrakenD pipes, as depicted in the following diagram: ![Execution flow](/images/documentation/diagrams/components-sequence-simple.mmd.svg) - `HTTP Adapter` or also known as `RunServer` is the entry conduit that receives the request from the user. In this phase, you can add your custom HTTP Server plugins and execute other components like Analytics or CORS. - `Router` is the pipe part that identifies to which endpoint a request goes and allows to perform security checks before this happens. Both the `HTTP Adapter` and the `Router`are shared across all pipes. - `Endpoint` enters for a specific endpoint (e.g.: `/foo`) and is a stage that allows you to do all kinds of validations and functionalities like validating tokens or modifying the request. One endpoint can connect sequentially or in parallel to many services (HTTP or not), and the endpoint defines all the places KrakenD will connect to. In this stage KrakenD converts the HTTP request into an internal KrakenD request (its domain). - `Proxy` is the phase that splits the request into many connections (when necessary) and the other way around, merging the data from multiple places. Inside this pipe you can work with the data before it’s sent to all your backends, or after it comes back from them. - `Async agent` more than a pipe is a trigger that, without any request intervention, will execute a `Proxy` pipe without having an associated endpoint. - `Backend` is when KrakenD connects to your services. It’s the stage where you can do more operations as it manages the connections, drivers, limits, etc., going in and out of your upstream services. - `Workflow` is when a backend triggers a nested proxy inside its execution. A way to see it is as if you had an internal endpoint without needing to publish it as an HTTP route. Enterprise Once the request reaches the upstream services, the response follows the same path back of the pipe until it gets to the user. The same parts of the pipe defined above will let you modify, validate, discard, etc., responses through each stage’s components. ## Components flow The following diagram is the same as the one shown above but includes the **primary components** (not all) involved in the different pipes. We understand this graph it is not for the faint-hearted, but it will help you answer the specific question of what is loaded before and after: ![Sequence of KrakenD components](/images/documentation/diagrams/components-sequence-master.mmd.svg) --- # [Backend-for-Frontend (BFF) Design Pattern](https://www.krakend.io/docs/design/backend-for-frontend/) One of the main differences between KrakenD and any other API gateway in the market is that all your endpoints can offer a ready-to-use Backend For Frontend experience by simply declaring the backend sources and the fields your client needs. But why and when do you need to use a BFF? ## The problem a BFF solves When developers of APIs create new endpoints, whether in a microservices pattern or a monolith, using an intermediate gateway or directly connecting to backends, they must provide **general-purpose responses**. The responses from the backends, or upstream services, must cover all intended cases around the domain they belong to. For instance, as a backend developer, if you create a `/user` endpoint, it should return **all the user data for the different consumers** of the API. Of course, some consumers will use more significant portions of the data than others, but generally speaking, most clients will be parsing unnecessary data and wasting bandwidth: ![Problem 1 of API usage](/images/documentation/diagrams/backend-for-frontend-problem.mmd.svg) Another problem is that the clients consuming this data, like a desktop web UI, a mobile app, a TV app, etc., have very different capabilities and needs. Even data consumption between Android and iOS native apps is different! In addition to the problems above, each client needs to rely on **multiple domains** to complete a single use case. For instance, on the front page screen of an e-commerce application (whether it’s a mobile app or web page), you might need to simultaneously consume data from the `user`, `catalog`, `cart`, `pricing`, and `promotions` services. ![Problem with multiple API usage](/images/documentation/diagrams/backend-for-frontend-problem-2.mmd.svg) As you can see in the diagram above, the client must deal with four different HTTP connections, wait for the fat responses coming back by a slow channel (network), parse all the data, and pick only the attributes needed to render the page. So why wouldn’t you give what the client needs in the first place, omitting unnecessary data? Finally, upstream services are developed simultaneously with the UI in most cases, adding new capacities. As a result, the backend becomes a general-purpose service, serving the requirements of all the desktop, mobile, or IoT interfaces. Desktop and mobile interfaces usually compete in requirements, and the backends keep growing to fit both types of usage. Every frontend team will work in different data views to display the content, and the backend team becomes a bottleneck as it must fulfill the frontend team’s needs. In many cases, the same type of information (or view) will require different response formats and response errors for each consumer, making your backend developers spend a lot of time developing this logic. And many meetings and time making the different frontend dev teams get on the contract agreement! ## How the BFF works KrakenD implements the BFF pattern with aggregation, allowing your frontend teams to define precisely the information you want to retrieve from the different services and how to deal with the errors. ![BFF example](/images/documentation/diagrams/backend-for-frontend.mmd.svg) The client receives: - Exactly the data it needs, and no more. - The information for a use case is taken from a single call - Aggregated information containing all involved services - Optionally, stub static data when there’s missing information - Separation of concerns - Decoupling: Do not have to worry very much about the backend changing or evolving ### Configuring a BFF To add data from multiple services, you only need to declare [new backends](/docs/backends/) when creating endpoints. The backends can be REST services, but you can use services from different places, like lambdas, queues, gRPC, etc. Then you can choose which attributes you want to include in the response using [filtering strategies](/docs/backends/data-manipulation/#filtering), and if needed, add manipulation. In most cases, you will want each service [grouped](/docs/backends/data-manipulation/#grouping) in a new object, so you have each service in a different entity. The gateway will automatically aggregate all the data for you and let the client know using the `X-KrakenD-Completed` header when all the information has been retrieved. Additionally, you can return [stub data when there are errors](/docs/endpoints/static-proxy/). For instance, the following use case of a front page, would retrieve only a few fields from the two services it consume: ```json { "endpoints": [ { "endpoint": "/frontpage", "method": "GET", "backend": [ { "url_pattern": "/user/{id_user}", "method": "GET", "host": ["http://user-service"], "allow": ["id_user", "name", "email"], "group": "user" }, { "url_pattern": "/catalog", "method": "GET", "host": ["http://catalog-service"], "allow": ["featured"] } ] } ] } ``` The response of the `/frontpage` endpoint could look like this: ```json { "user": { "id_user": 123, "name": "John Doe", "email": "john@doe.com" }, "featured": { "today": {}, "week": {} } } ``` ## When to use this pattern The Backend for Frontend pattern in KrakenD allows you to have one API per user interface or different endpoints per user interface. Then you can define how the data is returned to each interface, both when there are errors or in the happy path, without needing to get on an agreement with other frontend teams. Use the BFF when you want or need: - To call multiple backend services to serve a use case - Less chatty responses and retrieving what is needed only - Avoid over-processing data - Transform or manipulate data before returning it to the client - Optimize the bandwidth usage and network calls number - Implement a Facade pattern - Have a front app with simpler code and less maintenance - Reduce backend development overhead - Save time getting to team agreements KrakenD allows powerful transformations and business logic, but we recommend leaving the gateway to do basic gluing and avoid having complex logic complicated to maintain. --- # [Zero Trust Design: Secure API Architecture](https://www.krakend.io/docs/design/zero-trust/) Real-world API deployments suffer daily attacks, even if you don’t notice them. It doesn’t matter if you have your infrastructure behind a delimited perimeter. Where there is a point of access, there is malicious activity. The Zero Trust security is a software architecture design choice to **deny by default unless specifically allowed**. In zero trust, you verify all requests, regardless of origin, and prove you can trust them. KrakenD both **evaluates** and **enforces** rules. ## The pillars of zero-trust 1. **Explicit declaration**: Do not let pass parameters, headers, query strings, etc., unless you have explicitly declared it. Don’t assume any open defaults; write any behavior in the configuration. 2. **Principle of least authority (PoLA)**: Assign to your users only those privileges which are essential to perform its intended function. For example, a user account for creating deals in a sales department does not need to create invoices: hence, it has rights only to create deals, and any other privileges are blocked. 3. **Assume breach**: Users’ credentials might be stolen and servers compromised. Minimize the attacking surface by segmenting access, work only with end-to-end encryption, and visualize activity in the dashboards. ## Zero-trust assumptions KrakenD assumes the following behaviors when serving an API: - All endpoints protected by tokens are strictly checked - No negotiation of algorithms possible - Do not trust keys from insecure protocols (HTTP) - No possibility of using expired tokens or incorrectly signed - Rules, audience, issuers, claims and business logic can be enforced - No endpoints exposed unless explicitly declared. It provides two immediate benefits: - **No possible scan** of the upstream services - **No zombie APIs** as all routes are typed. No possibility of leaving unnoticed endpoints behind - No header, query string, or cookie forwarding unless explicitly declared which ones: - Injections are limited because only what is explicitly declared is the attack surface - TLS defaults to TLS v1.3, the most secure, and rejects older versions - Multilayered rate limit (by service, by the user, by upstream) - CORS configuration denies API access from untrusted origins even when credentials are OK. - All software is designed with OWASP recommendations in mind to help mitigate risks. Including but not limited to the **Top 10**: - Broken Access Control - Cryptographic Failures - Injection - Insecure Design - Security Misconfiguration - Vulnerable and Outdated Components - Identification and Authentication Failures - Software and Data Integrity Failures - Security Logging and Monitoring Failures - Server-Side Request Forgery --- # [Lura vs. KrakenD](https://www.krakend.io/docs/overview/lura-vs-krakend/) If you had a quick look at our git repositories or the documentation, you might be confused at first, as there is something called the **Lura Project** and also **KrakenD**. ## TL;DR; Difference between Lura, KrakenD, and Enterprise - [**Lura**](https://luraproject.org) is the KrakenD’s engine. Formerly known as “**KrakenD framework**” until we [donated it to The Linux Foundation on 2021](/blog/krakend-framework-joins-the-linux-foundation/). It is not a product itself but a toolkit/set of libraries to build API gateways. KrakenD founders are in the steering committee of Lura at The Linux Foundation. - **KrakenD** is driven by the company KRAKEND S.L.U, and is our [open-source API Gateway](/open-source/) ready to use. - **KrakenD Enterprise** is our commercial version with added functionality and includes services to businesses. ### Lura Project The [Lura Project](https://luraproject.org) is our original *KrakenD framework* that we [donated to The Linux Foundation on 2021](/blog/krakend-framework-joins-the-linux-foundation/). Previously it lived from 2006 to 2021 under the KrakenD’s team organization ([@krakend\_io](https://twitter.com/krakend_io)) until it became a [Linux Foundation](https://linuxfoundation.org/) project. KrakenD team continues to be part of the steering committee. The mission of the Lura Project is to offer an extendable, simple and stateless high-performance API Gateway **framework** designed for both cloud-native and on-prem setups. It provides components for assembling your API Gateway. It can be used in its entirety or just importing it as Go libraries to take only some of the functionality it brings. Lura focuses on providing the core functionality that a pure API gateway needs. It keeps it clean and extensible so that you can create your custom gateway without any trouble. KrakenD is the primary representation of Lura’s possibilities. [Website](https://luraproject.org "Website") [Source code](https://github.com/luraproject/lura "Source code") ### KrakenD API gateway `KrakenD` ([repo](https://github.com/krakend/krakend-ce)) is our ready-to-use API gateway, assembled the way we think it delivers more value to the general audience. KrakenD uses the Lura Project in its core and extends its functionality by adding in the final binary multiple middlewares [contributions](https://github.com/krakend/krakend-contrib) we thought an API Gateway should have. KrakenD adds to Lura more functionality like logging, service discovery, developer tools, metrics, circuit breaker, rate limiting, OAuth, security, and other exciting stuff. [Download](/download/ "Download") [Source code](https://github.com/krakend/krakend-ce "Source code") ### KrakenD Enterprise Customers of the [KrakenD Enterprise](/enterprise/) package enjoy the development, consultancy, support, and training services offered by the very same KrakenD creators (and Lura steering committee). As per the software, KrakenD Enterprise offers more functionality than the open source edition, including the integration with additional proprietary observability solutions, wildcards, GeoIP, OpenAPI, Postman collections and more. There is also more tooling around KrakenD to increase productivity and enable working with KrakenD in large groups of developers. **Our commitment to open-source is in the center of our business**, and this is why our Enterprise solution is built on top of the open-source version. The Enterprise version uses the same OSS binary and extends it with a great variety of pluggable solutions. We want to make sure that both enterprise and community users have the excellent quality and reliability they are used to. [Learn more about Enterprise](/enterprise/) --- # [Frequently Asked Questions (FAQ) - KrakenD API Gateway](https://www.krakend.io/docs/faq/) ## I am getting a `200` status when the backend returns a `201` E.g: ``` yyyy/mm/dd - 10:31:27 | 200 | 1.134431ms | ::1 | POST /users ``` ### Explanation The gateway will default sending an HTTP status 200 if the backend returns a 200 or a 201. If you want to return the original `201` you must use [`no-op` encoding](/docs/endpoints/no-op/). ## Why is a 204 considered invalid? When my backend returns a `204 No Content` code, the gateway returns a 500 error. ### Explanation KrakenD can run either as a full API gateway or as a transparent proxy using [no-op encoding](/docs/endpoints/no-op/). When set as a proxy, Krakend faithfully returns backend headers, status codes (including 204), and response bodies, but most gateway features like circuit breakers are inactive since Krakend does not modify or interpret the response. The recommended mode is full gateway operation, which unlocks response composition, traffic control, security layers, and more. For many of these features to work correctly, **Krakend expects responses with non-empty bodies** from backends. A 204 status by definition means **no content**, so Krakend sees this as invalid when trying to compose or aggregate responses and returns an error. Because of this, the 204 status is considered invalid in contexts where Krakend needs a payload to process. It’s not about 204 being an HTTP-invalid code, but about Krakend’s internal handling requirements. The no-op encoding mode was introduced precisely to handle these edge cases, where you want Krakend to behave purely as a proxy without response processing. If you want features like the circuit breaker on responses that can be empty, you’ll need to adjust the backend to avoid 204 responses in those routes (instead returning something like a minimal JSON body with a 200 status) or configure those endpoints to use no-op encoding. ## I am getting a `500` status when the backend returns anything but `200`, `201` or redirects E.g: ``` yyyy/mm/dd - 10:31:37 | 500 | 1.007191ms | ::1 | POST /users_ko ``` ### Explanation The gateway will default send a 500 HTTP status code if the backend returns any status above 400. You can [change this strategy](/docs/backends/detailed-errors/). ## I am seeing frequent `503` errors in the logs E.g: ``` yyyy/mm/dd - 18:01:18 | 200 | 5.352143ms | ::1 | GET /frontpage yyyy/mm/dd - 18:01:18 | 503 | 5.662µs | ::1 | GET /frontpage yyyy/mm/dd - 18:01:18 | 503 | 5.662µs | ::1 | GET /frontpage ``` The `max_rate` setting defines the maximum number of requests allowed in a single second to an endpoint or backend. When this number is reached, subsequent connections are rejected with a `503` error. This limitation is optional and is usually set to avoid hammering your own backends and compromising their stability. ### Solution Increase the `max_rate` number or disable it (`max_rate = 0`). This setting can be set globally for all the endpoints, or overridden individually per endpoint. Remember: failing fast is always better than overloading your infrastructure and degrading the quality of your entire services. ## I have empty responses The main reasons for having responses are: - **Timeout** when connecting the backend. The KrakenD service will cut the connection and will return an empty response if the backend does not respond in the time you set through the `timeout` variable. This variable is usually written in a magnitude of **milliseconds**. - **Invalid JSON/XML**. When the backend received a malformed object as response and could not decode it. See the solutions below. ### Solution to cuts by timeout When there is a timeout, you’ll see the `context deadline exceeded` in the log, which means only one thing: KrakenD couldn’t get the info on time (because of a network problem or backend slowness). ``` Error #01: context deadline exceeded ``` Whenever possible, add caching layers in your backends, scale the infrastructure, etc., so backends answer on time. **Increasing the `timeout` variable should always be your last option**. If your backends are not able to respond in a short time, think that when you increment the timeout, what you do is block connections waiting for the backend. Memory consumption will increase, and the number of connections you can open is finite. In a gateway, your focus should be freeing the connections as soon as possible. Values above `2000ms` are not recommended. There are other times when KrakenD cannot reach the host due to a networking issue. ### Solution to invalid responses. Make sure your backend sources return valid JSON/XML/… data. Try any online service to check the validity and format of the returned content. If the response of your API is a collection, e.g., response comes inside brackets `[]`, then make sure to mark the option `Treat the response as a collection, not an object.` in the form. ## I’ve upgraded to v2.x, and I start to see `context canceled` errors You start to see in the log `context canceled` errors, but before the upgrade, you didn’t. ### Solution The error means that the client disconnected while consuming the content. KrakenD can only do something if a client cuts the connection in the middle of the transmission: notify it. You are seeing these errors now because the latest versions of KrakenD refactored how the router logging worked. Before this refactor, errors like this were showing in the access log (which was an incorrect approach) instead of the application log, where they belong. The effect is that you are seeing them for the first time, but the disconnections were always there. If you check the older access logs in the stdout (not the application log), you will still find them. ## Reserved endpoints The following endpoints are reserved, and you cannot use them (unless you disable them or rename them): - `/__debug/` (disabled by default) - `/__echo/` (disabled by default) - `/__stats/` (disabled by default) - `/__health/` (can be renamed or disabled) ## I have found a vulnerability If you think you have found a security problem, please [report us the vulnerability](/security-policy/) --- # [Switching to Community or Enterprise](https://www.krakend.io/docs/faq/switch-versions/) If you are considering upgrading to KrakenD Enterprise or downgrading to KrakenD Community, these are the things you should have in mind. ## Downgrade to Community If you are a KrakenD Enterprise Edition user and you want to switch to the Community Edition, these are the guidelines you should follow to keep your installation working. - Obtain the [Community Edition](/download/) and replace it by the Enterprise one - Make the configuration migration as described below - Start the server with the modified (if necessary) configuration ### Configuration migration You can run a configuration file designed for the KrakenD Enterprise in a Community Edition straight away, except if you have one of the following options that you should remove and will make your server panic during startup: - [Dynamic routing](/docs/enterprise/endpoints/dynamic-routing/). - [Endpoint with wildcards](/docs/enterprise/endpoints/wildcard/) (`/*`) If you have any of the components above, remove them from the configuration. The other [Enterprise-only features](/features/) are ignored and not loaded into the server, so whatever you leave or remove them from the configuration makes no difference to the server. Still, you should delete them to avoid confusion when reviewing the configuration later, as none of these actually work. Enterprise features are disabled The Community Edition **ignores unrecognized features from the Enterprise Edition** and does not load any of its behavior. If you used critical components like [Security Policies](/docs/enterprise/security-policies/), [API Keys](/docs/enterprise/authentication/api-keys/), [OpenAPI](/docs/enterprise/endpoints/openapi/), [WebSockets](/docs/enterprise/websockets/), [gRPC](/docs/enterprise/backends/grpc/#how-grpc-works), etc. you must know all these are gone even if they are in the configuration, and that you are substantially changing how the API used to operate. ## Upgrade to Enterprise To upgrade a Community configuration to an Enterprise one, **no configuration changes are needed**. The only steps required are: - Obtain [KrakenD Enterprise](/docs/enterprise/overview/installing/) - Add the [`LICENSE` file](/docs/enterprise/overview/license-file/) to your project - Start the new server If you use the same version, everything you configured on the Community Edition works in the Enterprise Edition.