github apollographql/router v0.16.0

latest releases: v2.0.0-preview.0, v1.56.0, v1.56.0-rc.0...
2 years ago

Changelog for the next release

All notable changes to Router will be documented in this file.

This project adheres to Semantic Versioning.

[0.16.0] - 2022-08-22

We're getting closer and closer to our 1.0 release and with that we have a lot of polish that we're applying to our API to get it ready for it to be a durable surface area for our consumers to depend on. Due to various learnings we've had during the pre-1.0 phases of the Router, we are evolving our API to match what we now know.

We do not intend on doing this much moving around of things again soon, but anyone who has been following the repository the last couple weeks knows there has been a lot of activity and discussion about where things should live. This means that this release has an abnormally high number of breaking changes, though we believe you'll find most of them to be relatively straightforward to pivot away from.

Please review the full change log to get all the details, but for the most part the changes in this release consist of:

  • a lot of renames of existing symbols
  • the re-location of exported symbols to more appropriate modules
  • the privatization of functions which we don't believe users needed directly (see below if any of these turn out to be a problem).

During each step of the migration, we recommend searching this changelog for a symbol to find advice on how to migrate it. We've tried to make the instructions and path forward as clear as possible.

  • If you find yourself needing help migrating to the new patterns, please first take a close look at the examples provided in this change log and if you still need help, please open a discussion.
  • If you find yourself unable to do something you had previously been able to do, please open an issue. Please make sure you include your use-case so we can understand better and document it for posterity!

We appreciate your patience working through these and we're excited for the steps ahead!

❗ BREAKING ❗

