Mapping GraphQL Responses

Mapping HTTP responses to GraphQL fields and transforming values


In this guide, you'll learn about:

  • Why connectors require mapping

  • Rules for mapping HTTP responses

  • Examples of common mapping types

  • How to transform values in your response data

tip
  • Just getting start with Apollo Connectors? Try out the quickstart to build your first connector.
  • Love developer tooling? Explore Apollo's IDE plugins for GraphQL development, including connectors support.

Mapping overview

Mapping HTTP responses to your GraphQL schema transforms the data returned by your REST APIs into a format that fits your GraphQL API structure. This ensures GraphQL requests can provide consistent, predictable responses to clients, even when working with data from different API sources.

You map an HTTP response to the GraphQL schema using the Apollo Connectors mapping language in the @connect directive's selection field. For that reason, this process is sometimes referred to as selection mapping. The mapping language you use for selection mapping is the same mapping language you use in URIs, headers, and request bodies when making HTTP requests.

tip
Check out the Connectors Mapping Playground to experiment and troubleshoot mapping expressions.

Unique features of selection mapping

In the context of selection mapping, the mapping language has an important, unique feature. When used in selection, it's assumed that all fields come from the HTTP response body unless otherwise specified. For example, given the following JSON response:

JSON
JSON Response
{
 "id": 1,
 "name": "Lunar Rover Wheels"
}

You can use the following selection to map the id and name fields:

GraphQL
Example connector with selection
type Query {
  products: Products
    @connect(
      source: "ecomm"
      http: { GET: "/products" }
      selection: "id name"
    )
}

type Product {
  id: ID!
  name: String
}

Multiline syntax

