Building Request URLs
Learn how to make dynamic URLs with Apollo Connectors
Apollo Connectors let you dynamically build HTTP request URLs using GraphQL field arguments, sibling fields, and router configuration variables. In this guide, you'll learn how to:
Add dynamic path segments to your URLs
Set query parameters, including repeated and optional parameters
Share a base URL, path, or query parameters across multiple Connectors
Implement common URL patterns like pagination and batching
If you haven't already, read the overview of Making HTTP Requests to gain familiarity with basic Connectors first.
Dynamic expressions
Dynamic expressions let you add parameters to the path or query string of the URL.
You can use dynamic expressions in URL templates with curly braces ({}
).
For example, you can reference a GraphQL field argument using $args
:
1type Query {
2 product(id: ID!): Product # Notice "id" argument
3 @connect(
4 http: {
5 GET: "https://ecommerce.demo-api.apollo.dev/products/{$args.id}"
6 }
7 )
8}
In this example, when a client queries product(id: "abc123")
, the Connector makes an HTTP request to /products/abc123
.
Where to use expressions
Expressions can appear anywhere in the path or query string of the URL:
1@connect(
2 http: {
3 GET: """
4 https://ecommerce.demo-api.apollo.dev
5 /products?{$args.paramName}={$args.paramValue}
6 """
7 }
8)
They can't appear in earlier portions of the URL:
1@connect(
2 http: { GET: "https://{$args.userControlledDomain}" }
3)
Allowed expressions
Any mapping expression can be used within the curly braces ({}
) and is evaluated dynamically at runtime:
1@connect(
2 http: { GET: """
3 https://ecommerce.demo-api.apollo.dev/products
4 ?ids={$args.ids->joinNotNull(",")}
5 """
6 }
7)
The result of every expression is percent-encoded. In the example above, if $args.ids
is [1, 2, 3]
, the resulting URL is https://ecommerce.demo-api.apollo.dev/products?ids=1%2C2%2C3
.
Each expression must evaluate to a simple scalar value (string, number, boolean, etc.)—not an object or array:
1type Query {
2 products(ids: [ID!]): [Product]
3 @connect(
4 http: { GET: """
5 https://ecommerce.demo-api.apollo.dev/products
6 ?ids={$args.ids}
7 """
8 }
9 )
10}
This example fails to build because $args.ids
is an array.
Null values
Parameter names still appear in the URL even when the parameter value is missing or null
.
In this example, if availability
in {$args.availability}
is not provided or set to null
, the URL will end with ?availability=
.
1type Query {
2 productsByAvailability(availability: String): [Product] # availability is nullable
3 @connect(
4 http: { GET: """
5 https://ecommerce.demo-api.apollo.dev/products
6 ?availability={$args.availability}
7 """ }
8 )
9}
To omit parameter names for null
values, see omitting optional parameters.
Reusing URL pieces
As described in the request overview, you can share some common pieces of a URL across multiple Connectors with a @source
directive. For example, the two Connectors below build off the ecomm
source's baseURL
:
1extend schema
2@source(
3 name: "ecomm"
4 http: {baseURL: "https://ecommerce.demo-api.apollo.dev"}
5)
6
7type Query {
8 allProducts: [Product]
9 @connect(
10 source: "ecomm"
11 http: { GET: "/products"}
12 )
13 productsByAvailability(availability: String): [Product]
14 @connect(
15 source: "ecomm"
16 http: { GET: "/products?availability={$args.availability}" }
17 )
18}
The baseURL
can also contain common path segments:
1extend schema
2@source(
3 name: "products"
4 http: {baseURL: "https://ecommerce.demo-api.apollo.dev/products"}
5)
6
7type Query {
8 allProducts: [Product]
9 @connect(
10 source: "products"
11 http: { GET: "/"}
12 )
13 productsByAvailability(availability: String): [Product]
14 @connect(
15 source: "products"
16 http: { GET: "?availability={$args.availability}" }
17 )
18}
Connectors handle deduplication of /
and ?
and &
automatically.
baseURL
cannot contain dynamic expressions ({}
).Setting baseURL
dynamically
You'll commonly want the baseURL
within your schema to be the URL used for local development, and then override this with per-environment router config. You can change the baseURL
of a source using router configuration, which can in turn load from additional sources like environment variables. See Overriding Base URLs for an example.
Advanced query parameters
For more advanced use cases, http
has a queryParams
attribute for dynamically setting parameters.
It takes a mapping expression that evaluates to an object.
1type Query {
2 productsByAvailability(availability: String, limit: Int): [Product]
3 @connect(
4 source: "ecomm"
5 http: {
6 GET: "/products"
7 queryParams: "$args"
8 }
9 )
10}
Because queryParams
expects an object, you can use any mapping expression that evaluates to an object, including a
short expression like $args
. In this case, the query parameters availability
and limit
are both added to the URL,
resulting in something like /products?availability=AVAILABLE&limit=10
.
Omitting optional parameters
Any value that evaluates to null
in queryParams
will be entirely omitted from the URL.
In the above example, if availability
is null
, the resulting URL will be /products?limit=10
.
If both values are null
, the resulting URL will be /products
.
Repeated query parameters
Each value of the queryParams
object can be a single value or an array of values. In the case of an array, the
parameter name will be repeated for each value.
1type Query {
2 productsByAvailability(ids: [ID!]): [Product]
3 @connect(
4 source: "ecomm"
5 http: {
6 GET: "/products"
7 queryParams: "id: $args.ids"
8 }
9 )
10}
In this example, if $args.ids
evaluates to [1, 2, 3]
, the resulting URL will be /products?id=1&id=2&id=3
.
ids
(plural), but the example above maps that to id
(singular) in id: $args.ids
.Query parameters in @source
You can add queryParams
to http
in both @connect
and @source
.
Connectors combine query parameters in the resulting URL in the following order:
Query parameters from
@source(http.baseURL:)
Query parameters from
@source(http.queryParams:)
Query parameters from
@connect(http.<METHOD>:)
Query parameters from
@connect(http.queryParams:)
Example query parameters in @source
and @connect
With this schema:
1extend schema
2@source(
3 name: "ecomm"
4 http: {
5 baseURL: "https://ecommerce.demo-api.apollo.dev/products?availability=AVAILABLE"
6 queryParams: "limit: $(10)"
7 }
8)
9
10type Query {
11 allProducts: [Product]
12 @connect(
13 source: "ecomm"
14 http: {
15 GET: "/products?availability=UNAVAILABLE"
16 queryParams: """availability: $("PREORDER")"""
17 }
18 )
19}
The final URL is https://ecommerce.demo-api.apollo.dev/products?availability=AVAILABLE&limit=10&availability=UNAVAILABLE&availability=PREORDER
.
Parameters are never deduplicated; they are only added in order.
@source
are different from those in @connect
. Refer to the variable reference's availability notes.Dynamic path segments
Both @source
and @connect
support the path
field in their http
configuration.
This field accepts a mapping expression that evaluates to an array of scalars.
Each entry in the array is added to the URL as a path segment.
1extend schema
2@source(
3 name: "example"
4 http: {
5 baseURL: "https://example.com"
6 path: "$config.basePath"
7 }
8)
9
10type Query {
11 dynamicPath(path: [String!]): [Product]
12 @connect(
13 source: "example"
14 http: {
15 GET: "/"
16 path: "$args.path"
17 }
18 )
19}
If $config.basePath
is ["v1", "products"]
and $args.path
is ["available", "reviews"]
, the resulting URL will be:
https://example.com/v1/products/available/reviews
All path segments are added before all query parameters in this order:
Path segments from
@source(http.baseURL:)
Path segments from
@source(http.path:)
Path segments from
@connect(http.<METHOD>:)
Path segments from
@connect(http.path:)
Common URL patterns
Pagination
If your API supports pagination, for example, by supporting limit
and offset
query parameters, you can expose that functionality in your schema by defining field arguments and using them in your Connector URLs.
Connectors make it easy to expose these pagination controls in your GraphQL schema while also capturing pagination metadata like total counts.
Common pagination patterns you can implement include:
Offset-based pagination with
limit
andoffset
parametersPage-based pagination with
page
andsize
parametersCursor-based pagination with
after
orbefore
tokens
Here's an example of implementing offset-based pagination:
1type Query {
2 paginatedProducts(limit: Int = 10, offset: Int = 0): ProductsResult!
3 @connect(
4 source: "ecomm"
5 http: { GET: "/products?limit={$args.limit}&offset={$args.offset}" }
6 selection: """
7 # Map the array of products to a "results" field
8 results: $.products {
9 id
10 name
11 description
12 }
13 # Map the total count from the summary object
14 totalCount: summary.total
15 """
16 )
17}
18
19# Define a wrapper type to hold both results and pagination metadata
20type ProductsResult {
21 results: [Product]
22 totalCount: Int
23}
This Connector enables GraphQL queries like this:
1query GetSecondPageOfProducts {
2 paginatedProducts(limit: 10, offset: 10) {
3 results {
4 id
5 name
6 }
7 totalCount # Includes the total count of all products
8 }
9}
This query would retrieve products 11-20 (the second page) along with the total count of all available products, enabling clients to build pagination UI controls.
Additional resources
To experiment with URLs you are building, use Connector mode in the Connectors Mapping Playground.