Golden cases
The compiler's behavior catalog, generated directly from packages/compiler/test/fixtures/ — every case below is a real golden fixture: input.tsx plus the expected operation, variables factory, read map and diagnostics, asserted byte-for-byte through BOTH type-checker engines and validated against the schema with graphql-js.
The fixtures import from a ~/graph test alias — the analyzer recognizes the accessor by binding, not by module path, so the harness keeps it framework-neutral. In an app you import from @gleanql/client / @gleanql/client/schema, as shown in Get started.
Basic Root
01-basic-root
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return <ProductHero product={product} />;
}
function ProductHero({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
}
}Variables factory
export function getProductRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"ProductHero": [
"Product.title"
]
}Deduped
02-deduped
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return (
<>
<ProductHero product={product} />
<Breadcrumb product={product} />
</>
);
}
function ProductHero({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}
function Breadcrumb({ product }: { product: Product }) {
return <span>{product.title}</span>;
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
}
}Read map
{
"ProductHero": [
"Product.title"
],
"Breadcrumb": [
"Product.title"
]
}Nested Merge
03-nested-merge
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return <ProductHero product={product} />;
}
function ProductHero({ product }: { product: Product }) {
return (
<>
<img src={product.featuredImage?.url} />
<span>{product.featuredImage?.altText}</span>
</>
);
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
featuredImage {
__typename
url
altText
}
}
}Read map
{
"ProductHero": [
"Product.featuredImage.url",
"Product.featuredImage.altText"
]
}Alias Tracking
04-alias-tracking
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return <BuyBox product={product} />;
}
function BuyBox({ product }: { product: Product }) {
const price = product.priceRange.minVariantPrice;
return (
<button>
{price.amount} {price.currencyCode}
</button>
);
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
priceRange {
__typename
minVariantPrice {
__typename
amount
currencyCode
}
}
}
}Read map
{
"BuyBox": [
"Product.priceRange.minVariantPrice.amount",
"Product.priceRange.minVariantPrice.currencyCode"
]
}Destructuring
05-destructuring
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return <ProductHero product={product} />;
}
function ProductHero({ product }: { product: Product }) {
const { title, featuredImage } = product;
return (
<>
<h1>{title}</h1>
<img src={featuredImage?.url} />
</>
);
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
featuredImage {
__typename
url
}
}
}Read map
{
"ProductHero": [
"Product.title",
"Product.featuredImage.url"
]
}Multiple Roots
06-multiple-roots
Input — input.tsx
import { glean } from "~/graph";
import type { Product, Cart } from "~/graph/schema";
export default function Route({ params }: { params: { handle: string; cartId: string } }) {
const product = glean.product({ handle: params.handle });
const cart = glean.cart({ id: params.cartId });
return (
<>
<ProductHero product={product} />
<CartSummary cart={cart} />
</>
);
}
function ProductHero({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}
function CartSummary({ cart }: { cart: Cart }) {
return <span>{cart.totalQuantity}</span>;
}Generated GraphQL
query Route($handle: String!, $id: ID!) {
product(handle: $handle) {
__typename
id
title
}
cart(id: $id) {
__typename
id
totalQuantity
}
}Variables factory
export function getRouteVariables(ctx) {
return {
handle: ctx.params.handle,
id: ctx.params.cartId,
};
}Read map
{
"ProductHero": [
"Product.title"
],
"CartSummary": [
"Cart.totalQuantity"
]
}Callable Args
07-callable-args
Input — input.tsx
import { glean } from "~/graph";
import type { Collection, Product } from "~/graph/schema";
export default function Route({ params }: { params: { handle: string } }) {
const collection = glean.collection({ handle: params.handle });
return <ProductList collection={collection} />;
}
function ProductList({ collection }: { collection: Collection }) {
return collection.products({ first: 12 }).nodes.map((product) => (
<ProductCard product={product} />
));
}
function ProductCard({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}Generated GraphQL
query Route($handle: String!) {
collection(handle: $handle) {
__typename
id
products(first: 12) {
__typename
nodes {
__typename
id
title
}
}
}
}Read map
{
"ProductCard": [
"Product.title"
]
}Conflicting Args
08-conflicting-args
Input — input.tsx
import { glean } from "~/graph";
import type { Collection, Product } from "~/graph/schema";
export default function Route({ params }: { params: { handle: string } }) {
const collection = glean.collection({ handle: params.handle });
return <ProductList collection={collection} />;
}
function ProductList({ collection }: { collection: Collection }) {
const first = collection.products({ first: 12 }).nodes;
const more = collection.products({ first: 24 }).nodes;
return (
<>
{first.map((product) => (
<ProductCard product={product} />
))}
{more.map((product) => (
<ProductCard product={product} />
))}
</>
);
}
function ProductCard({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}Generated GraphQL
query Route($handle: String!) {
collection(handle: $handle) {
__typename
id
products_first12: products(first: 12) {
__typename
nodes {
__typename
id
title
}
}
products_first24: products(first: 24) {
__typename
nodes {
__typename
id
title
}
}
}
}Read map
{
"ProductCard": [
"Product.title"
]
}Object Truthiness
09-object-truthiness
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return <ProductImageBadge product={product} />;
}
function ProductImageBadge({ product }: { product: Product }) {
if (!product.featuredImage) {
return null;
}
return <span>Has image</span>;
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
featuredImage {
__typename
}
}
}Read map
{}Union Narrowing
10-union-narrowing
Input — input.tsx
import { glean } from "~/graph";
import type { Collection, Product, SearchResultItem } from "~/graph/schema";
export default function Route({ params }: { params: { query: string } }) {
const results = glean.search({ query: params.query });
return results.nodes.map((node) => <SearchResult node={node} />);
}
function SearchResult({ node }: { node: SearchResultItem }) {
if (node.__typename === "Product") {
return <ProductCard product={node} />;
}
if (node.__typename === "Collection") {
return <CollectionCard collection={node} />;
}
return null;
}
function ProductCard({ product }: { product: Product }) {
return (
<>
<h1>{product.title}</h1>
<img src={product.featuredImage?.url} />
</>
);
}
function CollectionCard({ collection }: { collection: Collection }) {
return (
<>
<h1>{collection.title}</h1>
<img src={collection.image?.url} />
</>
);
}Generated GraphQL
query Route($query: String!) {
search(query: $query) {
__typename
nodes {
__typename
... on Product {
__typename
id
title
featuredImage {
__typename
url
}
}
... on Collection {
__typename
id
title
image {
__typename
url
}
}
}
}
}Read map
{
"ProductCard": [
"Product.title",
"Product.featuredImage.url"
],
"CollectionCard": [
"Collection.title",
"Collection.image.url"
]
}Dynamic Field Access
11-dynamic-field-access
Input — input.tsx
import type { Product } from "~/graph/schema";
export function ProductDebug({ product, fieldName }: { product: Product; fieldName: string }) {
return <pre>{product[fieldName]}</pre>;
}Diagnostics
[dynamic-field-access]
Cannot compile dynamic graph field access product[fieldName].
Graph fields must be accessed with static property names.Unknown Dynamic Component
12-unknown-dynamic-component
Input — input.tsx
import type { Product } from "~/graph/schema";
export function ProductRenderer({
product,
Component,
}: {
product: Product;
Component: (props: { product: Product }) => unknown;
}) {
return <Component product={product} />;
}Diagnostics
[unresolved-dynamic-component]
Cannot statically resolve graph-backed JSX component <Component />.
The component receives graph-backed props:
product: Product
Use a static conditional, a graph.components(...) registry,
or provide explicit candidates.Variable Factory
13-variable-factory
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const handle = params.handle.toLowerCase();
const product = glean.product({ handle });
return <ProductHero product={product} />;
}
function ProductHero({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}Generated GraphQL
query ProductRoute($product_handle: String!) {
product(handle: $product_handle) {
__typename
id
title
}
}Variables factory
export function getProductRouteVariables(ctx) {
const handle = ctx.params.handle.toLowerCase();
return {
product_handle: handle,
};
}Read map
{
"ProductHero": [
"Product.title"
]
}List Filter
14-list-filter
Input — input.tsx
import { glean } from "~/graph";
import type { Collection, Product } from "~/graph/schema";
export default function Route({ params }: { params: { handle: string } }) {
const collection = glean.collection({ handle: params.handle });
return <ProductList collection={collection} />;
}
function ProductList({ collection }: { collection: Collection }) {
const products = collection.products({ first: 12 }).nodes;
return products
.filter((product) => product.availableForSale)
.map((product) => <ProductCard product={product} />);
}
function ProductCard({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}Generated GraphQL
query Route($handle: String!) {
collection(handle: $handle) {
__typename
id
products(first: 12) {
__typename
nodes {
__typename
id
availableForSale
title
}
}
}
}Read map
{
"ProductList": [
"Product.availableForSale"
],
"ProductCard": [
"Product.title"
]
}Scalar Method
15-scalar-method
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return <ProductTitle product={product} />;
}
function ProductTitle({ product }: { product: Product }) {
return <h1>{product.title.toUpperCase()}</h1>;
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
}
}Read map
{
"ProductTitle": [
"Product.title"
]
}Object Spread
16-object-spread
Input — input.tsx
import type { Product } from "~/graph/schema";
export function ProductDebug({ product }: { product: Product }) {
const copy = { ...product };
return <pre>{JSON.stringify(copy)}</pre>;
}Diagnostics
[graph-value-spread]
Cannot spread graph-backed value product.
Graph values must be passed, read, or explicitly converted.Static Dynamic Component
17-static-dynamic-component
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return <ProductRenderer product={product} variant="card" />;
}
function ProductRenderer({
product,
variant,
}: {
product: Product;
variant: "card" | "row";
}) {
const Component = variant === "card" ? ProductCard : ProductRow;
return <Component product={product} />;
}
function ProductCard({ product }: { product: Product }) {
return (
<>
<h1>{product.title}</h1>
<img src={product.featuredImage?.url} />
</>
);
}
function ProductRow({ product }: { product: Product }) {
return (
<span>
{product.title} {product.priceRange.minVariantPrice.amount}
</span>
);
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
featuredImage {
__typename
url
}
priceRange {
__typename
minVariantPrice {
__typename
amount
}
}
}
}Read map
{
"ProductCard": [
"Product.title",
"Product.featuredImage.url"
],
"ProductRow": [
"Product.title",
"Product.priceRange.minVariantPrice.amount"
]
}Component Registry
18-component-registry
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
const productViews = glean.components({
card: ProductCard,
row: ProductRow,
hero: ProductHero,
});
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return <ProductRenderer product={product} view="card" />;
}
function ProductRenderer({
product,
view,
}: {
product: Product;
view: keyof typeof productViews;
}) {
const Component = productViews[view];
return <Component product={product} />;
}
function ProductCard({ product }: { product: Product }) {
return (
<>
<h1>{product.title}</h1>
<img src={product.featuredImage?.url} />
</>
);
}
function ProductRow({ product }: { product: Product }) {
return (
<span>
{product.title} {product.priceRange.minVariantPrice.amount}
</span>
);
}
function ProductHero({ product }: { product: Product }) {
return (
<>
<h1>{product.title}</h1>
<img src={product.featuredImage?.url} />
</>
);
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
featuredImage {
__typename
url
}
priceRange {
__typename
minVariantPrice {
__typename
amount
}
}
}
}Read map
{
"ProductCard": [
"Product.title",
"Product.featuredImage.url"
],
"ProductRow": [
"Product.title",
"Product.priceRange.minVariantPrice.amount"
],
"ProductHero": [
"Product.title",
"Product.featuredImage.url"
]
}Lazy Boundary
19-lazy-boundary
Input — input.tsx
import { graph, GraphLazy } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return (
<>
<ProductHero product={product} />
<GraphLazy>
<ProductDescription product={product} />
</GraphLazy>
</>
);
}
function ProductHero({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}
function ProductDescription({ product }: { product: Product }) {
return <div>{product.descriptionHtml}</div>;
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
}
}Read map
{
"ProductHero": [
"Product.title"
]
}Recursive Component
20-recursive-component
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return <Tree product={product} />;
}
function Tree({ product }: { product: Product }) {
return (
<>
<h1>{product.title}</h1>
<Tree product={product} />
</>
);
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
}
}Read map
{
"Tree": [
"Product.title"
]
}Diagnostics
[recursive-component]
Cannot statically expand recursive graph component <Tree />.
Provide an explicit recursion depth or wrap the recursive subtree in a lazy boundary.Split File Components
21-split-file-components
Input — input.tsx
import { glean } from "~/graph";
import { ProductCard } from "./ProductCard.js";
import { BuyBox } from "./nested/BuyBox.js";
// A route whose components live in other files (one even a directory deeper).
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return (
<main>
<ProductCard product={product} />
<BuyBox product={product} />
</main>
);
}Input — ProductCard.tsx
import type { Product } from "~/graph/schema";
// A component in its own file. Its reads must flow into the route's operation.
export function ProductCard({ product }: { product: Product }) {
return (
<div>
<h2>{product.title}</h2>
<img src={product.featuredImage?.url} />
</div>
);
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
featuredImage {
__typename
url
}
priceRange {
__typename
minVariantPrice {
__typename
amount
currencyCode
}
}
}
}Variables factory
export function getProductRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"ProductCard": [
"Product.title",
"Product.featuredImage.url"
],
"BuyBox": [
"Product.priceRange.minVariantPrice.amount",
"Product.priceRange.minVariantPrice.currencyCode"
]
}Path Alias
22-path-alias
Input — input.tsx
import { glean } from "~/graph";
// Imported through the app's `@/` tsconfig alias rather than a relative path.
import { PriceTag } from "@/components/PriceTag";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return (
<main>
<h1>{product.title}</h1>
<PriceTag product={product} />
</main>
);
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
priceRange {
__typename
minVariantPrice {
__typename
amount
currencyCode
}
}
}
}Variables factory
export function getProductRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"ProductRoute": [
"Product.title"
],
"PriceTag": [
"Product.priceRange.minVariantPrice.amount",
"Product.priceRange.minVariantPrice.currencyCode"
]
}Helper Functions
23-helper-functions
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
import { describeImage } from "./format.js";
// A local helper taking the whole graph value: its field reads must be tracked
// even though they happen inside a plain function, not a component.
function summary(p: Product): string {
return `${p.title} — ${p.descriptionHtml}`;
}
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return (
<main>
<p>{summary(product)}</p>
<figcaption>{describeImage(product)}</figcaption>
</main>
);
}Input — format.ts
import type { Product } from "~/graph/schema";
// A helper in another module, taking a destructured graph parameter. Its reads
// must flow into the operation of whatever route calls it.
export function describeImage({ featuredImage }: Product): string {
return featuredImage?.url ?? "no image";
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
descriptionHtml
featuredImage {
__typename
url
}
}
}Variables factory
export function getProductRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"summary": [
"Product.title",
"Product.descriptionHtml"
],
"describeImage": [
"Product.featuredImage.url"
]
}Nonnull Arg
24-nonnull-arg
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
// A TS-only non-null assertion on a root argument must NOT leak into the emitted
// JS variables factory (`handle!` is invalid JS) — it's stripped to `ctx.params.handle`.
export default function ProductRoute({ params }: { params: { handle?: string } }) {
const product = glean.product({ handle: params.handle! });
return <ProductHero product={product} />;
}
function ProductHero({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
}
}Variables factory
export function getProductRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"ProductHero": [
"Product.title"
]
}Island Reads
25-island-reads
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
import { Views } from "./Views.js";
// A route renders an island (in another file) that opens its OWN graph root via
// `useGraph()`. The island's reads must fold into the route operation + read-map,
// so the page fetches them and a per-component refetch can target them.
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return (
<main>
<ProductHero product={product} />
<Views handle={params.handle} />
</main>
);
}
function ProductHero({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}Input — Views.tsx
// An island: it gets the graph from `useGlean()` (opaque to the compiler) and opens
// its own root. The accessor is bound to ANY name (`g`, not `graph`) — the analyzer
// tracks the `useGlean()` binding — so its read folds into the owning route's op.
declare function useGlean(): typeof import("~/graph").glean | undefined;
export function Views({ handle }: { handle: string }) {
const g = useGlean();
const product = g?.product({ handle });
return <span>{product?.availableForSale}</span>;
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
availableForSale
}
}Variables factory
export function getProductRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"ProductHero": [
"Product.title"
],
"Views": [
"Product.availableForSale"
]
}Mutation
26-mutation
Input — input.tsx
import { useMutation } from "~/graph";
// A "use client" island uses `useMutation`. The selector roots at the schema's
// Mutation type: `m.setProductTitle(vars)` is the mutation root (its args lift to
// operation variables), and `.title` selects the result — so the returned Product
// normalizes into the cache and updates in place.
export function EditTitle({ id }: { id: string }) {
const [setTitle, { isLoading }] = useMutation(
(m, vars: { id: string; title: string }) => m.setProductTitle(vars).title,
);
return (
<button disabled={isLoading} onClick={() => setTitle({ id, title: "New Title" })}>
Rename
</button>
);
}Generated GraphQL
mutation EditTitle_setProductTitle($id: ID!, $title: String!) {
setProductTitle(id: $id, title: $title) {
__typename
id
title
}
}Variables factory
export function getEditTitle_setProductTitleVariables(ctx) {
return {
id: ctx.id,
title: ctx.title,
};
}Read map
{}Subscription
27-subscription
Input — input.tsx
import { useSubscription } from "~/graph";
// A "use client" island subscribes to live product updates. The selector roots at
// the schema's Subscription type: `s.productChanged(vars)` is the operation root
// (its args lift to operation variables), and `.title` selects the result — so each
// pushed Product normalizes into the cache and updates in place.
export function LiveTitle({ handle }: { handle: string }) {
const { data, error } = useSubscription(
(s, vars: { handle: string }) => s.productChanged(vars).title,
);
return <p>{error ? `error: ${error}` : `live title: ${data ?? "…"}`}</p>;
}Generated GraphQL
subscription LiveTitle_productChanged($handle: String!) {
productChanged(handle: $handle) {
__typename
id
title
}
}Variables factory
export function getLiveTitle_productChangedVariables(ctx) {
return {
handle: ctx.handle,
};
}Read map
{}Chained Root
28-chained-root
Input — input.tsx
import { glean } from "~/graph";
// The root call is mid-chain (`glean.product({...}).title`), not a bare
// `const product = glean.product(...)` — so the field read attaches to the root the
// call opens. This is the form a `useGlean()` island naturally writes
// (`glean.board().todos`); it must compile the same as the split form.
export default function ChainedRoute({ params }: { params: { handle: string } }) {
const title = glean.product({ handle: params.handle }).title;
const price = glean.product({ handle: params.handle }).priceRange.minVariantPrice.amount;
return (
<main>
<h1>{title}</h1>
<span>{price}</span>
</main>
);
}Generated GraphQL
query ChainedRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
priceRange {
__typename
minVariantPrice {
__typename
amount
}
}
}
}Variables factory
export function getChainedRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"ChainedRoute": [
"Product.title",
"Product.priceRange.minVariantPrice.amount"
]
}Map Block Binding
29-map-block-binding
Input — input.tsx
import { glean } from "~/graph";
// A `.map` with a BLOCK body that binds an intermediate (`const price = …`) and reads
// off it. The block must be walked (not just scanned) so the binding is tracked and
// `price.amount` / `price.currencyCode` fold into the operation.
export default function ListRoute({ params }: { params: { handle: string } }) {
const collection = glean.collection({ handle: params.handle });
return (
<ul>
{collection.products({ first: 10 }).nodes.map((product) => {
const price = product.priceRange.minVariantPrice;
return (
<li key={product.id}>
{product.title} — {price.amount} {price.currencyCode}
</li>
);
})}
</ul>
);
}Generated GraphQL
query ListRoute($handle: String!) {
collection(handle: $handle) {
__typename
id
products(first: 10) {
__typename
nodes {
__typename
id
priceRange {
__typename
minVariantPrice {
__typename
amount
currencyCode
}
}
title
}
}
}
}Variables factory
export function getListRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Island Map Component
30-island-map-component
Input — input.tsx
import { glean } from "~/graph";
import { ProductsList } from "./ProductsList.js";
// A route renders an island (`ProductsList`) that maps a connection to an IMPORTED
// component (`Row`, in a third file). The component's reads — reached only through a
// JSX prop inside the island's `.map` — must fold into the route operation. (The JSX
// attribute name lives in the island's file, so it must be read off the identifier,
// not via `getText` against the entry file.)
export default function ListRoute({ params }: { params: { handle: string } }) {
glean.collection({ handle: params.handle });
return <ProductsList handle={params.handle} />;
}Input — ProductsList.tsx
import { Row } from "./Row.js";
// An island: it opens its own root via `useGlean()` and renders an imported `Row`
// component for each connection node. `product={p}` forwards the element graph value
// into `Row`, whose reads must fold into the owning route's operation.
declare function useGlean(): typeof import("~/graph").glean | undefined;
export function ProductsList({ handle }: { handle: string }) {
const g = useGlean();
const products = g?.collection({ handle }).products({ first: 10 });
return <ul>{products?.nodes.map((p) => <Row key={p.id} product={p} />)}</ul>;
}Input — Row.tsx
import type { Product } from "~/graph/schema";
export function Row({ product }: { product: Product }) {
return <li>{product.title} — {product.priceRange.minVariantPrice.amount}</li>;
}Generated GraphQL
query ListRoute($handle: String!) {
collection(handle: $handle) {
__typename
id
products(first: 10) {
__typename
nodes {
__typename
id
title
priceRange {
__typename
minVariantPrice {
__typename
amount
}
}
}
}
}
}Variables factory
export function getListRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"ProductsList": [
"Product.id"
],
"Row": [
"Product.title",
"Product.priceRange.minVariantPrice.amount"
]
}List Root
31-list-root
Input — input.tsx
import { glean } from "~/graph";
// A top-level LIST root: `glean.products()` resolves to an array directly (no object
// wrapper), so `.map` iterates it and the element reads fold into the operation.
export default function AllProducts() {
return (
<ul>
{glean.products().map((product) => (
<li key={product.id}>{product.title}</li>
))}
</ul>
);
}Generated GraphQL
query AllProducts {
products {
__typename
id
title
}
}Variables factory
export function getAllProductsVariables(ctx) {
return {
};
}Read map
{
"AllProducts": [
"Product.id",
"Product.title"
]
}Map Named Callback
32-map-named-callback
Input — input.tsx
import { glean } from "~/graph";
import { renderRow } from "./render-row.js";
// `.map(renderRow)` — a function REFERENCE (imported), not an inline callback. The
// callback resolves like a helper: the element binds to its first parameter, its
// reads fold into the operation, and the read-map attributes them to the function's
// own name (so `refresh()` can target it like any component).
export default function ListRoute({ params }: { params: { handle: string } }) {
const products = glean.collection({ handle: params.handle }).products({ first: 10 }).nodes;
return <ul>{products.map(renderRow)}</ul>;
}Input — render-row.tsx
import type { Product } from "~/graph/schema";
export function renderRow(product: Product) {
return (
<li>
{product.title} — {product.priceRange.minVariantPrice.amount}
</li>
);
}Generated GraphQL
query ListRoute($handle: String!) {
collection(handle: $handle) {
__typename
id
products(first: 10) {
__typename
nodes {
__typename
id
title
priceRange {
__typename
minVariantPrice {
__typename
amount
}
}
}
}
}
}Variables factory
export function getListRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"renderRow": [
"Product.title",
"Product.priceRange.minVariantPrice.amount"
]
}Map Destructured Param
33-map-destructured-param
Input — input.tsx
import { glean } from "~/graph";
// A DESTRUCTURED `.map` element (`({ title, handle: slug }) => …`): each bound name
// is a field read off the element, and a renamed binding reads the ORIGINAL field.
export default function ListRoute({ params }: { params: { handle: string } }) {
const products = glean.collection({ handle: params.handle }).products({ first: 10 }).nodes;
return (
<ul>
{products.map(({ title, handle: slug }) => (
<li key={slug}>{title}</li>
))}
</ul>
);
}Generated GraphQL
query ListRoute($handle: String!) {
collection(handle: $handle) {
__typename
id
products(first: 10) {
__typename
nodes {
__typename
id
title
handle
}
}
}
}Variables factory
export function getListRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"ListRoute": [
"Product.title",
"Product.handle"
]
}Unsupported List Callback
34-unsupported-list-callback
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
// A dynamically-selected callback can't be analyzed. The compiler must SAY so — an
// `unsupported-list-flow` diagnostic — because staying silent would compile an
// operation that UNDER-FETCHES the callback's element reads.
const renderers: Record<string, (p: Product) => unknown> = {};
export default function ListRoute({ params }: { params: { handle: string; kind: string } }) {
const products = glean.collection({ handle: params.handle }).products({ first: 10 }).nodes;
return <ul>{products.map(renderers[params.kind])}</ul>;
}Generated GraphQL
query ListRoute($handle: String!) {
collection(handle: $handle) {
__typename
id
products(first: 10) {
__typename
nodes {
__typename
id
}
}
}
}Diagnostics
[unsupported-list-flow]
Cannot statically analyze the list callback renderers[params.kind].
Use an inline arrow/function, a reference to a named function, or a destructured
element parameter — the callback's element reads must be statically visible.Shared Param
35-shared-param
Input — input.tsx
import { glean } from "~/graph";
import type { Product, Collection } from "~/graph/schema";
// The SAME route param feeds two different roots. Both root args are "simple"
// context paths named `handle`, so they must lift to ONE `$handle` variable
// (deduped definition + a single factory entry), not two.
export default function Route({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
const collection = glean.collection({ handle: params.handle });
return (
<>
<ProductHero product={product} />
<CollectionTitle collection={collection} />
</>
);
}
function ProductHero({ product }: { product: Product }) {
return <h1>{product.title}</h1>;
}
function CollectionTitle({ collection }: { collection: Collection }) {
return <h2>{collection.title}</h2>;
}Generated GraphQL
query Route($handle: String!) {
product(handle: $handle) {
__typename
id
title
}
collection(handle: $handle) {
__typename
id
title
}
}Variables factory
export function getRouteVariables(ctx) {
return {
handle: ctx.params.handle,
};
}Read map
{
"ProductHero": [
"Product.title"
],
"CollectionTitle": [
"Collection.title"
]
}Acceptance
acceptance
Input — input.tsx
import { glean } from "~/graph";
import type { Product } from "~/graph/schema";
export default function ProductRoute({ params }: { params: { handle: string } }) {
const product = glean.product({ handle: params.handle });
return (
<>
<ProductHero product={product} />
<BuyBox product={product} />
</>
);
}
function ProductHero({ product }: { product: Product }) {
return (
<>
<h1>{product.title}</h1>
<img src={product.featuredImage?.url} />
</>
);
}
function BuyBox({ product }: { product: Product }) {
const price = product.priceRange.minVariantPrice;
return (
<button>
{price.amount} {price.currencyCode}
</button>
);
}Generated GraphQL
query ProductRoute($handle: String!) {
product(handle: $handle) {
__typename
id
title
featuredImage {
__typename
url
}
priceRange {
__typename
minVariantPrice {
__typename
amount
currencyCode
}
}
}
}Read map
{
"ProductHero": [
"Product.title",
"Product.featuredImage.url"
],
"BuyBox": [
"Product.priceRange.minVariantPrice.amount",
"Product.priceRange.minVariantPrice.currencyCode"
]
}36 fixtures. This page regenerates from the fixtures on every build.