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

> Learn to develop and deploy custom request response filters for data transformation in INFINI Gateway. Transform data at the edge with ease.

- Repository: [INFINI Labs/gateway](https://github.com/infinilabs/gateway)
- Tags: how-to-guide
- Published: 2026-03-04

---

**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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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.

```go
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.

```go
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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/gateway.yml) file (or any loaded configuration source) defines flows under the `flows` key, specifying filters as an ordered array.

```yaml
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`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/custom/header.go)) to maintain consistency with built-in filters like [`proxy/filters/transform/set_header.go`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/main/main.go) parses the configuration and instantiates your filter. If the gateway runs with hot-reloading enabled, editing [`gateway.yml`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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.