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
8.0 KB
Operation text
709 B
What the browser sends
45 B + vars
1 · You write
src/components/colocation/ColocationDemo.tsxquery ColocationDemoQuery {
viewer {
...ViewerBar_user
}
feed(first: 2) {
edges {
node {
id
...PostCard_post
}
}
}
}2 · Types you import
ColocationDemoQuery.graphql.tsexport type ColocationDemoQuery$variables = Record<PropertyKey, never>;
export type ColocationDemoQuery$data = {
readonly feed: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly id: string;
readonly " $fragmentSpreads": FragmentRefs<"PostCard_post">;
} | null | undefined;
} | null | undefined> | null | undefined;
};
readonly viewer: {
readonly " $fragmentSpreads": FragmentRefs<"ViewerBar_user">;
};
};
export type ColocationDemoQuery = {
response: ColocationDemoQuery$data;
variables: ColocationDemoQuery$variables;
};3 · Operation text, persisted at build time
md5 a4aa638d35b2e5ad0025f6c3e8b59a10Fragments from every component are inlined into one document. It lives in persisted_queries.json on the server; the client only knows the hash.
query ColocationDemoQuery {
viewer {
...ViewerBar_user
id
}
feed(first: 2) {
edges {
node {
id
...PostCard_post
}
}
}
}
fragment AuthorLine_user on User {
name
username
avatarColor
}
fragment CommentItem_comment on Comment {
body
author {
...AuthorLine_user
id
}
}
fragment LikeBar_post on Post {
likeCount
viewerHasLiked
tags
}
fragment PostCard_post on Post {
title
excerpt
author {
...AuthorLine_user
id
}
...LikeBar_post
comments(first: 2) {
edges {
node {
id
...CommentItem_comment
}
}
}
}
fragment ViewerBar_user on User {
name
avatarColor
followerCount
}
4 · Runtime artifact
imported in place of the graphql`` tagA 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<<39575cbc946c0e6659b3e459eb08f9cd>>
* @relayHash a4aa638d35b2e5ad0025f6c3e8b59a10
* @lightSyntaxTransform
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
// @relayRequestID a4aa638d35b2e5ad0025f6c3e8b59a10
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type ColocationDemoQuery$variables = Record<PropertyKey, never>;
export type ColocationDemoQuery$data = {
readonly feed: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly id: string;
readonly " $fragmentSpreads": FragmentRefs<"PostCard_post">;
} | null | undefined;
} | null | undefined> | null | undefined;
};
readonly viewer: {
readonly " $fragmentSpreads": FragmentRefs<"ViewerBar_user">;
};
};
export type ColocationDemoQuery = {
response: ColocationDemoQuery$data;
variables: ColocationDemoQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"kind": "Literal",
"name": "first",
"value": 2
}
],
v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v2 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "avatarColor",
"storageKey": null
},
v4 = {
"alias": null,
"args": null,
"concreteType": "User",
"kind": "LinkedField",
"name": "author",
"plural": false,
"selections": [
(v2/*:: as any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "username",
"storageKey": null
},
(v3/*:: as any*/),
(v1/*:: as any*/)
],
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "ColocationDemoQuery",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "User",
"kind": "LinkedField",
"name": "viewer",
"plural": false,
"selections": [
{
"args": null,
"kind": "FragmentSpread",
"name": "ViewerBar_user"
}
],
"storageKey": null
},
{
"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*/),
{
"args": null,
"kind": "FragmentSpread",
"name": "PostCard_post"
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": "feed(first:2)"
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [],
"kind": "Operation",
"name": "ColocationDemoQuery",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "User",
"kind": "LinkedField",
"name": "viewer",
"plural": false,
"selections": [
(v2/*:: as any*/),
(v3/*:: as any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "followerCount",
"storageKey": null
},
(v1/*:: as any*/)
],
"storageKey": null
},
{
"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*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "title",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "excerpt",
"storageKey": null
},
(v4/*:: as any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "likeCount",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "viewerHasLiked",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "tags",
"storageKey": null
},
{
"alias": null,
"args": (v0/*:: 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": [
(v1/*:: as any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "body",
"storageKey": null
},
(v4/*:: as any*/)
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": "comments(first:2)"
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": "feed(first:2)"
}
]
},
"params": {
"id": "a4aa638d35b2e5ad0025f6c3e8b59a10",
"metadata": {},
"name": "ColocationDemoQuery",
"operationKind": "query",
"text": null
}
};
})();
(node as any).hash = "1b649ef2a32e7f627cb3ac122aa8bade";
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
datatype 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.