Long selection strings can be broken up into multiple lines with GraphQL multiline string syntax ("""):

GraphQL
Example: Multiline selection
type Query {
  products: Products
    @connect(
      source: "ecomm"
      http: { GET: "/products" }
      selection: """
      id
      name
      description
      """
    )
}

type Product {
  id: ID!
  name: String
  description: String
}

This is particularly valuable when you have a longer nested selection to map. The following example shows how each line in the selection translates a JSON response to fields in the GraphQL schema below.

/products/1
JSON
JSON Response
{
  "id": 1,
  "name": "Lunar Rover Wheels",
  "variants": [
    {
      "name": "Standard Wheel",
      "price": {
        "original": 4999,
        "discounts": [],
        "final": 4999
      },
      "specifications": {
        "Material": {
          "value": "Titanium alloy"
        },
        "Diameter": {
          "value": "50 cm"
        }
      },
      "inventory": {
        "quantity": 100,
        "sellUnavailable": false
      },
      "shipping": {
        "ship1": {
          "weight": 5,
          "method": "GROUND",
          "estimate": {
            "price": 499,
            "arrival": 1675804800000
          }
        },
        "ship2": {
          "weight": 5,
          "method": "AIR",
          "estimate": {
            "price": 999,
            "arrival": 1675790400000
          }
        }
      },
      "upc": "0001234567890",
      "sku": "RW-001",
      "taxable": true,
      "variantId": "variant1"
    }
  ]
}
GraphQL
Multiline nested selection
type Query {
  product(id: ID!): Product
    @connect(
      http: { GET: "https://ecommerce.demo-api.apollo.dev/products/{$args.id}" }
      selection: """
      id                   # 1
      variants {           # 2
        name               # 3
        price {            # 4             
          original         # 5           
          final            # 6
        }        
      }
      """
    )
}

type Product {
  id: ID!                 # 1
  variants: [Variant]     # 2
}

type Variant {
  name: String           # 3
  price: Price           # 4
}

type Price {
  original: Int          # 5
  final: Int             # 6
}

$status variable

In selection, you also get access to the $status variable, which isn't available anywhere else. $status represents the HTTP status code of a response.

Selection mapping rules

The selection field is responsible for more than just mapping response fields to the schema; it powers the core of each connector, so it has some special rules.

Selections can't be empty

The selection field isn't allowed to be empty. You must map at least one field in every connector. If you have an endpoint that doesn't return any response data, you can map a scalar value using a literal value:

connectors
success: $(true)

All schema fields must be mapped

The only way to populate a field from a connector is via selection, so every field defined in the schema must be mapped at least once in a connector. The exception is fields that are resolved from another subgraph, such as those marked @external.

Leaf nodes must be scalars

Different connectors can resolve different fields of the same object, so you must specify every field that a given connector resolves. That means you can never map an entire object and expect the fields to be implicitly mapped. You must map all fields explicitly.

GraphQL
type Query {
  product(id: ID!): Product
    @connect(
      source: "ecomm"
      http: { GET: "/products/{$args.id}" }
      selection: "id name description"
    )
}

type Product {
  id: ID!
  name: String!
  description: String
  reviews: [Review]
    @connect(
      source: "ecomm"
      http: {GET: "/products/{$this.id}/reviews"}
      # selection: "$.reviews"  ❌ This won't work
      selection: """          # ✅ This works
      $.reviews {
        id
        rating
        comment
      }
      """
    )
}

type Review {
  id: ID!
  rating: Float!
  comment: String
}

Even though the reviews field contains all the information needed from the first connector, you can't map just $.reviews. You must specify each field—id, rating, and comment—individually. Doing so This enables the query planner to know that the rating field must be fetched from elsewhere.

Selection mapping examples

The following are examples for commonly used selection mappings. See the Mapping Language reference for a complete overview of the mapping language's capabilities.

Basic selection mapping

Given the following JSON response:

JSON
JSON Response
{
  "id": 1,
  "name": "Lunar Rover Wheels",
  "description": "Innovatively designed wheels for lunar rovers, built to endure harsh moon terrain and provide optimal agility. Each wheel is constructed using advanced materials to withstand temperature fluctuations and dust."
}

You can create a basic, flat GraphQL type with fields that map to REST endpoint fields of the same names:

GraphQL
Example: basic selection
type Query {
  product(id: ID!): Product
    @connect(
      source: "ecomm"
      http: { GET: "/products/{$args.id}" }
      # The REST endpoint returns "id", "name", and "description"
      # in its response, and they're mapped directly to fields of
      # the same name in the GraphQL schema.
      selection: "id name description"
    )
}

type Product {
  id: ID!
  name: String!
  description: String!
}

Renaming fields

Given the following JSON response:

JSON
JSON Response
{
  "product_id": "1",
  "title": "Lunar Rover Wheels"
}

You can map a JSON response field to a schema field of a different name using the same syntax as GraphQL aliases. The desired name (the one present in the schema type) comes first followed by a colon (:) and the name of the field in the response: desiredName: original_name

GraphQL
Example: renaming fields
type Query {
  product(id: ID!): Product
    @connect(
      source: "ecomm"
      http: { GET: "/products/{$args.id}" }
      selection: """
      id: product_id
      name: title
      """
    )
}

type User {
  id: ID!
  name: String!
}

Unwrapping fields

Suppose the JSON response includes nesting that you don't need in your schema:

JSON
JSON Response
{
  "result": {
    "id": "1",
    "name": {
      "value": "Lunar Rover Wheels"
    },
    "specifications": {
      "material": "Titanium alloy",
      "diameter": "50 cm"
    }
  }
}

You can "unwrap" fields using the . prefix:

GraphQL
Example: unwrapping
type Query {
  product(id: ID!): Product
    @connect(
      source: "ecomm"
      http: { GET: "/products/{$args.id}" }
      selection: """
      $.result {
        id
        name: name.value
        $.specifications {
          material
          diameter
        }
      }
      """
    )
}

type Product {
  id: ID!
  name: String!
  material: String
  diameter: String
}

Using $ when unwrapping

A leading $. is required when unwrapping a single property. Without $., it is interpreted as mapping the field to create an object. With $., it is interpreted as mapping the value.

For example, given the following JSON:

JSON
{ "name": "Lunar Rover Wheels" }

The following selections have the corresponding results:

SelectionResult
name{ "name": "Lunar Rover Wheels" }
$.name"Lunar Rover Wheels"
product_name: name{ "product_name": "Lunar Rover Wheels" }

When selecting a path of properties, such as name.value, the $. is allowed but not required:

JSON
{ "name": { "value": "Lunar Rover Wheels" } }
SelectionResult
$.name.value"Lunar Rover Wheels"
name.value"Lunar Rover Wheels"
$.name { value }{ "value": "Lunar Rover Wheels" }
name { value }{ "name": { "value": "Lunar Rover Wheels" } }

The simple form also applies when using value transformations. These are equivalent:

SelectionResult
name->match(["Lunar Rover Wheels", "Wheels"], ["Zero-Gravity Moon Boots", "Boots"]){ "name": "Wheels" }
$.name->match(["Lunar Rover Wheels", "Wheels"], ["Zero-Gravity Moon Boots", "Boots"]){ "name": "Wheels" }

Wrapping fields

You can create nested fields from a flat structure using a variation on the alias syntax. This is especially useful for converting a simple foreign key into an entity reference. If the foreign keys are in a list, you can use the $ symbol to refer to items in the list.

For example, given the following JSON response:

JSON
JSON Response
{
  "id": "1",
  "brand_id": "2",
  "variant_ids": ["3", "4"]
}

You can create the desired structure using curly braces ({}) and $:

GraphQL
Example: wrapping fields
type Query {
  user(id: ID!): Product
    @connect(
      source: "ecomm"
      http: { GET: "/products/{$args.id}" }
      selection: """
      id
      brand: { id: brand_id }
      variants: $.variant_ids { id: $ }
      """
    )
}

type Product {
  id: ID!
  brand: Brand
  variant: [Variant]
}

type Brand {
  id: ID!
}

type Variant {
  id: ID!
}

Accessing fields that start with a numerical value

Field names that start with a numerical value must be put in quotes (" ").

For example, given the following JSON response that includes a specifications.3DModel field:

JSON
JSON Response
{
  "id": 1,
  "name": "Lunar Rover Wheels",
  "specifications": {
    "material": "Titanium alloy",
    "diameter": "50 cm",
    "3DModel": "https://example.com/lunar-rover-wheel-3d"
  }
}

You can map the field like so:

GraphQL
Example: field name with numeric starting character
1type Query {
2  product(id: ID!): Product
3    @connect(
4      source: "ecomm"
5      http: { GET: "/products/{$args.id}" }
6      selection: """
7      id
8      modelUrl: specifications."3DModel"
9      """
10    )
11}
12
13type Product {
14  id: ID!
15  modelUrl: String
16}
note
This example also unwraps and renames the specifications.3DModel field to top-level modelUrl field.

Arrays

Mapping arrays happens automatically, so you must ensure that your schema uses list types appropriately.

Given the following JSON response:

JSON
JSON Response
{
  "results": [
    {
      "id": "1",
      "variants": [
        { "id": "1", "color": "Silver" },
        { "id": "2", "color": "Platinum" }
      ],
      "reviews": ["Best purchase ever!", "Good value"]
    }
  ]
}

You can use the following selection mapping:

GraphQL
Example: wrapping fields
1type Query {
2  products: [Product]   # list 1
3    @connect(
4      http: { GET: "https://ecommerce.demo-api.apollo.dev/products" }
5      selection: """
6      $.products {                    # list 1
7        id
8        variants {                    # list 2
9          id
10          type: color
11        }
12        reviews                      # list 3
13      }
14      """
15    )
16}
17
18type User {
19  id: ID!
20  variants: [Variant]   # list 2
21  reviews: [String]     # list 3
22}
23
24type Variant {
25  id: ID!
26  color: String
27}

Complex nested selection

A complex, nested GraphQL type, Product, maps its fields from a REST endpoint returning multiple nested objects.

See JSON Response
JSON
{
  "id": 1,
  "name": "Lunar Rover Wheels",
  "createdAt": 1675200000000,
  "updatedAt": 1675200000000,
  "description": "Innovatively designed wheels for lunar rovers, built to endure harsh moon terrain and provide optimal agility. Each wheel is constructed using advanced materials to withstand temperature fluctuations and dust.",
  "slug": "lunar-rover-wheels",
  "tags": [
    { "tagId": "1", "name": "Instruments" },
    { "tagId": "2", "name": "Space" }
  ],
  "category": "Engineering Components",
  "availability": "AVAILABLE",
  "variants": [
    {
      "name": "Standard Wheel",
      "price": {
        "original": 4999,
        "discounts": [],
        "final": 4999
      },
      "specifications": {
        "Material": { "value": "Titanium alloy" },
        "Diameter": { "value": "50 cm" }
      },
      "inventory": { "quantity": 100, "sellUnavailable": false },
      "variantId": "variant1"
    }
  ]
}
GraphQL
1type Query {
2  product(id: ID!): Product
3    @connect(
4      http: { path: "https://ecommerce.demo-api.apollo.dev/products/{$args.id}" }
5      selection: """
6      id
7      name
8      description
9      availability
10      createdAt
11      updatedAt
12      tags: $.tags {
13        id: tagId
14        name
15      }
16      variants: $.variants {
17        name
18        taxable
19        price {
20          original
21          final
22        }
23        specifications: $.specifications {
24          material: Material.value
25          diameter: Diameter.value
26        }
27        inventory {
28          quantity
29          sellUnavailable
30        }
31      }
32      """
33    )
34}
35
36type Product {
37  id: ID!
38  name: String!
39  createdAt: Float
40  updatedAt: Float
41  description: String!
42  availability: String!
43  tags: [Tag]
44  variants: [Variant]
45}
46
47type Tag {
48  id: ID!
49  name: String!
50}
51
52type Variant {
53  name: String!
54  price: Price
55  specifications: Specifications
56  inventory: Inventory
57}
58
59type Price {
60  original: Int
61  final: Int
62}
63
64type Specifications {
65  material: String
66  diameter: String
67}
68
69type Inventory {
70  quantity: Int
71  sellUnavailable: Boolean
72}

Transforming values

As part of your selection mapping, you can transform values in your response using the mapping language's methods. Methods use the ->method syntax. See the mapping language methods reference for the complete list of available methods.

Enum value mapping

Enum value mapping is helpful when your response data uses values that don't match the exact format or case sensitivity required by your GraphQL schema.

The example below uses the ->match method to transform status values from active to ACTIVE and not active to INACTIVE.

GraphQL
Selection mapping snippet
status: status->match(
  ["active", "ACTIVE"],
  ["not active", "INACTIVE"],
  [@, "UNKNOWN"] # fallback — the value always matches `@`
)

Using the above transformation on the following response data yields the following results:

JSON
Response data
{
  "status": "active"
}
JSON
Result
{
  "status": "ACTIVE"
}
JSON
Response data
{
  "status": "none of the above"
}
JSON
Result
{
  "status": "UNKNOWN"
}

If a match isn't found, the result will be omitted and the field will be null if nullable or result in a validation error if non-nullable. If you want to avoid this, you can use the @ variable to provide a fallback, as shown in the example above.

Convert a map into a list of key-value pairs

Converting a map into a list of key-value pairs is particularly useful when you need to work with data in a more structured or iterable format. For example, in a frontend application, you might want to render a list of items, such as color names and their corresponding hex codes.

The example below uses the ->entries method to convert a map of color names and hex codes into a list of objects. You can use the following selection mapping snippet:

GraphQL
Selection mapping snippet
colors: colors->entries

To transform response data like this:

JSON
Response data
{
  "colors": {
    "red": "#ff0000",
    "green": "#00ff00",
    "blue": "#0000ff"
  }
}
JSON
Result
{
  "colors": [
    { "key": "red", "value": "#ff0000" },
    { "key": "green", "value": "#00ff00" },
    { "key": "blue", "value": "#0000ff" }
  ]
}

To use different names for keys and values, select the fields with aliases:

GraphQL
Selection mapping snippet
colors: colors->entries {
  name: key
  hex: value
}
JSON
Response data
{
  "colors": {
    "red": "#ff0000",
    "green": "#00ff00",
    "blue": "#0000ff"
  }
}
JSON
Result
{
  "colors": [
    { "name": "red", "hex": "#ff0000" },
    { "name": "green", "hex": "#00ff00" },
    { "name": "blue", "hex": "#0000ff" }
  ]
}

List management

Various methods, including ->first, ->last, ->slice, and ->size, let you transform lists.

For example, to transform a list into its first value, use ->first like so:

GraphQL
Selection mapping snippet
color->first
JSON
Response data
{
  "color": [
    "red",
    "green",
    "blue"
  ]
}
JSON
Result
{
  "color": "red"
}

To wrap a single item in a list, you can use a literal list and select the property:

GraphQL
Selection mapping snippet
$([$.color])
JSON
Response data
{
  "color": "red"
}
JSON
Result
{
  "color": ["red"]
}

Additional resources

Refer to the mapping language reference for a complete overview of mapping syntax and usage.

odyssey tutorial
If you learn best with videos and exercises, this interactive course teaches the syntax and methods of the connectors mapping language.
Feedback

Forums