Custom Go Modules for GraphQL Router | Cosmo by WunderGraph - WunderGraph

GraphQL Router Middleware in Go

Add custom logic to the router in Go (auth, caching, rate limiting) without running a separate proxy or forking the codebase.

Same binary. No extra hop. Every Go tool works on it.

The problem

Why teams bolt on proxies, scripting, or forks

Federated GraphQL needs gateway logic that talks to the rest of your platform. That rarely fits in a single hop, a sandboxed script, or a one-off fork.

External proxies add a network hop

A sidecar or Envoy filter in front of the router handles the custom logic, at the cost of an extra hop, another process to run, and metrics that live in two places.

Scripting languages limit what you can reach for

Lua, JavaScript, or sandboxed WASM filters cover basic shape-shifting, but they can't call your company's Go client library, your internal gRPC service, or the cache SDK your platform team already ships.

Forked gateways drift

Copying the router source to add a few hooks means inheriting every merge conflict forever. Upstream fixes stop being free.

Our solution

Add custom gateway logic without running a second process

Custom modules are Go packages linked into the router binary. Your code runs in the same process as routing and federation, so you can reuse internal clients and observability without adding another network hop or maintaining a fork.

Each interface hooks into a specific point

  1. One module can implement several interfaces at once, and multiple modules can coexist; set priority when their relative order matters.
  2. RouterOnRequestHandler runs as early as possible, before built-in auth and parsing.
  3. RouterMiddlewareHandler runs after the operation is parsed, with access to the GraphQL operation and query plan stats.
  4. EnginePreOriginHandler and EnginePostOriginHandler wrap each subgraph fetch and response.
  5. Provisioner and Cleaner run at router start and shutdown for module lifecycle.
  6. At request time, handlers fire in-process in the same goroutine as the request, with access to the full request context.

Custom modules

Before & After

Before Cosmo With Cosmo
External proxy layer for custom logic In-process Go module, zero network hop
Scripting language with limited reach Full Go ecosystem and compiled performance
A single "on request" hook, workarounds for the rest Six distinct hooks for precise lifecycle control
Hard to test: proxy plus router plus test harness Plain Go: go test, pprof, delve all apply

The six interfaces

Interface When it runs
RouterOnRequestHandler Earliest intercept, before auth and parsing
RouterMiddlewareHandler After the operation is known, with query plan stats
EnginePreOriginHandler Before each subgraph fetch
EnginePostOriginHandler After each subgraph response
Provisioner Module setup on router start
Cleaner Module teardown on router shutdown

What a module can see

How custom Go modules work

Implement interfaces

Pick the hook point(s) needed. A module can implement more than one interface.

Config lives in router YAML, not scattered env vars.

Register the module

Register with the router. Set priority if order relative to other modules matters. Map YAML config values to struct fields with tags.

Build the router binary

Compile the router with your modules included. Output is a single binary.

Handlers fire in-process

At runtime, each hook fires at its point in the request lifecycle in the same goroutine as the request, with access to the full request context.

Use cases

Patterns teams ship first

Auth, cache, cost controls, and headers at the gateway, without subgraph changes.

Custom authentication

RouterOnRequestHandler runs before the router's built-in auth. Extract credentials, validate against the internal system, either let the request continue or return an early error.

Gateway-level caching

EnginePreOriginHandler checks cache using the operation hash and query plan stats; on a hit it returns early and skips the subgraph fetch. EnginePostOriginHandler populates the cache after a miss.

Per-tenant rate limiting

RouterMiddlewareHandler reads operation details and QueryPlanStats, estimates cost based on subgraph fetch count, and enforces per-tenant budgets.

Destination-aware header transformation

EnginePreOriginHandler reads ctx.ActiveSubgraph() and adds or rewrites headers on the outgoing request. No subgraph code changes.

Extend the router in Go today

Six hooks, one binary, zero extra hop.