Making HTTP Requests

How to make HTTP requests with Apollo Connectors


Preview
Apollo Connectors are currently in public preview. To get started, you need an Apollo account with a GraphOS Trial or Enterprise plan.

Methods

Within the http argument of a @connect directive, you can specify the URL and query parameters for one of the following HTTP methods: GET, POST, PUT, PATCH, or DELETE.

GraphQL
Example connector with URL and query parameters
1type Query {
2  products: [Product]
3    @connect(
4      http: {
5        GET: "https://myapi.dev/products"
6      }
7      selection: "id"
8    )
9}
10
11type Mutation {
12  createProduct(input: CreateProductInput): CreateProductPayload
13    @connect(
14      http: {
15        POST: "https://myapi.dev/products"
16        body: "$args.input { name }"
17      }
18      selection: "id"
19    )
20
21  updateProduct(input: UpdateProductInput): UpdateProductPayload
22    @connect(
23      http: {
24        PUT: "https://myapi.dev/products/{$args.input.id}"
25        body: "$args.input { name }"
26      }
27      selection: "id"
28    )
29
30  patchProduct(input: PatchProductInput): PatchProductPayload
31    @connect(
32      http: {
33        PATCH: "https://myapi.dev/products/{$args.input.id}"
34        body: "$args.input { name }"
35      }
36      selection: "id"
37    )
38
39  deleteProduct(input: DeleteProductInput): DeleteProductPayload
40    @connect(
41      http: {
42        DELETE: "https://myapi.dev/products/{$args.input.id}"
43      }
44      selection: "id"
45    )
46}

URLs and query parameters

URLs can be dynamic and use values from field arguments, sibling fields, and router configuration. The dynamic parts appear between curly braces ({}). For example, you can use field arguments as path segments:

GraphQL
Example connector with dynamic path segments
1type Query {
2  product(storeId: ID!, productId: ID!): Product
3    @connect(
4      http: {
5        GET: "https://myapi.dev/store/{$args.storeId}/products/{$args.productId}"
6      }
7      selection: "id"
8    )
9}

Path segment values are URL-encoded. If the value contains a /, it is encoded as %2F.

You can add query parameters to the URL by appending them with a ? and separating them with &. You can use field arguments to create query parameters:

GraphQL
Example connector with dynamic query parameters
1type Query {
2  products(limit: Int = 10, offset: Int): Product
3    @connect(
4      http: {
5        GET: "https://myapi.dev/products?limit={$args.limit}&offset={$args.offset}"
6      }
7      selection: "id"
8    )
9}

