How to Integrate Request/Response Plugins to Krakend API Gateway

Damindu Lakmal
5 min readApr 3, 2023

--

krakend overview

KrakenD is a high performance API Gateway. That can communicate between the client and all the source servers while adding a new layer that removes all the complexity to the clients, providing them only the information that the API response needs.

KrakenD acts as an aggregator of many sources, request can be route one to many endpoint. 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

KrakenD is a golang base project that can be extended through our usecase. Let’s take look at languages that are supported to krakenD,

Install

Recommended Way

Clone git-hub repo then follow the steps in the readme to build the krakend binary file. That is easy to match underline requirement of the custom plugins. In here v2.2.1 tag is used for future implementation.

Also you can install the binary file from here.

Easy Way

you can use docker image by using below command,

docker run -p 8080:8080 devopsfaith/krakend run --config /etc/krakend/krakend.json

docker image is using it’s own krakend.json file so if you need to use your own json file,

docker run -p 8080:8080 -v $PWD:/etc/krakend/ devopsfaith/krakend run --config /etc/krakend/krakend.json

Recommended to install krakenD through github repo to your system that will be easy to go on next steps.

Custom Plugins

In here, we will go through custom plugin implementation by using golang. Let’s check the steps which need to complete our task,

  • Choose right interface : select the exact matching inteface
  • Compatible Go version : same go version
  • Same architecture/platform: KrakenD and plugin file should be same platform
  • Same shared library version

There are main three type of plugin injection such as,

  • HTTP server plugin
  • HTTP client plugin
  • Request and response modifier plugin

HTTP Server Plugin

Plugin is belong to router layer and request can be modifies before processing krakenD or can modify the final response of the API. This plugin has ability to implement custom monetization, tracking, tenant control, protocol conversion and heavy modifications.

Note: HTTP server plugin can be chained

HTTP Client Plugin

Plugin is belong to proxy layer that can intercept, transform and manipulation before reach to backend services.

Note: HTTP client plugin can’t be chained

Request and Response Modifier Plugin

Plugin is allowed you to code your own business logic directly on request response in a simple and extesible way. This modifier is placed after router layer and before the request executor.

This plugin is not middleware but modifiers can call sequentially from new middleware. Request modifier can only inspect and modify request and response modifier only for responses.

Request Modifier

  • Request validation and complex checks
  • Request manipulation
  • Request filtering
  • Request debugging

Response Modifier

  • Response validation and complex checks
  • Response manipulation
  • Response filtering
  • Response debugging

Example

In here, krakenD binary is built from git-repo because it will compatible when you build your plugin on same machine.

Write Response Plugin

Let’s start new go project by executing command,

go mod init response-plugin

Add main.go file with below code lines,

package main

import (
"errors"
"fmt"
"io"
)

func main() {}

func init() {
fmt.Println(string(ModifierRegisterer), "loaded!!!")
}

var extraHeader string = "extra-header"

var ModifierRegisterer = registerer("response-plugin")

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), r.responseDump, false, true)
fmt.Println(string(r), " registered!")
}

// 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
}

type responseWrapper struct {
data map[string]interface{}
isComplete bool
metadata metadataWrapper
io io.Reader
}

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 }

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 }

var unkownTypeErr = errors.New("unknow request type")

func (r registerer) responseDump(
cfg map[string]interface{},
) func(interface{}) (interface{}, error) {
config := cfg[string(r)]
var extraHeaderConfig string
if config != nil {
if cf, ok := config.(map[string]interface{}); ok {
extraHeaderConfig = fmt.Sprintf(`%v`, cf[extraHeader])
}
}
fmt.Println("response dumper injected!!!")
return func(input interface{}) (interface{}, error) {
fmt.Printf("input %v", input)
fmt.Println("")
resp, ok := input.(ResponseWrapper)
if !ok {
return nil, unkownTypeErr
}

resTemp := responseWrapper{}
resTemp.data = resp.Data()
resTemp.isComplete = resp.IsComplete()
resTemp.io = resp.Io()
resTemp.metadata.headers = make(map[string][]string)
for k, v := range resp.Headers() {
resTemp.metadata.headers[k] = v
}
if extraHeaderConfig != "" {
resTemp.metadata.headers[extraHeader] = []string{extraHeaderConfig}
}

return resTemp, nil
}
}

more information describe in the official doc.

Further more responseWrapper struct is define to customise the header response to include the extra-header.

plugin .so file can be build,

go build -buildmode=plugin -o res-plugin.so .

Write KrakenD File

KrakenD server needs to start and operate is a single configuration file such as krakend.json.

In our use-case, we will communicate to github API call and include the new header as extra-header. Above plugin is configure as reponse-plugin and extra-header field is pass through the configuration.

{
"version": 3,
"port": 8080,
"name": "KrakenD response modifier demo",
"host": ["https://api.github.com"],
"plugin": {
"pattern":".so",
"folder": "./"
},
"endpoints": [
{
"endpoint": "/github/orgs/{org}",
"backend":[
{
"url_pattern": "/orgs/{org}",
"allow": [
"avatar_url",
"blog",
"followers"
],
"group": "org",
"extra_config":{
"plugin/req-resp-modifier":{
"name":["response-plugin"],
"response-plugin":{
"extra-header":"test"
}
}
}
}
],
"extra_config":{
"plugin/req-resp-modifier":{
"name": [
"response-plugin"
],
"response-plugin":{
"extra-header":"test"
}
}
}
}
]
}

Execute the already built krakend binary with current configuration,

./krakend run -c krakend.json   
successfully start the krakend

sample code can be found in here.

Test Endpoint

Let’s excute curl request to GitHub api,

curl --location 'http://localhost:8080/github/orgs/github'

API response should include the extra-header in header section,

header response

Summary

This is only an overview of krakenD cutom plugins. I’d recommended to use documentation of KrakenD. Understand configuration of krakend file, endpoints, backends, plugins, operators and underline architecture.

Thank you for reading this article. I hope you enjoyed it!

--

--