Router Customizations
Extend your router with custom functionality
You can create customizations for the GraphOS Router or Apollo Router Core to add functionality that isn't available via built-in configuration options. For example, you can make an external call to fetch authentication data for each incoming request.
Customization types
The GraphOS Router supports the following customization types:
The Rhai scripting language lets you add functionality directly to your stock router binary by hooking into different phases of the router's request lifecycle.
External co-processing (Enterprise feature)
If your organization has a GraphOS Enterprise plan, you can write custom request-handling code in any language. This code can run in the same container as your router or separately.
The router calls your custom code via HTTP, passing it the details of each incoming client request.
The Apollo Router Core supports customization only through Rhai scripts.
Because Rhai scripts are easier to deploy, we recommend using them if they support your use case. Use external co-processing if your customization needs to do any of the following (which Rhai scripts don't support):
Read or write to disk
Make network requests
Use libraries from a particular language or framework
Customizations along the request lifecycle
Customizations intervene at specific points of the request lifecycle, depending on the task you want to perform. Each point is represented by a specific service with its own request and response objects.
Request lifecycle plugins
Each service can have a set of plugins. For requests, the router executes plugins before the service.
For responses, the router executes the plugins after the service.
Each request and response object contains a Context
object, which is carried throughout the entire process. Each request's Context
object is unique. You can use it to store plugin-specific information between the request and response or to communicate between different hook points. (A plugin can be called at multiple steps of the request lifecycle.)
Request and response buffering
- Modifying the router
- Creating a native Rust plugin
- Creating a custom binary
The router expects to execute on a stream of data. In order to work correctly and provide high performance, the following expectations must be met:
Request Path: No buffering before the end of the
router_service
processing stepResponse Path: No buffering
router_service
step is complete.Request Context
The router makes several values available in the request context, which is shared across stages of the processing pipeline.
apollo::apq::cache_hit
: present if the request used APQ, true if we got a cache hit for the query id, false otherwiseapollo::apq::registered
: true if the request registered a query in APQapollo::authentication::jwt_claims
: claims extracted from a JWT if present in the requestapollo::authorization::authenticated_required
: true if the query covers type of fields marked with@authenticated
apollo::authorization::required_policies
: if the query covers type of fields marked with@policy
, it contains a map ofpolicy name -> Option<bool>
. A coprocessor or rhai script can edit this map to marktrue
on authorization policies that succeed orfalse
on ones that failapollo::authorization::required_scopes
: if the query covers type of fields marked with@requiresScopes
, it contains the list of scopes used by those directive applicationsapollo::demand_control::actual_cost
: calculated cost of the responses returned by the subgraphs; populated by the demand control pluginapollo::demand_control::estimated_cost
: estimated cost of the requests to be sent to the subgraphs; populated by the demand control pluginapollo::demand_control::result
:COST_OK
if allowed, andCOST_TOO_EXPENSIVE
if rejected due to cost limits; populated by the demand control pluginapollo::demand_control::strategy
: the name of the cost calculation strategy used by the demand control pluginapollo::entity_cache::cached_keys_status
: a map of cache control statuses for cached entities, keyed by subgraph request id; populated by the entity caching plugin whenexpose_keys_in_context
is turned on in the router configurationapollo::expose_query_plan::enabled
: true if experimental query plan exposure is enabledapollo::expose_query_plan::formatted_plan
: query plan formatted as textapollo::expose_query_plan::plan
: contains the query plan serialized as JSON (editing it has no effect on execution)apollo::progressive_override::labels_to_override
: used in progressive override, list of labels for which we need an overrideapollo::progressive_override::unresolved_labels
: used in progressive override, contains the list of unresolved labelsapollo::supergraph::first_event
: false if the current response chunk is not the first response in the stream, nonexistent otherwiseapollo::supergraph::operation_id
: contains the usage reporting stats report keyapollo::supergraph::operation_kind
: can beQuery
,Mutation
orSubscription
apollo::supergraph::operation_name
: name of the operation (according to the query and the operation_name field in the request)apollo::telemetry::client_name
: client name extracted from the client name headerapollo::telemetry::client_version
: client version extracted from the client version headerapollo::telemetry::contains_graphql_error
: true if the response contains at least one errorapollo::telemetry::studio_exclude
: true if the current request's trace details should be excluded from Studioapollo::telemetry::subgraph_ftv1
: JSON-serialized trace data returned by the subgraph when FTV1 is enabled
Creating customizations
To learn how to hook into the various lifecycle stages, including examples customizations, refer to the Rhai scripts and external coprocessing docs.