How to Develop and Deploy Custom Request/Response Filters for Data Transformation in INFINI Gateway

INFINI Gateway enables data transformation at the edge by implementing the pipeline.Filter interface, registering the component via pipeline.RegisterFilterPluginWithConfigMetadata, and declaring it in the gateway.yml flow configuration.

Developing custom request/response filters for data transformation in INFINI Gateway requires implementing a Go interface that intercepts HTTP traffic within the proxy's pipeline architecture. The gateway processes every request through an ordered sequence of filters defined in common/flow.go, allowing you to modify headers, rewrite bodies, or enrich context data before the request reaches the upstream backend. Because filters are compiled into the binary as native Go code, they execute with minimal overhead while maintaining full access to the fasthttp request context.

Understanding the Filter Architecture

INFINI Gateway organizes traffic processing into flows, each consisting of an ordered slice of filters. At runtime, GetFlow (defined in common/flow.go) loads the flow definition and assembles a FilterFlow struct containing the filter chain【link-filterflow】.

The FilterFlow.Process method iterates over each filter, invoking the Filter(ctx *fasthttp.RequestCtx) method while respecting the request's continuation flags【link-process】. This architecture ensures that filters execute sequentially unless explicitly halted or skipped.

Key structural components include:

  • pipeline.Filter interface – The contract requiring Name() string and Filter(ctx *fasthttp.RequestCtx) methods
  • FilterConfig – Configuration model defined in common/entry.go that maps YAML parameters to filter instances【link-filterconfig】
  • Registration system – Global registry populated via pipeline.RegisterFilterPluginWithConfigMetadata, as demonstrated in proxy/output/queue/queue.go【link-queue-register】

Implementing the pipeline.Filter Interface

Custom filters must satisfy the pipeline.Filter interface defined in the framework core. Create a Go struct that holds configuration fields and implements the required methods.

package custom

import (
	"infini.sh/framework/core/config"
	"infini.sh/framework/core/pipeline"
	"infini.sh/framework/lib/fasthttp"
	"log"
)

// HeaderFilter adds a static header to the response.
type HeaderFilter struct {
	HeaderName  string `config:"header_name"`  // Maps to YAML parameter
	HeaderValue string `config:"header_value"` // Maps to YAML parameter
}

// Name satisfies the pipeline.Filter interface.
func (f *HeaderFilter) Name() string { return "custom_header" }

// Filter implements the transformation logic.
func (f *HeaderFilter) Filter(ctx *fasthttp.RequestCtx) {
	if f.HeaderName == "" {
		log.Printf("custom_header filter: header_name not set")
		return
	}
	ctx.Response.Header.Add(f.HeaderName, f.HeaderValue)
}

The Filter method receives a pointer to fasthttp.RequestCtx, providing full access to request headers, body, response objects, and custom context values. Because this executes on the hot path, avoid blocking operations; use asynchronous clients for external service calls.

Registering Your Custom Filter

After implementing the interface, register the filter with the pipeline registry so the gateway can instantiate it from configuration. Place the registration in an init() function within the same package.

func init() {
	// RegisterFilterPluginWithConfigMetadata registers the constructor and
	// extracts configuration metadata from the empty instance.
	pipeline.RegisterFilterPluginWithConfigMetadata(
		"custom_header",                    // Name referenced in gateway.yml
		NewHeaderFilter,                    // Constructor function
		&HeaderFilter{},                    // Empty instance for config parsing
	)
}

// NewHeaderFilter constructs a HeaderFilter from the generic config.
func NewHeaderFilter(c *config.Config) (pipeline.Filter, error) {
	f := &HeaderFilter{}
	if err := c.Unpack(&f); err != nil {
		return nil, err
	}
	return f, nil
}

The constructor pattern used here mirrors the implementation found in proxy/output/queue/queue.go, where built-in filters register themselves using the same API【link-queue-register】. The config.Config parameter contains the parameters map defined in your YAML configuration.

Configuring the Flow in gateway.yml

Declare your custom filter within a flow definition to activate it. The gateway.yml file (or any loaded configuration source) defines flows under the flows key, specifying filters as an ordered array.

flows:
  my_custom_flow:
    filter:
      - name: custom_header
        parameters:
          header_name: "X-Customer-ID"
          header_value: "12345"
      - name: logging                # Built-in filters chain seamlessly

When the gateway starts, the flow loader creates each filter instance via its registered constructor, passing the parameters map to NewHeaderFilter. The resulting FilterFlow processes requests through your custom logic before executing subsequent filters like the built-in logging handler.

Building and Deploying the Custom Filter

Deploying custom filters requires compiling the code into the gateway binary, as INFINI Gateway statically links all filter implementations.

  1. Place the source file – Add your Go package under proxy/filters (e.g., proxy/filters/custom/header.go) to maintain consistency with built-in filters like proxy/filters/transform/set_header.go.

  2. Recompile the binary – Execute make or go build ./... from the repository root. The initialization code automatically registers your filter during package import.

  3. Update configuration – Edit gateway.yml to reference the new filter name in your target flow.

  4. Restart or reload – Start the compiled binary. The flow loader in main/main.go parses the configuration and instantiates your filter. If the gateway runs with hot-reloading enabled, editing gateway.yml triggers a flow reload without restarting the process, provided the filter code is already compiled into the running binary.

Summary

Custom request/response filters for data transformation in INFINI Gateway extend the proxy's capabilities through compiled Go code:

  • Implement the pipeline.Filter interface with Name() and Filter(ctx *fasthttp.RequestCtx) methods
  • Register the filter using pipeline.RegisterFilterPluginWithConfigMetadata to enable YAML-based instantiation
  • Configure flows in gateway.yml using the filter name and parameter map defined in your struct tags
  • Deploy by recompiling the binary and restarting the service, leveraging hot-reload for configuration changes

This pattern allows you to inject headers, transform request bodies, route traffic based on custom logic, or integrate with external systems while maintaining the high-performance characteristics of the underlying fasthttp framework.

Frequently Asked Questions

What Go interface must a custom filter implement in INFINI Gateway?

A custom filter must implement the pipeline.Filter interface, which requires two methods: Name() string returning the filter identifier, and Filter(ctx *fasthttp.RequestCtx) containing the transformation logic. The Filter method receives the request context from the fasthttp library, allowing manipulation of headers, body, and response objects.

How does INFINI Gateway load and execute custom filters at runtime?

The gateway loads filters through the GetFlow function in common/flow.go, which constructs a FilterFlow containing an ordered slice of pipeline.Filter instances【link-filterflow】. When processing requests, FilterFlow.Process iterates over this slice, calling each filter's Filter method sequentially while checking continuation flags【link-process】. Filters execute in the order defined in the gateway.yml configuration file.

Can custom filters access configuration parameters from gateway.yml?

Yes, filters receive configuration through struct tags and the constructor pattern. Define configuration fields in your filter struct with `config:"parameter_name"` tags, then register the filter using pipeline.RegisterFilterPluginWithConfigMetadata. The constructor receives a *config.Config object containing the parameters map from gateway.yml, which you unpack into your struct using c.Unpack(&filterInstance).

Where should custom filter source files be placed in the repository?

Place custom filter source files within the proxy/filters directory to maintain alignment with the project's architecture. Built-in examples like proxy/filters/transform/set_header.go demonstrate the expected package structure. After adding your files, recompile the gateway binary using make or go build to statically link your filter into the executable.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →