core.RouterOnRequestHandler
Implements a custom middleware that runs before most internal middleware in the router for each client request. Most importantly this is called before tracing and authentication logic for each request. Use case: Custom Authentication Logic, Custom Tracing Logic, Early return, Request Validation.core.RouterMiddlewareHandler
Implements a custom middleware on the router. The middleware is called for every client request. It allows you to modify the request before it is processed by the GraphQL engine. Use case: Logging, Caching, Early return, Request Validation, Header manipulation.core.EnginePreOriginHandler
Implements a custom handler that is executed before the request is sent to the subgraph. This handler is called for every subgraph request. Use case: Logging, Header manipulation.core.EnginePostOriginHandler
Implement a custom handler executed after the request to the subgraph but before the response is passed to the GraphQL engine. This handler is called for every subgraph response. Use cases: Logging, Caching.core.Provisioner
Implements a Module lifecycle hook that is executed when the module is instantiated. Use it to prepare your module and validate the configuration.core.Cleaner
Implements a Module lifecycle hook that is executed after the server is shutdown. Use it to close connections gracefully or for any other cleanup.
*OriginHandler
handlers are called concurrently when your GraphQL operation
results in multiple subgraph requests. Due to that circumstance, you should
handle the initial router request/response objects ctx.Request()
and
ctx.ResponseWriter()
as read-only objects. Any modification without
protecting them from concurrent writes, e.g., by a mutex, results in a race
condition.RouterOnRequestHander
is only available since Router
0.188.0Learn by Example
If you want to see how to develop your own modules, check out our examples repository. It contains examples to build and upgrade your own router.Router Examples
Explore production-ready examples of custom modules and router setups.
Concepts
The following sections will guide you through important concepts and best practices for developing custom modules.Priority Loading of Modules
When loading multiple modules, the order is not inherently guaranteed. To ensure a specific loading order, you can use thePriority
option. Modules with lower priority numbers are loaded first. Below is an example configuration:
myModule
has a priority of 1, meaning it will be loaded before modules with higher priority values.
Access the GraphQL operation
During the client request, you have access to the actual GraphQL operation. Simply call:Access query plan information
In Middleware, you can access some stats about the query plan that will be used for the operation.- Total count of subgraph fetches
- A map of subgraph names to the number of times they will be fetched
Access Request Context
In every handler, you can add/remove, or modify response headers. We also provide a convenient, safe way to share data across handlers.Access Subgraph through Request Context
Through the request context you can retrieve the active subgraph for the current request. This can be done in the OnOriginRequest hook as show belowA more complex example including tests is accessible at
https://github.com/wundergraph/cosmo/tree/main/router/cmd/custom
Access authentication information
Authentication information, including claims and the provider that authenticated the request, can be accessed throughcore.RequestContext.Authentication()
For a full example, please check out
https://github.com/wundergraph/cosmo/tree/main/router/cmd/custom-jwt
Change Authentication Information
Above, we showed how to access Authentication information. There can be cases where your authentication could depend partly on another system, and you want to set elements for use with other directives, such as @requiresScopes. In order to do that, you can useauth.SetScopes()
to manually change the authenticationβs scopes.
.SetScopes()
overwrites the existing scopes. If youβd like to append/preserve the built-in scopes, you can first use auth.Claims()
to get the existing scopes, and incorporate that into the updates scopes.
ctx.SetAuthenticationScopes(scopes []string)
that, if the Authentication is not set, it will initialize it with an empty object and set the scopes. If the authentication is already set, it will just override the scopes.
SetScopes()
.
Do Changes Before Authentication Occurs
In the previous section, theMiddleware
runs after the authentication of the request. However, sometimes you might want to run authentication related logic before the authentication actually happens. For example, letβs say that your client sends the Authorization
header without the Bearer
part in the header and you want to add Bearer
to the header, for this you can use the RouterOnRequestHandler
hook.
Return GraphQL conform errors
Please always usecore.WriteResponseError
to return an error. It ensures that the request is properly tracked for tracing and metrics.
Request Handler lifecycle
The current module handler allow to intercept and modify request / response subgraphs.Module Configuration
If you need to pass external configuration values to your module, you can do so easily by annotating the fields in your module struct. Fields must start with an uppercase letter to make them accessible.Example Config file
Based on the example above we will populate the fieldValue
with the value 1
. You can also validate your config in the core.Provisioner
handler.
config.yaml