Record and Persist API call details in KrakenD for API monetization

396 views Asked by At

We are trying out KrakenD as a primary API gateway for our backend services. The plugins available are only for response manipulation, we want to go a step ahead and start recording all the API calls and persist them in a database. I was checking the example of having a custom HTTPStatusHandler, but that mainly caters to handling the status codes and as a best case we can only return the status codes from our backends as it is. Is there any working example of this scenario that we are trying? We are already using a custom middleware to grant API access based on our role based access rules.

Other commercial solutions offer API monetization out of the box, but we want to stick with KrakenD for performance reasons.

1

There are 1 answers

0
alo On

KrakenD plugins are not only for response manipulation, but a much more powerful solution. There are 4 types of plugins in krakend, what you want to do I think that fits best as an HTTP SERVER plugin.

This type of plugin intercepts all incoming traffic and you can record it in a Redis database.

I have improvised a main.go of the idea, but you should check the plugin guide in the documentation for more details.

Hope this helps illustrating the idea

package main

import (
    "context"
    "errors"
    "fmt"
    "html"
    "net/http"
)

// HandlerRegisterer is the symbol the plugin loader will try to load. It must implement the Registerer interface
var HandlerRegisterer = registerer("krakend-api-monetization")

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", HandlerRegisterer))
}

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) {
    // check the passed configuration and initialize the plugin
    name, ok := extra["name"].([]interface{})
    if !ok {
        return nil, errors.New("wrong config")
    }
    if v, ok := name[0].(string); !ok || v != 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-server":{
               "name":["krakend-api-monetization"],
               "krakend-api-monetization":{
                   "redis_host": "localhost:6379"
               }
           }
       }
    */

    // The config variable contains all the keys you hace defined in the configuration:
    config, _ := extra["krakend-api-monetization"].(map[string]interface{})

    // The plugin will save activity in this host:
    redis_host, _ := config["redis_host"].(string)
    logger.Debug(fmt.Sprintf("The plugin is now storing on %s", redis_host))

    // 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) {

    // TODO: Save in Redis the request
rdb := redis.NewClient(&redis.Options{
        Addr:     redis_host,
        Password: "", // no password set
        DB:       0,  // use default DB
    })
// etc...
        logger.Debug("Request saved:", html.EscapeString(req.URL.Path))
    }), nil
}

func main() {}

type Logger interface {
    Debug(v ...interface{})
    Info(v ...interface{})
    Warning(v ...interface{})
    Error(v ...interface{})
    Critical(v ...interface{})
    Fatal(v ...interface{})
}