### Run Async Auth Example Source: https://github.com/apollographql/router/blob/dev/examples/async-auth/rust/README.md Use this command to run the example. Ensure you have the supergraph.graphql and router.yaml files. ```bash cargo run -- -s ../../graphql/supergraph.graphql -c ./router.yaml ``` -------------------------------- ### Start Local Router with License Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/license.mdx Use this command to start a local GraphOS Router instance, providing the necessary environment variables for fetching a license. Ensure APOLLO_GRAPH_REF points to your license-specific variant and APOLLO_KEY is a valid graph API key. ```bash APOLLO_GRAPH_REF="..." APOLLO_KEY="..." ./router --supergraph schema.graphql ``` -------------------------------- ### Example Coprocessor Helm Chart Structure Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/self-hosted/containerization/kubernetes/extensibility.mdx Illustrates a typical folder structure for a standalone coprocessor Helm chart, separate from the router's chart. ```yaml charts/ ├── coprocessor/ │ ├── Chart.yaml │ ├── values.yaml │ ├── templates/ │ │ ├── deployment.yaml │ │ ├── service.yaml │ │ └── ... │ └── ... ├── router/ │ ├── values.yaml │ └── ... ``` -------------------------------- ### Download example supergraph schema Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/custom-binary.mdx Download a sample supergraph schema file to test the compiled router. This command fetches the schema from a provided URL. ```bash curl -sSL https://supergraph.demo.starstuff.dev/ > supergraph-schema.graphql ``` -------------------------------- ### Configure a custom plugin in router.yaml Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/custom-binary.mdx Add configuration for your custom plugin to the `router.yaml` file. This example shows how to specify a message for a 'hello_world' plugin. ```yaml plugins: starstuff.hello_world: message: "starting my plugin" ``` -------------------------------- ### Deploy Router with Coprocessor Values Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/self-hosted/containerization/kubernetes/extensibility.mdx Install or upgrade the Apollo Router Helm deployment, including your custom coprocessor configuration by referencing the values file. ```bash helm install --namespace --set managedFederation.apiKey="" --set managedFederation.graphRef="" oci://ghcr.io/apollographql/helm-charts/router --version --values router/values.yaml --values coprocessor_values.yaml ``` -------------------------------- ### Log Output Snapshot Test Source: https://github.com/apollographql/router/blob/dev/dev-docs/logging.md Example of log output captured during a test using `assert_snapshot_subscriber!()`, formatted as YAML. ```yaml --- source: apollo-router/src/plugins/authentication/tests.rs expression: yaml --- - fields: alg: "" reason: "invalid value: map, expected map with a single key" index: 3 level: WARN message: "ignoring a key since it is not valid, enable debug logs to full content" - fields: alg: ES256 reason: "invalid type: string \"Hmm\", expected a sequence" index: 5 level: WARN message: "ignoring a key since it is not valid, enable debug logs to full content" ``` -------------------------------- ### Set Multiple Log Levels with Filters Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/observability/router-telemetry-otel/telemetry-pipelines/log-exporters/overview.mdx This example shows how to set different log levels for various components using filters. The RUST_LOG environment variable takes precedence for granular control. ```bash RUST_LOG=hyper=debug,apollo_router=info,h2=trace APOLLO_ROUTER_LOG=hyper=debug,info,h2=trace --log=hyper=debug,info,h2=trace ``` -------------------------------- ### Set Log Level to Debug Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/observability/router-telemetry-otel/telemetry-pipelines/log-exporters/overview.mdx These examples demonstrate setting the log level to 'debug' using different methods. The router checks for RUST_LOG, command-line arguments, and APOLLO_ROUTER_LOG in that order. ```bash RUST_LOG=apollo_router::debug APOLLO_ROUTER_LOG=debug --log=debug ``` -------------------------------- ### Good Rust struct for exporting metrics Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Defines a struct for exporting metrics with a URL. Includes an example configuration in YAML. ```rust /// Export the data to the metrics endpoint /// Example configuration: /// ```yaml /// export: /// url: http://example.com /// ``` #[serde(deny_unknown_fields)] struct Export { /// The url to export metrics to. url: Url } ``` -------------------------------- ### Configure Custom Telemetry Instrument in router.yaml Source: https://github.com/apollographql/router/blob/dev/dev-docs/telemetry-selectors.md Example of how to configure a custom telemetry instrument named 'authenticated_operation' in the `router.yaml` configuration file. This demonstrates setting attributes and conditions for the instrument. ```yaml telemetry: instrumentation: instruments: supergraph: # Custom metric authenticated_operation: value: unit type: counter unit: operation description: "Number of authenticated operations" attributes: http.response.status_code: true "my_attribute": request_header: "x-my-header" graphql.authenticated: authenticated: true # Can also be used as a value for an attribute condition: eq: - true - authenticated: true ``` -------------------------------- ### Bad Rust struct for exporting metrics Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md An example of a less ideal struct definition for exporting metrics, lacking documentation and explicit configuration examples. ```rust #[serde(deny_unknown_fields)] struct Export { url: Url } ``` -------------------------------- ### Get Data from Context Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/native-plugins.mdx Fetches a value from the request context using its key. Ensure the type annotation matches the stored value. ```rust let value : u32 = context.get("key1")?; ``` -------------------------------- ### Configure subgraph APQ settings Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/operations/apq.mdx Configure Automatic Persisted Queries (APQ) for subgraph communications. This example disables APQ globally for all subgraphs except for the 'products' subgraph, which has APQ explicitly enabled. ```yaml apq: subgraph: # Disables subgraph APQ globally except where overridden per-subgraph all: enabled: false # Override global APQ setting for individual subgraphs subgraphs: products: enabled: true ``` -------------------------------- ### Bad Rust struct for telemetry (not extendable) Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md An example of a non-extensible telemetry configuration where a direct URL is used, making it difficult to add related options like authentication. ```rust #[serde(deny_unknown_fields)] struct Telemetry { url: Url } ``` ```yaml telemetry: url: http://example.com # Url for what? ``` -------------------------------- ### Define Service Hooks with ServiceBuilder Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/native-plugins.mdx Use ServiceBuilder to compose plugins and apply common building blocks (layers) to your service. This example shows how to integrate custom logic into the supergraph service. ```rust // Replaces the default definition in the example above use tower::ServiceBuilderExt; use apollo_router::ServiceBuilderExt as ApolloServiceBuilderExt; fn supergraph_service( &self, service: router::BoxService, ) -> router::BoxService { // Always use service builder to compose your plugins. // It provides off-the-shelf building blocks for your plugin. ServiceBuilder::new() // Some example service builder methods: // .map_request() // .map_response() // .rate_limit() // .checkpoint() // .timeout() .service(service) .boxed() } ``` -------------------------------- ### Implement Selector Trait for SupergraphSelector Source: https://github.com/apollographql/router/blob/dev/dev-docs/telemetry-selectors.md Implement the `Selector` trait for the `SupergraphSelector` enum. This example shows how to handle the `Authenticated` variant by checking for an authentication status in the request context. ```rust impl Selector for SupergraphSelector { type Request = supergraph::Request; type Response = supergraph::Response; fn on_request(&self, request: &supergraph::Request) -> Option { match self { // ... SupergraphSelector::Authenticated { authenticated } if *authenticated => { let is_authenticated = request.context.get::(APOLLO_AUTHENTICATED_USER).ok().flatten(); match is_authenticated { Some(is_authenticated) => Some(opentelemetry::Value::Bool(is_authenticated)), None => None, } } // ... // For response _ => None, } } } ``` -------------------------------- ### Build Router with Heap Tracing Only Source: https://github.com/apollographql/router/blob/dev/DEVELOPMENT.md Enable only the `dhat-heap` feature for heap allocation tracing. Requires the `release-dhat` profile. ```shell cargo build --profile release-dhat --features dhat-heap ``` -------------------------------- ### Current PluginInit Structure Source: https://github.com/apollographql/router/blob/dev/dev-docs/decision-records/extend-plugin-init.md The original PluginInit structure includes configuration, supergraph SDL, and a notification channel. ```rust pub struct PluginInit { /// Configuration pub config: T, /// Router Supergraph Schema (schema definition language) pub supergraph_sdl: Arc, pub(crate) notify: Notify, } ``` -------------------------------- ### Build Router with Heap and Ad-hoc Tracing Source: https://github.com/apollographql/router/blob/dev/DEVELOPMENT.md Enable both `dhat-heap` and `dhat-ad-hoc` features to trace heap and ad-hoc memory allocations. Requires the `release-dhat` profile. ```shell cargo build --profile release-dhat --features dhat-heap,dhat-ad-hoc ``` -------------------------------- ### Use #[serde(deny_unknown_fields)] Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Annotate all configuration containers with `#[serde(deny_unknown_fields)]` to catch user mistakes in their configuration files. ```rust #[serde(deny_unknown_fields)] struct Export { url: Url } ``` ```yaml export: url: http://example.com backup: http://example2.com # The user will receive an error for this ``` ```rust struct Export { url: Url } ``` ```yaml export: url: http://example.com backup: http://example2.com # The user will NOT receive an error for this ``` -------------------------------- ### Run Instrumented Router Source: https://github.com/apollographql/router/blob/dev/DEVELOPMENT.md Execute the router binary built with memory tracing features. Specify the schema and configuration files. ```shell cargo run --profile release-dhat --features dhat-heap -- -s ./apollo-router/testing_schema.graphql -c router.yaml ``` -------------------------------- ### Create a new Rust project for the custom router Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/custom-binary.mdx Use the `cargo new` command to initialize a new binary project for your custom router. This command sets up the basic project structure. ```bash cargo new --bin starstuff cd starstuff ``` -------------------------------- ### Use Consistent Terminology in Configuration Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Employ consistent terminology (e.g., request, response, subgraph) and include action verbs to clarify the purpose and timing of configuration options. ```yaml headers: subgraphs: # Modifies the subgraph service products: request: # Retrieves data from the request - propagate: # The action. named: foo ``` ```yaml headers: named: foo # From where, what are we doing, when is it happening? ``` -------------------------------- ### Compile the Apollo Router binary Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/custom-binary.mdx Compile the Apollo Router from source. Use `cargo build` for a debug build, suitable for development, or `cargo build --release` for an optimized production build. ```bash cargo build ``` ```bash cargo build --release ``` -------------------------------- ### Avoid #[serde(flatten)] Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Do not use `#[serde(flatten)]` as it is incompatible with `#[serde(deny_unknown_fields)]`, leading to a poor user experience. Consider nesting configuration instead. ```rust #[serde(deny_unknown_fields)] struct Export { url: Url, backup: Url } #[serde(deny_unknown_fields)] struct Telemetry { export: Export } #[serde(deny_unknown_fields)] struct Metrics { export: Export } ``` ```yaml telemetry: export: url: http://example.com backup: http://example2.com metrics: export: url: http://example.com backup: http://example2.com ``` ```rust #[serde(deny_unknown_fields)] struct Export { url: Url, backup: Url } struct Telemetry { export: Export } ``` ```yaml telemetry: url: http://example.com backup: http://example2.com unknown: sadness # The user will NOT receive an error for this ``` -------------------------------- ### Nesting Coprocessor Configuration in router/values.yaml Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/self-hosted/containerization/kubernetes/extensibility.mdx Shows how to configure the coprocessor URL within the router's values.yaml when the router chart is used as a dependency. ```yaml router: configuration: coprocessor: url: http://: ``` -------------------------------- ### Mocking subgraphs with the mock_subgraphs plugin (recommended) Source: https://github.com/apollographql/router/blob/dev/dev-docs/mock_subgraphs_plugin.md Use the experimental mock_subgraphs plugin for a simplified testing approach. This plugin uses a GraphQL executor to handle any request, requiring only the configuration of response data. It is recommended over the legacy MockedSubgraphs for its ease of use. ```rust let subgraphs = serde_json::json!({ "subgraph_name": { "entities": [ { "__typename": "Contact", "id": "0", "displayName": "Max", "country": "Fr", }, ], }, }); let service = TestHarness::builder() .configuration_json(serde_json::json!({ "include_subgraph_errors": { "all": true }, "experimental_mock_subgraphs": subgraphs, })) .unwrap() .schema(SOME_TESTING_SCHEMA) .build_supergraph() .await .unwrap(); ``` -------------------------------- ### Attach Subscriber for Sync Test Block Source: https://github.com/apollographql/router/blob/dev/dev-docs/logging.md Demonstrates how to attach a subscriber using `subscriber::with_default` for the duration of a synchronous test block. ```rust subscriber::with_default(assert_snapshot_subscriber!(), || { ... }) ``` -------------------------------- ### Updated PluginInit Structure Source: https://github.com/apollographql/router/blob/dev/dev-docs/decision-records/extend-plugin-init.md The extended PluginInit structure provides additional fields for last configuration, root configuration, licensing status, parsed supergraph, and APOLLO_KEY/APOLLO_GRAPH_REF. ```rust pub struct PluginInit { /// Configuration pub config: T, /// Configuration from last successful reload unless this is the first load. pub last_config: Option, /// Router Supergraph Schema (schema definition language) pub supergraph_sdl: Arc, /// The root configuration object of the router. pub (crate) root_configuration: Arc, /// True it the router was started with a valid license. pub (crate) licensed: bool, /// The parsed supergraph. pub (crate) supergraph: Arc, /// APOLLO_KEY if set pub (crate) apollo_key: Option, /// APOLLO_GRAPH_REF if set pub (crate) apollo_graph_ref: Option, pub(crate) notify: Notify, } ``` -------------------------------- ### Apollo Router CLI Usage Source: https://github.com/apollographql/router/blob/dev/README.md Displays the command-line interface options for the Apollo Router. Use flags to specify configuration, supergraph schema location, and logging levels. ```bash Usage: Commands: config Configuration subcommands help Print this message or the help of the given subcommand(s) Options: --log Log level (off|error|warn|info|debug|trace) [env: APOLLO_ROUTER_LOG=] [default: info] --hot-reload Reload locally provided configuration and supergraph files automatically. This only affects watching of local files and does not affect supergraphs and configuration provided by GraphOS through Uplink, which is always reloaded immediately [env: APOLLO_ROUTER_HOT_RELOAD=] -c, --config Configuration location relative to the project directory [env: APOLLO_ROUTER_CONFIG_PATH=] --dev Enable development mode [env: APOLLO_ROUTER_DEV=] -s, --supergraph Schema location relative to the project directory [env: APOLLO_ROUTER_SUPERGRAPH_PATH=] --apollo-uplink-endpoints The endpoints (comma separated) polled to fetch the latest supergraph schema [env: APOLLO_UPLINK_ENDPOINTS=] --anonymous-telemetry-disabled Disable sending anonymous usage information to Apollo [env: APOLLO_TELEMETRY_DISABLED=] --apollo-uplink-timeout The timeout for an http call to Apollo uplink. Defaults to 30s [env: APOLLO_UPLINK_TIMEOUT=] [default: 30s] --listen The listen address for the router. Overrides `supergraph.listen` in router.yaml [env: APOLLO_ROUTER_LISTEN_ADDRESS=] -V, --version Display version and exit -h, --help Print help ``` -------------------------------- ### Implement Async Authentication Service Source: https://github.com/apollographql/router/blob/dev/examples/async-auth/rust/README.md Integrates asynchronous authentication using checkpoint_async. Requires a subsequent buffer call because checkpoint_async requires the downstream service to be Clone. ```rust fn supergraph_service( &mut self, service: router::BoxService, ) -> router::BoxService { ServiceBuilder::new() .checkpoint_async(...) // Authentication happens here .buffer(20_000) // Required, see note below .service(service) .boxed() } ``` -------------------------------- ### Use Positive Configuration Options Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Design configuration options using positive terms with defaults set to `true` or enabled states. This avoids negation when users read the configuration. ```yaml homepage: enabled: true log_headers: true ``` ```yaml my_plugin: disabled: false redact_headers: false ``` -------------------------------- ### Good Rust struct for telemetry with future extensibility Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Demonstrates an extensible design for telemetry configuration using nested structs, allowing for future additions like authentication. ```rust #[serde(deny_unknown_fields)] struct Export { url: Url // Future export options may be added here } #[serde(deny_unknown_fields)] struct Telemetry { export: Export } ``` ```yaml telemetry: export: url: http://example.com ``` -------------------------------- ### Run the compiled router with a supergraph schema Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/custom-binary.mdx Execute the compiled router using `cargo run`, providing the path to your supergraph schema file and any necessary configuration. This command is useful for development and testing. ```bash cargo run -- --hot-reload --config router.yaml --supergraph supergraph-schema.graphql ``` -------------------------------- ### Attach Subscriber for Async Test Block Source: https://github.com/apollographql/router/blob/dev/dev-docs/logging.md Shows how to attach a subscriber to an asynchronous block using `.with_subscriber()` for testing. ```rust async{...}.with_subscriber(assert_snapshot_subscriber!()).await ``` -------------------------------- ### Rust Plugin: Use Declarations Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/native-plugins.mdx Essential `use` declarations for most Rust plugins. The compiler will warn if any are unnecessary. You can also include modules from other crates. ```rust use apollo_router::plugin::Plugin; use apollo_router::register_plugin; use apollo_router::services::*; use schemars::JsonSchema; use serde::Deserialize; use tower::{BoxError, ServiceBuilder, ServiceExt}; ``` -------------------------------- ### Write Concise Error Messages with Actions Source: https://github.com/apollographql/router/blob/dev/dev-docs/logging.md Error messages should be short and concise. Include potential resolution steps in an 'actions' attribute if applicable. ```rust error!(actions = ["check that the request is valid on the client, and modify the router config to allow the request"], "bad request"); ``` ```rust error!(request, "bad request, check that the request is valid on the client, and modify the router config to allow the request"); ``` -------------------------------- ### Publish Custom Metrics with OpenTelemetry Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/native-plugins.mdx Use the OpenTelemetry meter_provider to register a new instrument for publishing custom metrics. The instrument will publish metrics until it is dropped. ```rust use apollo_router::metrics::meter_provider; let meter = meter_provider().meter("apollo/router"); // The instrument will publish metrics until it is dropped. let _instrument = meter.u64_observable_gauge("foo") .with_description("The amount of active foos") .with_callback(|gauge| { gauge.observe(count_the_foos()); }) .init(); ``` -------------------------------- ### Register a Plugin Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/native-plugins.mdx Registers a custom plugin with Apollo Router using the `register_plugin!` macro. Provide a group name, plugin name, and the struct implementing the `Plugin` trait. ```rust register_plugin!("example", "hello_world", HelloWorld); ``` -------------------------------- ### Run Code Coverage Locally Source: https://github.com/apollographql/router/blob/dev/DEVELOPMENT.md Generate code coverage reports using `cargo-llvm-cov` and `nextest`. This command provides a summary of the coverage. ```shell cargo llvm-cov nextest --summary-only ``` -------------------------------- ### Rust struct with enabled flag and optional URL Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md For configurations with no sub-options or obvious defaults, use an `enabled: bool` flag. Optional fields should have explicit defaults defined. ```rust #[serde(deny_unknown_fields)] struct Export { enabled: bool, #[serde(default = "default_resource")] url: Url // url is optional, see also but see advice on defaults. } ``` ```yaml export: enabled: true ``` -------------------------------- ### Configure Coprocessor Container in values.yaml Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/self-hosted/containerization/kubernetes/extensibility.mdx Add a container definition for your coprocessor within the router's Helm chart values. This includes the container name, application image, exposed port, and environment variables. ```yaml extraContainers: - name: # name of deployed container image: # name of application image ports: - containerPort: # must match port of router.configuration.coprocessor.url env: [] # array of environment variables ``` -------------------------------- ### Rust Plugin: Configuration Struct Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/native-plugins.mdx Define the configuration struct for your plugin. It must derive `Debug`, `Default`, `Deserialize`, and `JsonSchema`. The configuration is deserialized from YAML. ```rust #[derive(Debug, Default, Deserialize, JsonSchema)] struct Conf { // Put your plugin configuration here. It's deserialized from YAML automatically. } ``` -------------------------------- ### Create Custom Spans with Tracing Macros Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/native-plugins.mdx Utilize the `tracing` macros to generate custom spans for tracing within the router. This is useful for customizing traces generated and linked by the router. ```rust use tracing::info_span; info_span!("my_span"); ``` -------------------------------- ### Avoid Option for Configuration Fields Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Prefer direct types over `Option` to simplify consuming code, especially when using builder patterns without `with_` methods. ```rust #[serde(deny_unknown_fields)] struct Export { #[serde(default="default_url_fn") url: Url } ``` ```rust builder = builder.with_url(config.url); ``` ```rust #[serde(deny_unknown_fields)] struct Export { url: Option } ``` ```rust if let Some(url) = config.url { builder = builder.with_url(url); } ``` -------------------------------- ### Define Metrics with Units Source: https://github.com/apollographql/router/blob/dev/dev-docs/metrics.md Use `_with_unit` variant macros for new metrics. Units should conform to OpenTelemetry semantic conventions. Seconds are used for durations, and counts use curly braces like `{request}`. ```rust u64_counter_with_unit!("apollo.test.requests", "test description", "{request}", 1); // apollo_test_requests f64_counter_with_unit!("apollo.test.total_duration", "test description", "s", 1); // apollo_test_total_duration_seconds ``` -------------------------------- ### Mocking subgraphs with MockedSubgraphs (legacy) Source: https://github.com/apollographql/router/blob/dev/dev-docs/mock_subgraphs_plugin.md Use MockedSubgraphs to intercept subgraph requests and respond with fixed JSON data. This method requires configuring expected requests and their corresponding responses, which can be challenging due to unpredictable query strings generated by the query planner. ```rust let subgraphs = MockedSubgraphs([ ( "subgraph_name", MockSubgraph::builder() .with_json( serde_json::json!({ "query": "query($representations:[_Any!]!){_entities(representations:$representations){...on Contact{__typename country}}}", "variables": { "representations": [ { "__typename":"Contact", "id":"0", "displayName": "Max", } ] } }), serde_json::json!({"data": { "_entities": [{ "__typename":"Contact", "country": "Fr" }] }}) ).with_json( serde_json::json!({ "query": "query($representations:[_Any!]!){_entities(representations:$representations){country}}", "variables": { "representations": [ { "__typename":"Contact", "id":"0", "displayName": "Max", } ] } }), serde_json::json!({"data": { "_entities": [{ "country": "Fr" }] }}) ).build(), ) ].collect()); let service = TestHarness::builder() .configuration_json(serde_json::json!({ "include_subgraph_errors": { "all": true }, })) .unwrap() .schema(SOME_TESTING_SCHEMA) .extra_plugin(subgraphs) .build_supergraph() .await .unwrap(); ``` -------------------------------- ### Test Non-Async Metrics Source: https://github.com/apollographql/router/blob/dev/dev-docs/metrics.md When testing, use `u64_counter_with_unit!` for metrics and `assert_counter!` to verify their values. Metrics are stored in a thread-local storage for each test. ```rust #[test] fn test_non_async() { // Each test is run in a separate thread, metrics are stored in a thread local. u64_counter_with_unit!("test", "test description", 1, "attr" => "val"); assert_counter!("test", 1, "attr" => "val"); } ``` -------------------------------- ### Rust struct with default URL using a function Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Use `#[serde(default = "default_value_fn")]` to provide default values for fields. The default function's result should be static and can be used to generate JSON schemas with defaults. ```rust #[serde(deny_unknown_fields)] struct Export { #[serde(default="default_url_fn") url: Url } ``` -------------------------------- ### Testing Spawned Task Metric Resolution Without Propagation Source: https://github.com/apollographql/router/blob/dev/dev-docs/metrics.md Demonstrates how metrics updated in spawned tasks are not collected correctly without using `with_current_meter_provider()`. In testing, the metric will resolve to the value before the spawned task's update. ```rust #[tokio::test] async fn test_spawned_metric_resolution() { async { u64_counter!("apollo.router.test", "metric", 1); assert_counter!("apollo.router.test", 1); tokio::spawn(async move { u64_counter!("apollo.router.test", "metric", 2); }) .await .unwrap(); // In real operations, this metric resolves to a total of 3! // However, in testing, it will resolve to 1, because the second incrementation happens in another thread. // assert_counter!("apollo.router.test", 3); assert_counter!("apollo.router.test", 1); } .with_metrics() .await; } ``` -------------------------------- ### Propagating Metrics Across Spawned Tasks Source: https://github.com/apollographql/router/blob/dev/dev-docs/metrics.md Use `.with_current_meter_provider()` to propagate the meter provider to child tasks when testing metrics across spawned tasks. This ensures metrics updated in spawned tasks are correctly collected. ```rust #[tokio::test] async fn test_metrics_across_tasks() { async { u64_counter!("apollo.router.test", "metric", 1); assert_counter!("apollo.router.test", 1); // Use with_current_meter_provider to propagate metrics to spawned task tokio::spawn(async move { u64_counter!("apollo.router.test", "metric", 2); }.with_current_meter_provider()) .await .unwrap(); // Now the metric correctly resolves to 3 since the meter provider was propagated assert_counter!("apollo.router.test", 3); } .with_metrics() .await; } ``` -------------------------------- ### Rust struct with optional URL field (BAD) Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Avoid using `Option` for configuration fields as it can lead to users being unaware that a value has been defaulted, especially for complex types. ```rust #[serde(deny_unknown_fields)] struct Export { url: Option } ``` ```yaml export: # The user is not aware that url was defaulted. ``` -------------------------------- ### Replace OneShotAsyncCheckpointLayer with AsyncCheckpointLayer Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/upgrade/from-router-v1.mdx When upgrading Rust plugins, replace `OneShotAsyncCheckpointLayer` with `AsyncCheckpointLayer`. The `buffered()` method, provided by `apollo_router::layers::ServiceBuilderExt`, ensures your service can be cloned. ```rust use apollo_router::layers::async_checkpoint_layer::AsyncCheckpointLayer; AsyncCheckpointLayer::new(move |request: execution::Request| { let request_config = request_config.clone(); // ... }) .buffered() .service(service) .boxed() ``` -------------------------------- ### Create Non-Observable and Observable Metrics Source: https://github.com/apollographql/router/blob/dev/dev-docs/metrics.md Use non-observable instruments for histograms and counters. Observable instruments, like gauges, must be created after the telemetry plugin is activated. ```rust u64_counter!("test", "test description", 1, vec![KeyValue::new("attr", "val")]); u64_counter!("test", "test description", 1, "attr" => "val"); u64_counter!("test", "test description", 1); // observable instruments - good for gauges meter_provider() .meter("test") .u64_observable_gauge("test") .with_callback(|m| m.observe(5, &[])) .init(); ``` -------------------------------- ### Bad Rust struct for telemetry (unclear naming) Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Illustrates a poorly named field for telemetry export URL, which lacks clarity and extensibility for features like authentication. ```rust #[serde(deny_unknown_fields)] struct Telemetry { export_url: Url // export_url is not extendable. You can't add things like auth. } ``` ```yaml telemetry: export_url: http://example.com # How do I specify auth ``` -------------------------------- ### Define Static Metric Source: https://github.com/apollographql/router/blob/dev/dev-docs/metrics.md Use this macro to define static metrics for monitoring feature usage. Ensure metrics are low cardinality and do not leak sensitive information. These metrics are transmitted to Apollo unless explicitly disabled. ```rust u64_counter!("apollo.router..", "description", 1, "attr" => "val"); ``` -------------------------------- ### Testing Async Metrics with .with_metrics() Source: https://github.com/apollographql/router/blob/dev/dev-docs/metrics.md Use `.with_metrics()` on async blocks to ensure metrics are stored in a task local. Tests will fail silently if this is omitted. This is crucial for both single-threaded and multi-threaded runtimes. ```rust use crate::metrics::FutureMetricsExt; #[tokio::test(flavor = "multi_thread")] async fn test_async_multi() { // Multi-threaded runtime needs to use a tokio task local to avoid tests interfering with each other async { u64_counter!("test", "test description", 1, "attr" => "val"); assert_counter!("test", 1, "attr" = "val"); } .with_metrics() .await; } #[tokio::test] async fn test_async_single() { async { // It's a single threaded tokio runtime, so we can still use a thread local u64_counter!("test", "test description", 1, "attr" => "val"); assert_counter!("test", 1, "attr" = "val"); } .with_metrics() .await; } ``` -------------------------------- ### Router Helm Chart Dependency Configuration Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/self-hosted/containerization/kubernetes/extensibility.mdx Defines the router Helm chart as a dependency within another Helm chart's Chart.yaml file. ```yaml dependencies: - name: router version: 2.3.0 repository: oci://ghcr.io/apollographql/helm-charts ``` -------------------------------- ### Use #[serde(default)] on Structs Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Apply `#[serde(default)]` to the entire struct when all fields have default values. This ensures consistent behavior between deserialization and default instantiation. ```rust #[serde(deny_unknown_fields, default)] struct Export { url: Url, enabled: bool } impl Default for Export { fn default() -> Self { Self { url: default_url_fn(), enabled: false } } } ``` ```rust #[serde(deny_unknown_fields)] struct Export { #[serde(default="default_url_fn") url: Url, #[serde(default)] enabled: bool } ``` -------------------------------- ### Insert Data into Context Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/native-plugins.mdx Adds a key-value pair to the request context. Serialization and deserialization are handled automatically. Specify the type if the compiler cannot infer it. ```rust context.insert("key1", 1)?; ``` -------------------------------- ### MockSubgraphPluginConfig type definition Source: https://github.com/apollographql/router/blob/dev/dev-docs/mock_subgraphs_plugin.md Defines the structure for configuring the mock_subgraphs plugin, including entities, query, mutation, and headers for each subgraph. ```rust type MockSubgraphPluginConfig = Map; type SubgraphName = String; struct ConfigForOneSubgraph { /// Entities that can be queried through Federation’s special `_entities` field entities: Vec, /// Data for `query` operations (excluding the special `_entities` field) query: JsonMap, /// Data for `mutation` operations mutation: Option, /// HTTP headers for the subgraph response headers: Map, } ``` -------------------------------- ### Rust struct with required URL field Source: https://github.com/apollographql/router/blob/dev/dev-docs/yaml-design-guidance.md Use `#[serde(deny_unknown_fields)]` to ensure all specified fields are required. This prevents users from being unaware that a field was defaulted. ```rust #[serde(deny_unknown_fields)] struct Export { url: Url // url is required } ``` ```yaml export: url: http://example.com ``` -------------------------------- ### Implement Checkpoint for Operation Validation Source: https://github.com/apollographql/router/blob/dev/examples/forbid-anonymous-operations/rust/README.md Use the `checkpoint` function within a `ServiceBuilder` to validate operations before they are processed. This middleware can halt requests and return immediately, which is useful for authentication checks. ```rust fn supergraph_service( &mut self, service: router::BoxService, ) -> router::BoxService { ServiceBuilder::new() .checkpoint(...) // Validation happens here .service(service) .boxed() } ``` -------------------------------- ### Disable default memory allocator in Cargo.toml Source: https://github.com/apollographql/router/blob/dev/docs/source/routing/customization/custom-binary.mdx To use a different memory allocator than the default jemalloc on Linux, disable the default features for the `apollo-router` dependency in your `Cargo.toml` file. This allows the executable crate to choose its own allocator. ```toml [dependencies] apollo-router = {version = "[…]", default-features = false} ```