All skills
apollographql avatar

/apollo-router-plugin-creator

@7be6130 official
by Apollo GraphQLapollographql/skills115 stars
13

Guide for writing Apollo Router native Rust plugins. Use this skill when: (1) users want to create a new router plugin, (2) users want to add service hooks (router_service, supergraph_service, execution_service, subgraph_service), (3) users want to modify an existing router plugin, (4) users need to understand router plugin patterns or the request lifecycle. (5) triggers on requests like "create a new plugin", "add a router plugin", "modify the X plugin", or "add subgraph_service hook".

Use this Skill: https://skilld.dev/gh/apollographql/skills/apollo-router-plugin-creator

This session only. Nothing lands on disk.

SKILL.md

β‰ˆ130 tokens always: the name and description. β‰ˆ4.4k when used: this file. β‰ˆ3.5k more on demand in 3 files.

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_service request
  • After parsing, before planning β†’ supergraph_service request
  • After planning, before execution β†’ execution_service request
  • Before/after each subgraph call β†’ subgraph_service
  • Final response to client β†’ router_service response

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

Source: SKILL.md on GitHub

1 warning16d5 checks Β· Risk SAFE
  • Gen Agent Trust Hub16d

    This skill provides educational guidance and code templates for developing Apollo Router plugins. All external references target the vendor's official trusted repositories, and the provided code follows standard development practices for the platform.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW Β· No issues

  • Runlayer7mo

    3/4 files flagged

  • ZeroLeaks5mo

    Score: 93/100 Β· 2 sections analyzed

Signed by skilld at 7be6130. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 20 hours ago.

Activeupdated 8 months ago
All 1 allowed tools
Read Write Edit Glob Grep
Other metadata
metadata
{
  "author": "apollographql",
  "version": "1.0.0",
  "compatibility": "Requires Apollo Router with native plugin support"
}
  • Rust
  • apollo-router
  • graphql
  • plugins
  • authentication
  • middleware
  • request-lifecycle
  • subgraph
  • tower

README badge

README badge for apollographql/skills/apollo-router-plugin-creator

Guides writing native Rust plugins for Apollo Router that hook into the request lifecycle via router_service, supergraph_service, execution_service, and subgraph_service. Use this to add authentication, request validation, query plan control, or per-subgraph request modification.

Generated from the current SKILL.md.

Which service hook should I use to modify HTTP headers?
Use router_service, which runs at the beginning and end of the HTTP request lifecycle and has access to HTTP context and headers.
Can I access the GraphQL query and variables in a plugin?
Yes, use supergraph_service, which runs at the beginning and end of the GraphQL request lifecycle and can inspect and modify the GraphQL request and response.
How do I add custom logic for each subgraph request?
Use subgraph_service, which is called once per subgraph request and receives the subgraph name, allowing you to configure per-subgraph communication like dynamically adding headers.
What language are Apollo Router plugins written in?
Plugins are written in Rust and compile as native plugins for Apollo Router.
Can I add rate limiting or timeouts in a plugin?
Yes, Tower layers like rate_limit and timeout are available to compose within any service hook.

Generated from the current SKILL.md. These answers refresh after source changes.