apollo-router-plugin-creator
Apollo Router Plugin Creator
Create native Rust plugins for Apollo Router.
Request Lifecycle
┌────────┐ ┌────────────────┐ ┌────────────────────┐ ┌───────────────────┐ ┌─────────────────────┐
│ Client │ │ Router Service │ │ Supergraph Service │ │ Execution Service │ │ Subgraph Service(s) │
└────┬───┘ └────────┬───────┘ └──────────┬─────────┘ └─────────┬─────────┘ └──────────┬──────────┘
│ │ │ │ │
│ Sends request │ │ │ │
│──────────────────────────▶ │ │ │
│ │ │ │ │
│ │ Converts raw HTTP request to GraphQL/JSON request │ │ │
│ │──────────────────────────────────────────────────────▶ │ │
│ │ │ │ │
│ │ │ Initiates query plan execution │ │
│ │ │───────────────────────────────────▶ │
│ │ │ │ │
│ │ │ ┌par [Initiates sub-operation]───────┐
│ │ │ │ │ │ │
│ │ │ │ │ Initiates sub-operation │ │
│ │ │ │ │────────────────────────────▶ │
│ │ │ │ │ │ │
│ │ │ ├[Initiates sub-operation]╌╌╌╌╌╌╌╌╌╌╌┤
│ │ │ │ │ │ │
│ │ │ │ │ Initiates sub-operation │ │
│ │ │ │ │────────────────────────────▶ │
│ │ │ │ │ │ │
│ │ │ ├[Initiates sub-operation]╌╌╌╌╌╌╌╌╌╌╌┤
│ │ │ │ │ │ │
│ │ │ │ │ Initiates sub-operation │ │
│ │ │ │ │────────────────────────────▶ │
│ │ │ │ │ │ │
│ │ │ └────────────────────────────────────┘
│ │ │ │ │
│ │ │ Assembles and returns response │ │
│ │ ◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌│ │
│ │ │ │ │
│ │ Returns GraphQL/JSON response │ │ │
│ ◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌│ │ │
│ │ │ │ │
│ Returns HTTP response │ │ │ │
◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌│ │ │ │
│ │ │ │ │
┌────┴───┐ ┌────────┴───────┐ ┌──────────┴─────────┐ ┌─────────┴─────────┐ ┌──────────┴──────────┐
│ Client │ │ Router Service │ │ Supergraph Service │ │ Execution Service │ │ Subgraph Service(s) │
└────────┘ └────────────────┘ └────────────────────┘ └───────────────────┘ └─────────────────────┘
Service Hooks
Service Overview
| Service | Description |
|---|---|
router_service |
Runs at the very beginning and very end of the HTTP request lifecycle.For example, JWT authentication is performed within the RouterService.Define router_service if your customization needs to interact with HTTP context and headers. It doesn't support access to the body property |
supergraph_service |
Runs at the very beginning and very end of the GraphQL request lifecycle.Define supergraph_service if your customization needs to interact with the GraphQL request or the GraphQL response. For example, you can add a check for anonymous queries. |
execution_service |
Handles initiating the execution of a query plan after it's been generated.Define execution_service if your customization includes logic to govern execution (for example, if you want to block a particular query based on a policy decision). |
subgraph_service |
Handles communication between the router and your subgraphs.Define subgraph_service to configure this communication (for example, to dynamically add HTTP headers to pass to a subgraph).Whereas other services are called once per client request, this service is called once per subgraph request that's required to resolve the client's request. Each call is passed a subgraph parameter that indicates the name of the corresponding subgraph. |
Signatures:
fn router_service(&self, service: router::BoxService) -> router::BoxService
fn supergraph_service(&self, service: supergraph::BoxService) -> supergraph::BoxService
fn execution_service(&self, service: execution::BoxService) -> execution::BoxService
fn subgraph_service(&self, name: &str, service: subgraph::BoxService) -> subgraph::BoxService
Individual Hooks (Tower Layers)
Use ServiceBuilder to compose these hooks within any service:
| Hook | Purpose | Sync/Async |
|---|---|---|
map_request(fn) |
Transform request before proceeding | Sync |
map_response(fn) |
Transform response before returning | Sync |
checkpoint(fn) |
Validate/filter, can short-circuit | Sync |
checkpoint_async(fn) |
Async validation, can short-circuit | Async |
buffered() |
Enable service cloning (needed for async) | - |
instrument(span) |
Add tracing span around service | - |
rate_limit(num, period) |
Control request throughput | - |
timeout(duration) |
Set operation time limit | - |
Choosing a Service Hook
By data needed:
- HTTP headers only →
router_service - GraphQL query/variables →
supergraph_service - Query plan →
execution_service - Per-subgraph control →
subgraph_service
By timing:
- Before GraphQL parsing →
router_servicerequest - After parsing, before planning →
supergraph_servicerequest - After planning, before execution →
execution_servicerequest - Before/after each subgraph call →
subgraph_service - Final response to client →
router_serviceresponse
See references/service-hooks.md for implementation patterns.
Quick Start
Step 1: Create Plugin File
Create a new file src/plugins/my_plugin.rs with required imports:
use std::ops::ControlFlow;
use apollo_router::plugin::{Plugin, PluginInit};
use apollo_router::register_plugin;
use apollo_router::services::{router, subgraph, supergraph};
use schemars::JsonSchema;
use serde::Deserialize;
use tower::{BoxError, ServiceBuilder, ServiceExt};
const PLUGIN_NAME: &str = "my_plugin";
Step 2: Define Configuration Struct
Every plugin needs a configuration struct with Deserialize and JsonSchema derives. The JsonSchema enables configuration validation in editors:
#[derive(Debug, Clone, Default, Deserialize, JsonSchema)]
struct MyPluginConfig {
/// Enable the plugin
enabled: bool,
// Add other configuration fields as needed
}
Step 3: Define Plugin Struct
#[derive(Debug)]
struct MyPlugin {
configuration: MyPluginConfig,
}
Step 4: Implement Plugin Trait
Implement the Plugin trait with the required Config type and new constructor:
#[async_trait::async_trait]
impl Plugin for MyPlugin {
type Config = MyPluginConfig;
async fn new(init: PluginInit<Self::Config>) -> Result<Self, BoxError> {
Ok(MyPlugin { configuration: init.config })
}
// Add service hooks based on your needs (see "Choosing a Service Hook" section)
}
Step 5: Add Service Hooks
Choose which service(s) to hook based on your requirements, see Service Overview for details.
Example service hook:
fn supergraph_service(&self, service: supergraph::BoxService) -> supergraph::BoxService {
if !self.configuration.enabled {
return service;
}
ServiceBuilder::new()
.map_request(|req| { /* transform request */ req })
.map_response(|res| { /* transform response */ res })
.service(service)
.boxed()
}
Step 6: Register Plugin
At the bottom of your plugin file, register it with the router:
register_plugin!("acme", "my_plugin", MyPlugin);
Step 7: Add Module to mod.rs
In src/plugins/mod.rs, add your module:
pub mod my_plugin;
Step 8: Configure in YAML
Enable your plugin in the router configuration:
plugins:
acme.my_plugin:
enabled: true
Common Patterns
For implementation patterns and code examples, see references/service-hooks.md:
- Enable/disable pattern
- Request/response transformation (
map_request,map_response) - Checkpoint (early return/short-circuit)
- Context passing between hooks
- Async operations (
checkpoint_async,buffered) - Error response builders
Examples
Apollo Router Examples
Located in the Apollo Router plugins directory:
| Plugin | Service Hook | Pattern | Description |
|---|---|---|---|
forbid_mutations.rs |
execution_service |
checkpoint | Simple gate on query plan |
expose_query_plan.rs |
execution + supergraph | Context passing | Multi-service coordination |
cors.rs |
router_service |
HTTP layer | CORS handling at HTTP level |
headers/ |
subgraph_service |
Layer composition | Complex header manipulation |
For full code examples and testing patterns, see references/examples.md.
Prerequisites
It is advised to have the rust-best-practices skill installed for writing idiomatic Rust code when developing router plugins. If installed, follow those best practices when generating or modifying plugin code.
Resources
- references/service-hooks.md - Detailed service hook implementations
- references/existing-plugins.md - Index of existing plugins
- references/examples.md - Full code examples and testing patterns
- Apollo Router plugins: https://github.com/apollographql/router/tree/dev/apollo-router/src/plugins