06

The compiler does the work before your app runs

relay-compiler reads every graphql tag in the project, validates it against schema.graphql, and writes an artifact per fragment and operation: a precomputed runtime AST, TypeScript types, and (here) a persisted query id. Pick any artifact this app generated.
Kind
query
Artifact size
9.2 KB
Operation text
424 B
What the browser sends
45 B + vars

1 · You write

src/components/store/StoreDemo.tsx
query StoreDemoQuery {
  feed(first: 3) {
    edges {
      node {
        title
        likeCount
        author {
          name
          avatarColor
        }
        comments(first: 2) {
          edges {
            node {
              body
              author {
                name
              }
            }
          }
        }
      }
    }
  }
}

2 · Types you import

StoreDemoQuery.graphql.ts
export type StoreDemoQuery$variables = Record<PropertyKey, never>;
export type StoreDemoQuery$data = {
  readonly feed: {
    readonly edges: ReadonlyArray<{
      readonly node: {
        readonly author: {
          readonly avatarColor: string;
          readonly name: string;
        };
        readonly comments: {
          readonly edges: ReadonlyArray<{
            readonly node: {
              readonly author: {
                readonly name: string;
              };
              readonly body: string;
            } | null | undefined;
          } | null | undefined> | null | undefined;
        };
        readonly likeCount: number;
        readonly title: string;
      } | null | undefined;
    } | null | undefined> | null | undefined;
  };
};
export type StoreDemoQuery = {
  response: StoreDemoQuery$data;
  variables: StoreDemoQuery$variables;
};

3 · Operation text, persisted at build time

md5 83e894a00401b34f38ac77350005e8c7

Fragments from every component are inlined into one document. It lives in persisted_queries.json on the server; the client only knows the hash.

query StoreDemoQuery {
  feed(first: 3) {
    edges {
      node {
        title
        likeCount
        author {
          name
          avatarColor
          id
        }
        comments(first: 2) {
          edges {
            node {
              body
              author {
                name
                id
              }
              id
            }
          }
        }
        id
      }
    }
  }
}

4 · Runtime artifact

imported in place of the graphql`` tag

A preprocessed AST, so there's no GraphQL parsing in the browser. The reader part tells useFragment what to read; the normalization part tells the store how to write.

/**
 * @generated SignedSource<<ec5f9d73b57b289a12041dd9647e6e41>>
 * @relayHash 83e894a00401b34f38ac77350005e8c7
 * @lightSyntaxTransform
 */

/* tslint:disable */
/* eslint-disable */
// @ts-nocheck

// @relayRequestID 83e894a00401b34f38ac77350005e8c7

import { ConcreteRequest } from 'relay-runtime';
export type StoreDemoQuery$variables = Record<PropertyKey, never>;
export type StoreDemoQuery$data = {
  readonly feed: {
    readonly edges: ReadonlyArray<{
      readonly node: {
        readonly author: {
          readonly avatarColor: string;
          readonly name: string;
        };
        readonly comments: {
          readonly edges: ReadonlyArray<{
            readonly node: {
              readonly author: {
                readonly name: string;
              };
              readonly body: string;
            } | null | undefined;
          } | null | undefined> | null | undefined;
        };
        readonly likeCount: number;
        readonly title: string;
      } | null | undefined;
    } | null | undefined> | null | undefined;
  };
};
export type StoreDemoQuery = {
  response: StoreDemoQuery$data;
  variables: StoreDemoQuery$variables;
};