When a value is missing or null, the result is an empty string. In the previous example, if offset in {$args.offset} is not provided, the URI will end with &offset=. Parameter names still appear in the URI if the parameter value is missing or `null``.

Disambiguating arguments and fields

You can reference both field arguments and sibling fields in the URL, even if they have the same name, via the $args and $this variables.

GraphQL
Example connector using $args and $this
1type Foo {
2  bar: ID
3  baz(bar: String): String
4    @connect(
5      http: { GET: "/foo/{$this.bar}?bar={$args.bar}" }
6      #                   ^ field         ^ argument
7    )
8}

For more information about variables, see the Variables section in the Mapping documentation.

Using baseURL in @source

When your connector has a related @source and the connector's URL doesn't start with http(s), the connector URL is appended to the baseURL of the @source. Query parameters in the baseURL are also included.

The connector URL below resolves to https://myapi.dev/v1/products?client=router&first=10.

GraphQL
Example connector with a related @source
1extend schema
2  @source(
3    name: "myapi"
4    http: { baseURL: "https://myapi.dev/v1?client=router" }
5  )
6
7type Query {
8  products(first: Int = 10): [Product]
9    @connect(
10      source: "myapi"
11      http: { GET: "/products?first={$args.first}" }
12      selection: "id"
13    )
14}

Headers

You can add headers to your HTTP requests using the headers argument in the http configuration. The headers argument is a list of objects.

You can define values for headers using strings as well as interpolate values from router configuration using the $config variable.

GraphQL
Example connector with headers
1type Query {
2  products: [Product]
3    @connect(
4      http: {
5        GET: "https://myapi.dev/products"
6        headers: [
7          { name: "x-api-version", value: "2024-01-01" }
8          { name: "x-api-key", value: "{$config.api_key}" }
9        ]
10      }
11      selection: "id"
12    )
13}

You can also propagate headers from the incoming client request using the from argument:

GraphQL
Example connector with client header propagation
1type Query {
2  products: [Product]
3    @connect(
4      http: {
5        GET: "https://myapi.dev/products"
6        headers: [{ name: "Authorization", from: "Authorization" }]
7      }
8      selection: "id"
9    )
10}
note
The router's header propagation configuration has no effect on connector requests.

Using headers in @source

When your connector has a related @source, the connector inherits its headers. When the name argument is the same, the headers in the @connect directive take precedence.

The connector below uses the x-api-key header from the related @source and overrides the x-api-version header with a different value.

GraphQL
Example connector with related @source
1extend schema
2  @source(
3    name: "myapi"
4    http: {
5      baseURL: "https://myapi.dev/v1"
6      headers: [
7        { name: "x-api-version", value: "2024-01-01" }
8        { name: "x-api-key", value: "{$config.api_key}" }
9      ]
10    }
11  )
12
13type Query {
14  products: [Product]
15    @connect(
16      source: "myapi"
17      http: {
18        GET: "/products"
19        headers: [{ name: "x-api-version", value: "2023-01-01" }]
20      }
21      selection: "id"
22    )
23}

Content Type

Connectors expect a JSON response body. If your API endpoint doesn't default to a JSON content type, you may need to specify an Accept: application/json header using the mechanisms described above.

Some APIs may have a different mechanism for specifying the response content type, such as a query parameter or file extension. Ensure that your connector is configured appropriately to request a JSON response.

Note that connectors do not require a Content-Type response header and interpret any response body as JSON by default.

Guidelines for using APIs with connectors

Connectors make it easy to build a GraphQL API using your existing HTTP/JSON APIs. They're flexible and work with most APIs, especially if they conform to the following guidelines.

JSON-over-HTTP basics

  1. Your endpoints accept requests using these HTTP verbs: GET, POST, PUT, PATCH, DELETE.

  2. Your endpoints use path segments and query string parameters for inputs, such as /users/123 or /users?limit=10.

  3. For POST, PUT, and PATCH requests with request bodies, your endpoints accept JSON.

  4. You endpoints respond with JSON values—typically objects, but any JSON value is allowed.

  5. Your endpoints always return a status code between 200 and 299 for successful requests.

  6. Your endpoints provide a known set of properties. GraphQL doesn't have a Map type, so you must define the mapping between JSON properties and GraphQL fields in advance. (You can map an arbitrary map to a scalar field using a custom scalar type.)

Graph-like conventions

  1. You represent your entities across various endpoints using the same identifiers. For example, a User is consistently identified by 123 across all endpoints.

  2. You provide endpoints to fetch an entity by its primary key. For example, /users/123 returns the user with ID 123.

  3. Your endpoints use simple values for foreign keys. For example, a User object has a companyId field containing the ID of the company it belongs to. (Using a full URL such as {"company": "http://myapi.com/company/234"} is difficult to work with).

  4. When appropriate, your endpoints embed related entities in the response. For example, a User object might include a company field that contains a Company object.

Security

  1. Your endpoints perform their own authentication and authorization checks as necessary. You can add layers of additional security using GraphOS Router's security features.

  2. Your endpoints accept authentication information in request headers. The headers can come directly from the client or be injected by the router.

  3. Your endpoints do not use query parameters for sensitive information. The router will emit full URLs in logs and traces.

When your APIs aren't a good fit for connectors

Apollo Connectors are designed to work with a wide variety of APIs, but there are some cases where they are not a good fit. The most common reason is that you need some business logic to transform or aggregate data from endpoints in order for them to compose cleanly into a unified GraphQL schema.

Fortunately, connectors and Apollo Federation work great together. You can combine GraphQL subgraphs with connectors to add business logic to your subgraph alongside declarative data fetching.

For example, if you have an API that returns a list but doesn't support filtering, you can use a resolver to fetch and filter the results and let the connector handle the details.

Feedback

Forums