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:

GraphQL
products.graphql
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.

note
Only certain variables are available for URL templates. Refer to the variable reference's availability notes.

Where to use expressions

Expressions can appear anywhere in the path or query string of the URL:

GraphQL
products.graphql
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:

GraphQL
❌ This URL fails to build
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:

GraphQL
products.graphql
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:

GraphQL
❌ This URL fails to build
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=.

GraphQL
products.graphql
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:

GraphQL
products.graphql
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:

GraphQL
products.graphql
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.

note
A 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.

GraphQL
products.graphql
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.

GraphQL
products.graphql
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.

tip
It's common to use a singular name when repeating query parameters. The GraphQL parameter is named 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:

  1. Query parameters from @source(http.baseURL:)

  2. Query parameters from @source(http.queryParams:)

  3. Query parameters from @connect(http.<METHOD>:)

  4. Query parameters from @connect(http.queryParams:)

Example query parameters in @source and @connect

With this schema:

GraphQL
products.graphql
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.

note
The variables available in @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.

GraphQL
products.graphql
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:

Text
https://example.com/v1/products/available/reviews

All path segments are added before all query parameters in this order:

  1. Path segments from @source(http.baseURL:)

  2. Path segments from @source(http.path:)

  3. Path segments from @connect(http.<METHOD>:)

  4. 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 and offset parameters

  • Page-based pagination with page and size parameters

  • Cursor-based pagination with after or before tokens

Here's an example of implementing offset-based pagination:

GraphQL
products.graphql
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:

GraphQL
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.

Feedback

Ask Community