Working with Router
Configuring GraphOS Router for Apollo Connectors
Overriding baseURL
for environment-specific API hosts
When using a self-hosted router, the baseURL
of a @source
directive can be overridden in the router configuration file.
For example, this configuration overrides the baseURL
for all @connect
directives in the subgraph that reference the v1
source:
1preview_connectors:
2 subgraphs:
3 example: # The name of the subgraph
4 sources:
5 v1: # Refers to @source(name: "v1")
6 override_url: "https://api.example.com/v1/beta"
@connect
directive doesn't specify the source
attribute, its URL can't be overridden.Configuration and environment variables
You may want to use a configuration value or environment variable as part of a request. You can do this per
subgraph in the router configuration by using the $config
section of each
connectors subgraph:
1preview_connectors:
2 subgraphs:
3 name_of_the_subgraph:
4 $config:
5 name_of_the_variable: value
You can then access the value using the $config
variable in the schema, for example in the URL template or header of a
connector:
1type Query {
2 something: String!
3 @connect(
4 http: {
5 GET: "https://api.example.com/products/{$config.name_of_the_variable}"
6 headers: [
7 {
8 name: "Authorization"
9 value: "Bearer {$config.name_of_variable_containing_token}"
10 }
11 ]
12 }
13 selection: ""
14 )
15}
You can use $config
in the same locations as $this
and $args
.
You can also access nested values using dot notation. For example $config.nested.value
would refer to:
1preview_connectors:
2 subgraphs:
3 name_of_the_subgraph:
4 $config:
5 nested:
6 value: "some value"
Router configuration also lets you inject environment variables, like this:
1preview_connectors:
2 subgraphs:
3 name_of_the_subgraph:
4 $config:
5 name_of_the_variable: ${env.VARIABLE_NAME}
Request Limits
The maximum number of REST API requests for each GraphQL operation can be configured in the router. This can help
avoid overwhelming upstream services. Once the limit has been reached, a null
value will be returned in the
GraphQL result for any fields with @connect
directives whose requests were not executed. Additionally, a GraphQL
error will be returned in the errors
array of the response. Partial data may still be returned for portions of
the operation that were not affected by the limit.
The limit can be set generally in the router config YAML:
1preview_connectors:
2 max_requests_per_operation_per_source: 100
This will limit the number of requests made to each connector source for a given GraphQL operation. If a connector does not define a source, then this limit is applied at the connector level.
The limit can also be configured for each individual connector source:
1preview_connectors:
2 subgraphs:
3 subgraph_name:
4 sources:
5 source_name:
6 max_requests_per_operation: 50
Limits set on an individual source will override the general max_requests_per_operation_per_source
limit.
The limit can also be configured by setting the environment variable APOLLO_CONNECTORS_MAX_REQUESTS_PER_OPERATION
.
Any configuration in the YAML file will override the environment variable setting.
Telemetry
Router telemetry can be configured for connectors.
Attributes
Attributes can be attached to telemetry such as instruments and events. These attributes are used to filter and group data in your application performance monitor (APM).
The following standard attributes are available for connectors:
Attribute | Description |
---|---|
subgraph.name | The name of the subgraph containing the connector |
connector.source.name | The name of the @source associated with this connector, if any |
connector.http.method | The HTTP method for the connector (GET or POST , for example) |
connector.url.template | The URL template for the connector |
Selectors
A selector is used to extract data from connectors requests and responses and attach the data to telemetry such as instruments and events.
Apollo Connectors for REST APIs make HTTP calls to the upstream HTTP API. The selectors in the following table let you extract metrics from these HTTP requests and responses.
Selector | Defaultable | Values | Description |
---|---|---|---|
subgraph_name | No | true |false | The name of the subgraph containing the connector |
connector_source | No | name | The name of the @source associated with this connector, if any |
connector_http_request_header | Yes | The name of a connector request header | |
connector_http_response_header | Yes | The name of a connector response header | |
connector_http_response_status | No | code |reason | The status of a connector response |
connector_http_method | No | true |false | The HTTP method of a connector request |
connector_url_template | No | true |false | The URL template of a connector request |
static | No | A static string value | |
error | No | reason | A string value containing error reason when it's a critical error |
Instruments
An instrument in the router collects data and reports measurements to a metric backend. Supported instruments include standard instruments from OpenTelemetry, standard instruments for the router's request lifecycle, and custom instruments. Supported instrument kinds are counters and histograms.
You can configure instruments in router.yaml
with telemetry.instrumentation.instruments
.
OpenTelemetry standard instruments
OpenTelemetry specifies multiple standard metric instruments that are available for connectors HTTP requests and responses:
http.client.request.body.size
- A histogram of request body sizes for connectors HTTP requests.http.client.request.duration
- A histogram of request durations for connectors HTTP requests.http.client.response.body.size
- A histogram of response body sizes for connectors HTTP responses.
These instruments are configurable in router.yaml
:
1telemetry:
2 instrumentation:
3 instruments:
4 connector:
5 http.client.request.body.size: true
6 http.client.request.duration: true
7 http.client.response.body.size: true
The default_requirement_level
setting configures whether or not these instruments are enabled by default. They can be customized by attaching or removing attributes. See attributes to learn more about configuring attributes.
1telemetry:
2 instrumentation:
3 instruments:
4 connector:
5 http.client.request.duration:
6 attributes:
7 connector.source.name: true
Custom instruments
You can define custom instruments on connectors HTTP requests and responses.
For example, the following custom instrument provides the number of 404 response statuses from a specific REST API:
1telemetry:
2 instrumentation:
3 instruments:
4 connector:
5 acme.user.not.found:
6 value: unit
7 type: counter
8 unit: count
9 description: "Count of 404 responses from the user API"
10 condition:
11 all:
12 - eq:
13 - 404
14 - connector_http_response_status: code
15 - eq:
16 - "user_api"
17 - connector_source: name
See the router documentation for more details about configuring instruments.
Events
An event is used to signal when something of note happens, such as a connector request or response.
You can configure events for each service in router.yaml
. Events can be standard or custom, and they can be triggered by configurable conditions.
See the router documentation for more details about configuring events.
Standard events
Standard events can be configured for connectors. The following enables standard connector HTTP response events at the INFO
level:
1events:
2 connector:
3 request: off
4 response: info
5 error: error
Custom events
Custom events can also be configured for connectors. The following example defines a custom event for each connector HTTP response at the INFO
level:
1events:
2 connector:
3 connector.response:
4 message: "Connector response"
5 level: info
6 on: response
7 attributes:
8 connector.http.method: true
9 connector.url.template: true
10 response_status:
11 connector_http_response_status: code
If you have a stdout
logging exporter, the router will then log each connector response with the attributes defined above:
1INFO connector.http.method=GET connector.url.template=/users response_status=200 Connector response kind=connector.response
2INFO connector.http.method=GET connector.url.template=/users/{$this.id}/posts response_status=200 Connector response kind=connector.response
Authentication
Apollo Connectors can be used to call AWS HTTP APIs using AWS Signature Version 4 (SigV4). For example, you can use Apollo Connectors to invoke an AWS Lambda function and select fields from the JSON result to include in your GraphQL response:
1@source(
2 name: "lambda"
3 http: { baseURL: "https://lambda.us-east-1.amazonaws.com" }
4)
5...
6 @connect(
7 source: "lambda"
8 http: {
9 POST: "/2015-03-31/functions/function_name/invocations"
10 body: "argument: $this.function_argument"
11 }
12 selection: "$.function_output"
13 )
SigV4 authentication is configured separately for each connector source, allowing you to specify a role with the least-privilege necessary to invoke the AWS API for that source:
1authentication:
2 connector:
3 sources:
4 subgraph_name.connector_source_name:
5 aws_sig_v4:
6 default_chain:
7 profile_name: "default"
8 region: "us-east-1"
9 service_name: "lambda"
10 assume_role:
11 role_arn: "arn:aws:iam::XXXXXXXXXXXX:role/lambaexecute"
12 session_name: "connector"
Authentication with coprocessors
You can use coprocessors to fetch authentication tokens for connectors. This is useful when you need to fetch a token from a different source, such as a database or a third-party service, before making a request to an API.
Start by configuring the coprocessor for the Execution Request stage and enabling the expose_sources_in_context
feature of connectors:
1preview_connectors:
2 expose_sources_in_context: true
3coprocessor:
4 url: http://localhost:4001
5 execution:
6 request:
7 context: true
In the context
of the coprocessor request, you will find a list of subgraph and source names from the query plan. You can use this information to determine which identity providers (IDPs) to query for tokens.
1{
2 "version": 1,
3 "stage": "ExecutionRequest",
4 "control": "continue",
5 "id": "d0a8245df0efe8aa38a80dba1147fb2e",
6 "context": {
7 "entries": {
8 "apollo_connectors::sources_in_query_plan": [
9 { "subgraph_name": "products", "source_name": "v1" }
10 ]
11 }
12 }
13}
In the coprocessor response, you can add the API keys for each source to the context
:
1{
2 "version": 1,
3 "stage": "ExecutionRequest",
4 "control": "continue",
5 "id": "d0a8245df0efe8aa38a80dba1147fb2e",
6 "context": {
7 "entries": {
8 "apollo_connectors::sources_in_query_plan": [
9 { "subgraph_name": "products", "source_name": "v1" }
10 ],
11 "api_keys": {
12 "products_v1": "abcd1234"
13 }
14 }
15 }
16}
Then in the configuration for your connector source, you can use the keys from the context as header values:
1extend schema
2 @source(
3 name: "v1"
4 http: {
5 baseURL: "https://api.example.com/v1"
6 headers: [
7 {
8 name: "Authorization"
9 value: "Bearer {$context.api_keys.products_v1}"
10 }
11 ]
12 }
13 )