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
mutation
Artifact size
2.7 KB
Operation text
155 B
What the browser sends
45 B + vars

1 · You write

src/components/consistency/LikeButton.tsx
mutation LikeButtonMutation($input: LikePostInput!) {
  likePost(input: $input) {
    post {
      id
      likeCount
      viewerHasLiked
    }
  }
}

2 · Types you import

LikeButtonMutation.graphql.ts
export type LikePostInput = {
  clientMutationId?: string | null | undefined;
  like: boolean;
  postId: string;
};
export type LikeButtonMutation$variables = {
  input: LikePostInput;
};
export type LikeButtonMutation$data = {
  readonly likePost: {
    readonly post: {
      readonly id: string;
      readonly likeCount: number;
      readonly viewerHasLiked: boolean;
    } | null | undefined;
  } | null | undefined;
};
export type LikeButtonMutation = {
  response: LikeButtonMutation$data;
  variables: LikeButtonMutation$variables;
};

3 · Operation text, persisted at build time

md5 f543b7dcd38a0c08857aa177ddc22784

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

mutation LikeButtonMutation(
  $input: LikePostInput!
) {
  likePost(input: $input) {
    post {
      id
      likeCount
      viewerHasLiked
    }
  }
}

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<<765671a50e1e4fc8f55924bb4939435f>>
 * @relayHash f543b7dcd38a0c08857aa177ddc22784
 * @lightSyntaxTransform
 */

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

// @relayRequestID f543b7dcd38a0c08857aa177ddc22784

import { ConcreteRequest } from 'relay-runtime';
export type LikePostInput = {
  clientMutationId?: string | null | undefined;
  like: boolean;
  postId: string;
};
export type LikeButtonMutation$variables = {
  input: LikePostInput;
};
export type LikeButtonMutation$data = {
  readonly likePost: {
    readonly post: {
      readonly id: string;
      readonly likeCount: number;
      readonly viewerHasLiked: boolean;
    } | null | undefined;
  } | null | undefined;
};
export type LikeButtonMutation = {
  response: LikeButtonMutation$data;
  variables: LikeButtonMutation$variables;
};

const node: ConcreteRequest = (function(){
var v0 = [
  {
    "defaultValue": null,
    "kind": "LocalArgument",
    "name": "input"
  }
],
v1 = [
  {
    "alias": null,
    "args": [
      {
        "kind": "Variable",
        "name": "input",
        "variableName": "input"
      }
    ],
    "concreteType": "LikePostPayload",
    "kind": "LinkedField",
    "name": "likePost",
    "plural": false,
    "selections": [
      {
        "alias": null,
        "args": null,
        "concreteType": "Post",
        "kind": "LinkedField",
        "name": "post",
        "plural": false,
        "selections": [
          {
            "alias": null,
            "args": null,
            "kind": "ScalarField",
            "name": "id",
            "storageKey": null
          },
          {
            "alias": null,
            "args": null,
            "kind": "ScalarField",
            "name": "likeCount",
            "storageKey": null
          },
          {
            "alias": null,
            "args": null,
            "kind": "ScalarField",
            "name": "viewerHasLiked",
            "storageKey": null
          }
        ],
        "storageKey": null
      }
    ],
    "storageKey": null
  }
];
return {
  "fragment": {
    "argumentDefinitions": (v0/*:: as any*/),
    "kind": "Fragment",
    "metadata": null,
    "name": "LikeButtonMutation",
    "selections": (v1/*:: as any*/),
    "type": "Mutation",
    "abstractKey": null
  },
  "kind": "Request",
  "operation": {
    "argumentDefinitions": (v0/*:: as any*/),
    "kind": "Operation",
    "name": "LikeButtonMutation",
    "selections": (v1/*:: as any*/)
  },
  "params": {
    "id": "f543b7dcd38a0c08857aa177ddc22784",
    "metadata": {},
    "name": "LikeButtonMutation",
    "operationKind": "mutation",
    "text": null
  }
};
})();

(node as any).hash = "d90061819adec0adb5153b3b59f0392d";

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.