Making HTTP Requests
Learn how to make HTTP requests with Apollo Connectors
Apollo Connectors provide a declarative way to integrate REST API calls into your graph using the @connect
and @source
directives in your schemas.
In this guide, you'll learn the basics of making HTTP requests with Apollo Connectors.
It includes examples specifying the HTTP method and URL, setting headers, and defining request bodies.
HTTP Methods
You specify the HTTP method for your request in the http
argument of a @connect
directive. You must specify one of the following HTTP methods: GET
, POST
, PUT
, PATCH
, or DELETE
, followed by the URL.
Example GET request
1type Query {
2 products: [Product]
3 @connect(
4 http: { GET: "https://myapi.dev/products" }
5 selection: "id"
6 )
7}
Example POST request
1type Mutation {
2 createProduct(name: String!): Product
3 @connect(
4 http: {
5 POST: "https://myapi.dev/products"
6 body: "name: $args.name"
7 }
8 selection: "id"
9 )
10}
Example PUT request
1type Mutation {
2 setProduct(id: ID!, name: String!): Product
3 @connect(
4 http: {
5 PUT: "https://myapi.dev/products/{$args.id}"
6 body: """
7 {
8 name: $args.name,
9 description: $args.description,
10 price: $args.price,
11 inventory: $args.inventory
12 }
13 """
14 }
15 selection: "id"
16 )
17}
Example PATCH request
1type Mutation {
2 updateProduct(id: ID!, name: String!): Product
3 @connect(
4 http: {
5 PATCH: "https://myapi.dev/products/{$args.id}?name={$args.name}"
6 body: "name: $args.name"
7 }
8 selection: "id"
9 )
10}
Example DELETE request
1type Mutation {
2 deleteProduct(id: ID!): Product
3 @connect(
4 http: {
5 DELETE: "https://myapi.dev/products/{$args.id}"
6 }
7 selection: "id"
8 )
9}
Dynamic URLs
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.
The example below uses the productID
argument in the path:
1type Query {
2 product(storeId: ID!, productId: ID!): Product
3 @connect(
4 http: {
5 GET: "https://myapi.dev/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
.
Learn more about using arguments in your URLs, including as query parameters and for pagination.
Headers
You add headers to your HTTP requests using the http.headers
argument. Like with URIs, you can define header values using a combination of fixed values and dynamic mapping expressions in curly braces ({}
).
The following example uses a static value for the x-api-version
header and a dynamic value with the $config
variable for the Authorization
header:
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: "Authorization", value: "Bearer {$config.token}" }
9 ]
10 }
11 selection: "id"
12 )
13}
You can also propagate headers from the incoming client request using the from
argument:
1type Query {
2 products: [Product]
3 @connect(
4 http: { GET: "https://myapi.dev/products", headers: [{ name: "Authorization", from: "Authorization" }] }
5 selection: "id"
6 )
7}
Sharing configuration with @source
You can use the @source
directive to share a partial URL and headers with multiple Connectors.
Source definition
To define a @source
:
1extend schema
2 @link(
3 url: "https://specs.apollo.dev/connect/v0.1",
4 import: ["@connect", "@source"]
5 )
6 @source(
7 name: "myapi"
8 http: {
9 baseURL: "https://myapi.example.com/v1?client=router"
10 headers: [{ name: "X-API-Key", value: "{$config.api_key}" }]
11 }
12 )
Import the
@source
directive from the Connectors@link
. (line 4 above)Apply the
@source
directive to theschema
. (lines 6-12 above)Define a
baseURL
.Optionally define
headers
, which can't contain$this
or$args
.
Source usage
To use a @source
:
1
2type Query {
3 products(first: Int = 10): [Product]
4 @connect(
5 source: "myapi"
6 http: {
7 GET: "/products?first={$args.first}",
8 headers: [{ name: "X-Product-Type", from: "Product-Type" }]
9 }
10 selection: "id"
11 )
12}
Set the
source
field in each@connect
directive that should use the shared configuration. Use thename
defined in source definition.Use a partial URL in the
@connect
directive containing only the path and query parameters (no scheme, host, etc.).Define headers in
@connect
to override headers from the@source
with the samename
.
The Connector request above resolves to https://myapi.example.com/v1/products?client=router&first=10
.
It includes the X-API-Key
header from the @source
configuration and the X-Product-Type
header from the @connect
configuration.
Defining request bodies
The http.body
field defines a JSON body to send with the request using the mapping language.
1type Mutation {
2 createProduct(input: CreateProductInput!): Product
3 @connect(
4 http: {
5 POST: "https://myapi.dev/products"
6 body: """
7 $args.input {
8 name
9 price
10 }
11 """
12 }
13 selection: "id"
14 )
15}
Form URL encoding
By adding a Content-Type
header of exactly application/x-www-form-urlencoded
, GraphOS Router encodes the request body as a form URL encoded string.
1type Mutation {
2 createPost(input: CreatePostInput!): Post
3 @connect(
4 http: {
5 POST: "https://api.example.com/posts"
6 headers: [{ name: "Content-Type", value: "application/x-www-form-urlencoded" }]
7 }
8 selection: """
9 $args.input {
10 title
11 content
12 }
13 """
14 )
15}
The router first maps the request body to a JSON object:
{
"title": "Hello, world!",
"content": "This is a post."
}
Then, it encodes the object as a x-www-form-urlencoded
string:
title=Hello%2C+world%21&content=This+is+a+post.
Form URL encoding details
- List values are indexed starting from 0 using the
list[0]=value
syntax. - Nested objects use the
parent[child]=value
syntax. - Spaces are encoded as
+
.
1type Mutation {
2 example(input: ExampleInput!): Example
3 @connect(
4 http: { POST: "/example", headers: [{ name: "content-type", value: "application/x-www-form-urlencoded" }] }
5 selection: """
6 $args.input {
7 name
8 tags
9 addresses {
10 street
11 city
12 state
13 zip
14 }
15 }
16 """
17 )
18}
19
20input ExampleInput {
21 name: String!
22 tags: [String!]
23 addresses: [AddressInput!]
24}
25
26input AddressInput {
27 street: String!
28 city: String!
29 state: String!
30 zip: String!
31}
1name=Example
2&tags[0]=tag1
3&tags[1]=tag2
4&addresses[0][street]=123+Main+St
5&addresses[0][city]=Anytown
6&addresses[0][state]=CA
7&addresses[0][zip]=12345
8&addresses[1][street]=456+Elm+St
9&addresses[1][city]=Othertown
10&addresses[1][state]=NY
11&addresses[1][zip]=54321
Error handling
Following the GraphQL specification, if a Connector returns an error, the corresponding field in the response's data
is null
, and the error is logged in the errors
array. Connectors follow this convention for all non-20x responses.
See the common errors section on the troubleshooting page for information on composition errors.
Additional resources
Follow the guide on creating a Connector with arguments to see an example using the fictional ecommerce API used in the quickstart.