laguz

One request per render, streamed back field by field.

laguz batches the GraphQL queries your React components declare into a single request per commit. With its server package, each field comes back the moment it resolves, and queries that depend on each other share the same round trip.

npm install @laguzjs/client

When each widget gets its data

laguz, 1 request: 0 of 20 TanStack Query, 20 requests: 0 of 20

How it works

Components ask for what they need

Each component selects its own fields with a hook, as it would with any data library. It receives exactly that selection, under its own names.

function Header() {
  const { data } = useData(pool, "user", { id: 1 },
    ["name", { avatar: { args: { size: 32 } } }]);
  return <h1>{data?.name}</h1>;
}

One commit becomes one query

Everything a render asks for goes out together. Selections on the same field are merged, fields already cached or in flight are skipped, and arguments can point at another field's result.

{
  q0: user(id: 1) {
    name teamId avatar_ccrvkh: avatar(size: 32)
  }
  q1: team(id: $_ref__q0__teamId) { leadId }
  q2: stats { revenue }
}

Fields flow back as they resolve

@laguzjs/server runs every root field on its own and writes a line as soon as one is done. Fast widgets render right away, a slow query only delays itself, and an invalid field fails alone.

{"data":{"q0":{"name":"Ada Lovelace", …}}}
{"data":{"q1":{"leadId":"3"}}}
… 300 ms later
{"data":{"q2":{"revenue":48210.5}}}

Measured against TanStack Query

Same components, same server, headless Chromium, HTTP/2 with brotli on a slow 4G link. Medians of five runs.

Scenario TanStack Query laguz
20-widget page, median widget on screen 278 ms, 20 requests 171 ms, 1 request
Database round trips for that page 28 6
Response bytes for that page 2.2 KB 0.9 KB
Refresh an infinite list with 5 pages 832 ms, 5 requests 171 ms, 1 request
Three dependent queries 498 ms, 3 requests 186 ms, 1 request

On a fast network over HTTP/2 the latencies are close, and what remains is fewer requests and less work for the server. Methodology and full results.

Get started

In the browser

import { createPool, httpFetcher } from "@laguzjs/client";
import { useData } from "@laguzjs/client/react";

const pool = createPool(
  httpFetcher("/graphql", { stream: true }),
);

On the server

import { batchLoader, createHandler } from "@laguzjs/server";

export const POST = createHandler({
  schema,
  context: () => ({ users: batchLoader(loadUsers) }),
});