Remove QueryPlannerService (PR #1552)

This service was redundant, since anything done as part of the QueryPlannerService could be done either at the SupergraphService or at the ExecutionService level.

By @o0Ignition0o in #1552

Rename map_future_with_context to map_future_with_request_data (PR #1547)

The function is not very well named since it's in fact used to extract any data from a request for use in a future. This rename makes it clear.

By @garypen in #1547

Rename traffic shaping deduplication options (PR #1540)

In the traffic shaping module:

  • variables_deduplication configuration option is renamed to deduplicate_variables.
  • query_deduplication configuration option is renamed to deduplicate_query.
- traffic_shaping:
-   variables_deduplication: true # Enable the variables deduplication optimization
-   all:
-     query_deduplication: true # Enable query deduplication for all subgraphs.
-   subgraphs:
-     products:
-       query_deduplication: false # Disable query deduplication for products.
+ traffic_shaping:
+   deduplicate_variables: true # Enable the variables deduplication optimization
+   all:
+     deduplicate_query: true # Enable query deduplication for all subgraphs.
+   subgraphs:
+     products:
+       deduplicate_query: false # Disable query deduplication for products.

By @garypen in #1540

Make query_plan_options private and wrap QueryPlanContent in an opaque type (PR #1486)

QueryPlanOptions::query_plan_options is no longer public. If you still necessitate usage of this, please open an issue with your use case.

By @bnjjj in #1486

Removed delay_interval in telemetry configuration. (PR #1498)

It was doing nothing.

telemetry:
  metrics:
    common:
      # Removed, will now cause an error on Router startup:
      delay_interval:
        secs: 9
        nanos: 500000000

By @SimonSapin in #1498

Remove telemetry configuration hot reloading (PR #1463)

Configuration hot reloading is not very useful for telemetry, and is the
source of regular bugs that are hard to fix.

This removes the support for configuration reloading entirely. Now, the
router will reject a configuration reload with an error log if the
telemetry configuration changed.

It is now possible to create a subscriber and pass it explicitely to the telemetry plugin
when creating it. It will then be modified to integrate the telemetry plugin's layer.

By @geal in #1463

Reorder query planner execution (PR #1484)

Query planning is deterministic and only depends on the query, operation name and query planning
options. As such, we can cache the result of the entire process.

This changes the pipeline to apply query planner plugins between the cache and the bridge planner,
so those plugins will only be called once on the same query. If changes must be done per query,
they should happen in a supergraph service.

By @Geal in #1464

Remove Buffer from Mock*Service (PR #1440

This removes the usage of tower_test::mock::Mock in mocked services because it isolated the service in a task
so panics triggered by mockall were not transmitted up to the unit test that should catch it.
This rewrites the mocked services API to remove the build() method, and make them clonable if needed,
using an expect_clone call with mockall.

By @Geal in #1440

Some items were renamed or moved (PR #1487 PR #1534 PR #1555 PR #1563)

At the crate root:

  • SchemaKindSchemaSource
  • SchemaKind::String(String)SchemaSource::Static { schema_sdl: String }
  • ConfigurationKindConfigurationSource
  • ConfigurationKind::InstanceConfigurationSource::Static
  • ShutdownKindShutdownSource
  • ApolloRouterRouterHttpServer

In the apollo_router::plugin::Plugin trait:

  • router_servicesupergraph_service

In the apollo_router::services module, to new public sub-modules:

  • SupergraphRequestsupergraph::Request
  • SupergraphResponsesupergraph::Response
  • ExecutionRequestexecution::Request
  • ExecutionResponseexecution::Response
  • SubgraphRequestsubgraph::Request
  • SubgraphResponsesubgraph::Response

For convenience, these new sub-modules each contain type aliases
base on their respective Request and Response types.

pub type BoxService = tower::util::BoxService<Request, Response, tower::BoxError>;
pub type BoxCloneService = tower::util::BoxCloneService<Request, Response, tower::BoxError>;
pub type ServiceResult = Result<Response, tower::BoxError>;

Migration example:

-use tower::util::BoxService;
-use tower::BoxError;
-use apollo_router::services::{RouterRequest, RouterResponse};
+use apollo_router::services::router;
 
-async fn example(service: BoxService<RouterRequest, RouterResponse, BoxError>) -> RouterResponse {
+async fn example(service: router::BoxService) -> router::Response {
-    let request = RouterRequest::builder()/*…*/.build();
+    let request = router::Request::builder()/*…*/.build();
     service.oneshot(request).await
 }

By @SimonSapin in #1487, #1534, #1555, #1563

Some items were removed from the public API (PR #1487 PR #1535)

If you used some of them and don’t find a replacement,
please file an issue
with details about the use case.

apollo_router::Configuration::boxed
apollo_router::Configuration::is_compatible
apollo_router::errors::CacheResolverError
apollo_router::errors::JsonExtError
apollo_router::errors::ParsesError::print
apollo_router::errors::PlanError
apollo_router::errors::PlannerError
apollo_router::errors::PlannerErrors
apollo_router::errors::QueryPlannerError
apollo_router::errors::ServiceBuildError
apollo_router::json_ext
apollo_router::layers::ServiceBuilderExt::cache
apollo_router::mock_service!
apollo_router::plugins
apollo_router::plugin::plugins
apollo_router::plugin::PluginFactory
apollo_router::plugin::DynPlugin
apollo_router::plugin::Handler
apollo_router::plugin::test::IntoSchema
apollo_router::plugin::test::MockSubgraphFactory
apollo_router::plugin::test::PluginTestHarness
apollo_router::query_planner::QueryPlan::execute
apollo_router::services
apollo_router::Schema

By @SimonSapin

Router startup API changes (PR #1487)

The RouterHttpServer::serve method and its return type RouterHandle were removed,
their functionality merged into RouterHttpServer (formerly ApolloRouter).
The builder for RouterHttpServer now ends with a start method instead of build.
This method immediatly starts the server in a new Tokio task.

 RouterHttpServer::builder()
     .configuration(configuration)
     .schema(schema)
-    .build()
-    .serve()
+    .start()
     .await

By @SimonSapin in #1487

router_builder_fn replaced by shutdown in the Executable builder (PR #1487)

The builder for apollo_router::Executable had a router_builder_fn method
allowing the specification of how a RouterHttpServer (previously ApolloRouter) was to be created
with a provided configuration and schema.
Since the only possible variation was specifying when the server should shut down
(with a ShutdownSource parameter) the router_builder_fn was replaced with a new shutdown method.

 use apollo_router::Executable;
-use apollo_router::RouterHttpServer;
 use apollo_router::ShutdownSource;

 Executable::builder()
-    .router_builder_fn(|configuration, schema| RouterHttpServer::builder()
-        .configuration(configuration)
-        .schema(schema)
-        .shutdown(ShutdownSource::None)
-        .start())
+    .shutdown(ShutdownSource::None)
     .start()
     .await

By @SimonSapin in #1487

Removed constructors when there is a public builder (PR #1487)

Many types in the Router API can be constructed with the builder pattern.
We use the buildstructor crate
to auto-generate builder boilerplate based on the parameters of a constructor.
These constructors have been made private so that users must go through the builder instead,
which will allow us to add parameters in the future without a breaking API change.
If you were using one of these constructors, the migration generally looks like this:

-apollo_router::graphql::Error::new(m, vec![l], Some(p), Default::default())
+apollo_router::graphql::Error::build()
+    .message(m)
+    .location(l)
+    .path(p)
+    .build()

By @SimonSapin in #1487

Removed deprecated type aliases (PR #1487)

A few versions ago, some types were moved from the crate root to a new graphql module.
To help the transition, type aliases were left at the old location with a deprecation warning.
These aliases are now removed and remaining imports must be changed to the new locations:

-use apollo_router::Error;
-use apollo_router::Request;
-use apollo_router::Response;
+use apollo_router::graphql::Error;
+use apollo_router::graphql::Request;
+use apollo_router::graphql::Response;

Alternatively, import the module with use apollo_router::graphql
then use qualified paths such as graphql::Request.
This can help disambiguate when multiple types share a name.

By @SimonSapin in #1487

RouterRequest::fake_builder defaults to Content-Type: application/json (PR #1487)

apollo_router::services::RouterRequest has a builder for creating a “fake” request during tests.
When no Content-Type header is specified, this builder will now default to application/json.
This will help tests where a request goes through mandatory plugins, including CSRF protection,
which makes the request be accepted by CSRF protection.

If a test requires a request specifically without a Content-Type header,
this default can be removed from a RouterRequest after building it:

let mut router_request = RouterRequest::fake_builder().build();
router_request.originating_request.headers_mut().remove("content-type");

By @SimonSapin in #1487

Plugins return a service to create custom endpoints (Issue #1481)

Rust plugins can implement the Plugin::custom_endpoint trait method
to handle non-GraphQL HTTP requests.

Previously, the return type of this method was Option<apollo_router::plugin::Handler>,
where a Handler could be created with:

impl Handler {
    pub fn new(service: tower::util::BoxService<
        apollo_router::http_ext::Request<bytes::Bytes>, 
        apollo_router::http_ext::Response<bytes::Bytes>, 
        tower::BoxError
    >) -> Self {/* … */}
}

Handler has been removed from the public API and plugins now return a BoxService directly.
Additionally, the type for HTTP request and response bodies was changed
from bytes::Bytes to hyper::Body which is more flexible and is compatible with streams (which are necessary in future versions of the Router).

The changes needed if using custom endpoints are:

  • Replace Handler::new(service) with service
  • To read the full request body,
    use hyper::body::to_bytes
    or hyper::body::aggregate.
  • A response Body can be created through conversion traits from various types.
    For example: "string".into()

By @SimonSapin in #1533

🚀 Features

rhai logging functions now accept Dynamic parameters (PR #1521)

Prior to this change, rhai logging functions worked with string parameters. This change means that any valid rhai object
may now be passed as a logging parameter.

By @garypen

Reduce initial memory footprint by lazily populating introspection query cache (Issue #1517)

In an early alpha release of the Router, we only executed certain "known" introspection queries because of prior technical constraints that prohibited us from doing something more flexible. Because the set of introspection queries was "known", it made sense to cache them.

As of #802, this special-casing is (thankfully) no longer necessary and we no longer need to know (and constrain!) the introspection queries that the Router supports.

We could have kept caching those "known" queries, however we were finding that the resulting cache size was quite large and making the Router's minimum memory footprint larger than need be since we were caching many introspection results which the Router instance would never encounter.

This change removes the cache entirely and allows introspection queries served by the Router to merely be lazily calculated and cached on-demand, thereby reducing the initial memory footprint. Disabling introspection entirely will prevent any use of this cache since no introspection will be possible.

By @o0Ignition0o in #1517

Expose query plan in extensions of GraphQL response (experimental) (PR #1470)

When enabled in configuration, it is now possible to expose the query plan in the GraphQL response extensions. This is only experimental at the moment, and we plan to integrate it into an upcoming version of Apollo Studio. Currently, no documentation is available.

By @bnjjj in #1470

Add support of global rate limit and timeout. PR #1347

Additions to the traffic shaping plugin:

  • Global rate limit - If you want to rate limit requests to subgraphs or to the router itself.
  • Timeout: - Set a timeout to subgraphs and router requests.
traffic_shaping:
  router: # Rules applied to requests from clients to the router
    global_rate_limit: # Accept a maximum of 10 requests per 5 secs. Excess requests must be rejected.
      capacity: 10
      interval: 5s # Value in milliseconds must be greater than 0 and less than the max of a 64-bit integer (2^64-1).
    timeout: 50s # If a request to the router takes more than 50secs then cancel the request (30 sec by default)
  subgraphs: # Rules applied to requests from the router to individual subgraphs
    products:
      global_rate_limit: # Accept a maximum of 10 requests per 5 secs from the router. Excess requests must be rejected.
        capacity: 10
        interval: 5s # Value in milliseconds must be greater than 0 and less than the max of a 64-bit integer (2^64-1).
      timeout: 50s # If a request to the subgraph 'products' takes more than 50secs then cancel the request (30 sec by default)

By @bnjjj in #1347

Explicit shutdown for RouterHttpServer handle (PR #1487)

If you explicitly create a RouterHttpServer handle,
dropping it while the server is running instructs the server shut down gracefuly.
However with the handle dropped, there is no way to wait for shutdown to end
or check that it went without error.
Instead, the new shutdown async method can be called explicitly
to obtain a Result:

 use RouterHttpServer;
 let server = RouterHttpServer::builder().schema("schema").start();
 // …
-drop(server);
+server.shutdown().await.unwrap(); 

By @SimonSapin in #1487

Added apollo_router::TestHarness (PR #1487)

This is a builder for the part of an Apollo Router that handles GraphQL requests,
as a tower::Service.
This allows tests, benchmarks, etc
to manipulate request and response objects in memory without going over the network.
See the API documentation for an example. (It can be built with cargo doc --open.)

By @SimonSapin in #1487

Introduce map_deferred_response method for deferred responses (PR #1501)

The map_deferred_response method is now available for the router service and execution
service in Rhai. When using the @defer directive, we get the data in a serie of graphql
responses. The first one is available with the map_response method, where the HTTP headers
and the response body can be modified. The following responses are available through
map_deferred_response, which only has access to the response body.

By @geal in #1501

🐛 Fixes

Return HTTP status code 400 when variables validation fails (Issue #1403)

Failure to validate out-of-band variables against both the query and the corresponding schema will now result in an HTTP status code of 400 being returned to the client. This instructs the client not to bother retrying without changing something about what it previously sent since subsequent retries would just fail validation again and again.

By @o0Ignition0o

Include usage reporting data in the context even when the query plan has been cached (#1559)

Include usage reporting data in the context even when the query plan has been cached when calling CachingQueryPlanner.

By @bnjjj in #1559

Accept SIGTERM as shutdown signal (PR #1497)

This will make containers stop faster as they will not have to wait until a SIGKILL to stop the router (which generally comes several seconds later).

By @Geal in #1497

Set the response path for deferred responses (PR #1529)

Some GraphQL clients rely on the response path to find out which
fragment created a deferred response, and generate code that checks the
type of the value at that path.
Previously the router was generating a value that starts at the root
for every deferred response. Now it checks the path returned by the query
plan execution and creates a response for each value that matches that
path.
In particular, for deferred fragments on an object inside an array, it
will create a separate response for each element of the array.

By @Geal in #1529

Activate defer support in introspection (PR #1557)

Introspection queries will now see the @defer directive if it was activated in the configuration file.

By @Geal in #1557

Support the incremental response field (PR #1551)

Recent changes in the @defer specification now mandate that the deferred responses are transmitted
as an array in the new incremental field of the JSON response.

By @Geal in #1551

🛠 Maintenance

These are generally internal improvements to the Router repository on GitHub.

Display licenses.html diff in CI if the check failed (#1524)

The CI check that ensures that the license.html file is up to date now displays what has changed when the file is out of sync.

By @o0Ignition0o

🚀 Features

Helm: Rhai script and Istio virtualservice support (#1478)

You can now pass a Rhai script file to the helm chart.
You can also provide an Istio VirtualService configuration, as well as custom Egress rules.
Head over to the helm chart default values to get started.

By @o0Ignition0o in #1478

📚 Documentation

Clarify path parameter usage (PR #1473)

Add an inline example of path parameter usage to the section of the docs explaining that you cannot specify a wildcard in the middle of a path.

By @EverlastingBugstopper in #1473

Don't miss a new router release

NewReleases is sending notifications on new releases.