const node: ConcreteRequest = (function(){
var v0 = [
  {
    "kind": "Literal",
    "name": "first",
    "value": 3
  }
],
v1 = {
  "alias": null,
  "args": null,
  "kind": "ScalarField",
  "name": "title",
  "storageKey": null
},
v2 = {
  "alias": null,
  "args": null,
  "kind": "ScalarField",
  "name": "likeCount",
  "storageKey": null
},
v3 = {
  "alias": null,
  "args": null,
  "kind": "ScalarField",
  "name": "name",
  "storageKey": null
},
v4 = {
  "alias": null,
  "args": null,
  "kind": "ScalarField",
  "name": "avatarColor",
  "storageKey": null
},
v5 = [
  {
    "kind": "Literal",
    "name": "first",
    "value": 2
  }
],
v6 = {
  "alias": null,
  "args": null,
  "kind": "ScalarField",
  "name": "body",
  "storageKey": null
},
v7 = {
  "alias": null,
  "args": null,
  "kind": "ScalarField",
  "name": "id",
  "storageKey": null
};
return {
  "fragment": {
    "argumentDefinitions": [],
    "kind": "Fragment",
    "metadata": null,
    "name": "StoreDemoQuery",
    "selections": [
      {
        "alias": null,
        "args": (v0/*:: as any*/),
        "concreteType": "PostConnection",
        "kind": "LinkedField",
        "name": "feed",
        "plural": false,
        "selections": [
          {
            "alias": null,
            "args": null,
            "concreteType": "PostEdge",
            "kind": "LinkedField",
            "name": "edges",
            "plural": true,
            "selections": [
              {
                "alias": null,
                "args": null,
                "concreteType": "Post",
                "kind": "LinkedField",
                "name": "node",
                "plural": false,
                "selections": [
                  (v1/*:: as any*/),
                  (v2/*:: as any*/),
                  {
                    "alias": null,
                    "args": null,
                    "concreteType": "User",
                    "kind": "LinkedField",
                    "name": "author",
                    "plural": false,
                    "selections": [
                      (v3/*:: as any*/),
                      (v4/*:: as any*/)
                    ],
                    "storageKey": null
                  },
                  {
                    "alias": null,
                    "args": (v5/*:: as any*/),
                    "concreteType": "CommentConnection",
                    "kind": "LinkedField",
                    "name": "comments",
                    "plural": false,
                    "selections": [
                      {
                        "alias": null,
                        "args": null,
                        "concreteType": "CommentEdge",
                        "kind": "LinkedField",
                        "name": "edges",
                        "plural": true,
                        "selections": [
                          {
                            "alias": null,
                            "args": null,
                            "concreteType": "Comment",
                            "kind": "LinkedField",
                            "name": "node",
                            "plural": false,
                            "selections": [
                              (v6/*:: as any*/),
                              {
                                "alias": null,
                                "args": null,
                                "concreteType": "User",
                                "kind": "LinkedField",
                                "name": "author",
                                "plural": false,
                                "selections": [
                                  (v3/*:: as any*/)
                                ],
                                "storageKey": null
                              }
                            ],
                            "storageKey": null
                          }
                        ],
                        "storageKey": null
                      }
                    ],
                    "storageKey": "comments(first:2)"
                  }
                ],
                "storageKey": null
              }
            ],
            "storageKey": null
          }
        ],
        "storageKey": "feed(first:3)"
      }
    ],
    "type": "Query",
    "abstractKey": null
  },
  "kind": "Request",
  "operation": {
    "argumentDefinitions": [],
    "kind": "Operation",
    "name": "StoreDemoQuery",
    "selections": [
      {
        "alias": null,
        "args": (v0/*:: as any*/),
        "concreteType": "PostConnection",
        "kind": "LinkedField",
        "name": "feed",
        "plural": false,
        "selections": [
          {
            "alias": null,
            "args": null,
            "concreteType": "PostEdge",
            "kind": "LinkedField",
            "name": "edges",
            "plural": true,
            "selections": [
              {
                "alias": null,
                "args": null,
                "concreteType": "Post",
                "kind": "LinkedField",
                "name": "node",
                "plural": false,
                "selections": [
                  (v1/*:: as any*/),
                  (v2/*:: as any*/),
                  {
                    "alias": null,
                    "args": null,
                    "concreteType": "User",
                    "kind": "LinkedField",
                    "name": "author",
                    "plural": false,
                    "selections": [
                      (v3/*:: as any*/),
                      (v4/*:: as any*/),
                      (v7/*:: as any*/)
                    ],
                    "storageKey": null
                  },
                  {
                    "alias": null,
                    "args": (v5/*:: as any*/),
                    "concreteType": "CommentConnection",
                    "kind": "LinkedField",
                    "name": "comments",
                    "plural": false,
                    "selections": [
                      {
                        "alias": null,
                        "args": null,
                        "concreteType": "CommentEdge",
                        "kind": "LinkedField",
                        "name": "edges",
                        "plural": true,
                        "selections": [
                          {
                            "alias": null,
                            "args": null,
                            "concreteType": "Comment",
                            "kind": "LinkedField",
                            "name": "node",
                            "plural": false,
                            "selections": [
                              (v6/*:: as any*/),
                              {
                                "alias": null,
                                "args": null,
                                "concreteType": "User",
                                "kind": "LinkedField",
                                "name": "author",
                                "plural": false,
                                "selections": [
                                  (v3/*:: as any*/),
                                  (v7/*:: as any*/)
                                ],
                                "storageKey": null
                              },
                              (v7/*:: as any*/)
                            ],
                            "storageKey": null
                          }
                        ],
                        "storageKey": null
                      }
                    ],
                    "storageKey": "comments(first:2)"
                  },
                  (v7/*:: as any*/)
                ],
                "storageKey": null
              }
            ],
            "storageKey": null
          }
        ],
        "storageKey": "feed(first:3)"
      }
    ]
  },
  "params": {
    "id": "83e894a00401b34f38ac77350005e8c7",
    "metadata": {},
    "name": "StoreDemoQuery",
    "operationKind": "query",
    "text": null
  }
};
})();

(node as any).hash = "57f2951bf268ee047c1159dbe2fc8bc4";

export default node;
  • why it mattersA typo in a field name fails the build, not the user's session. Every fragment is checked against the schema on every save.
  • why it mattersTypes come from the fragment rather than the whole schema, so a component's data type has exactly the fields it asked for.
  • why it mattersPersisted queries ship for free: smaller requests, cacheable GETs, and a server that can refuse any query it didn't compile.