> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bijection.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reading Publications

> Query published tables and require the result you depend on

A published table is read like any other table. Use `ctx.db` in a
[query](/functions/query-functions) or [mutation](/functions/mutation-functions),
read through its indexes, and subscribe to it from a client. When a new
publication switches in, subscribed queries rerun and receive the new rows.

```ts bijection/orders.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { query } from "./_generated/server";

export const openOrderCount = query({
  args: {},
  handler: async (ctx) => {
    const orders = await ctx.db.query("open_orders").take(500);
    return orders.length;
  },
});
```

## One publication per read

A pipeline switches all of its output tables in one commit. A query or mutation
reads every table at one point in time, so it reads all of a pipeline's tables
from the same publication. It never sees the new `open_orders` next to the old
`customer_totals`.

Published tables are not guaranteed to reflect the latest input. After an order
is written, `open_orders` keeps serving the previous publication until the next
run finishes. To depend on a particular input change, use a
[coverage requirement](#requiring-a-publication).

## Unavailable tables

A published table is unavailable before its first publication, after its
publication is withdrawn, and while its group is in an
[ownership transition](/publications/pipelines#changing-a-published-group).
Reading it then fails with `PublishedUnavailable`. It is never served as an
empty table.

The failed read stays a dependency of the query. When the table becomes
available, a subscribed query reruns without the client reconnecting.

Availability is checked on every read, including cached results and paginated
continuations. Withdrawing a publication, or revoking the authority it was
published under, refuses its rows immediately. Rows a client already received
cannot be recalled.

## Paginating published tables

[Paginated queries](/database/pagination) over a published table do not
continue silently onto a newer publication. If the publication changes between
pages, continuing from the old cursor can fail with `QueryAccessChanged`.
Restart the read from the first page.

## Knowing what is serving

To show whether a result is current, expose the pipeline's `publication` method
from a query. It reads the pipeline's state and every output table at one
point in time, and requires an authenticated user the access policy grants.

```ts bijection/summaries.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export const publication = query({
  args: {},
  handler: (ctx) => orderSummary.publication(ctx),
});
```

It returns `null` before the pipeline is configured, and otherwise an object
with these fields:

| Field | Meaning |
| - | - |
| `phase` | `"serving"`, `"preparing"`, `"refused"`, `"withdrawing"`, `"unavailable"` or `"transitioning"`. |
| `isReadable` | Whether the tables currently serve an eligible publication. |
| `isServingSelected` | Whether the run the pipeline selected is the one serving. |
| `selectedRun`, `servingRun` | The run selected most recently, and the run actually serving. They differ while a new result prepares. |
| `servingRevision` | The revision at which the serving publication was committed. |
| `isRefreshPending` | Whether a run is active or waiting to start. |
| `reason` | Why the selected run is not serving, when it is not. |
| `relations` | Each output table's own publication status. |

An older result can keep serving while a newer one prepares or fails, so a
client should check `isServingSelected` before reporting that a change has been
published.

For a single table, `publications.status` from `bijection/server` returns its
publication status directly:

```ts bijection/orders.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { publications } from "bijection/server";
import { query } from "./_generated/server";

export const openOrdersStatus = query({
  args: {},
  handler: () => publications.status("open_orders"),
});
```

The result has `serving` (the serving publisher, producer, run and input
evidence, or its `transitioning` or `withdrawn` state), `publishedAt`,
`preparation` (the state, rows and pages of a publication being prepared) and
`scan` (set when Bijection refuses the pipeline's publisher declaration).
`status` is governed by the table's own read rule. It remains readable while
the rows are unavailable, so you can diagnose why; it never returns rows.

## Requiring a publication

A function can state which publication it needs, and Bijection checks it in the
function's own transaction before the handler runs. Wrap a query or mutation
with `publications.consuming`:

```ts bijection/orders.ts {5,19-23} theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { publications } from "bijection/server";
import { query } from "./_generated/server";
import { v } from "bijection/values";

export const currentCustomerSummary = publications.consuming(
  query({
    args: { customer: v.string(), after: v.string() },
    handler: async (ctx, args) => ({
      orders: await ctx.db
        .query("open_orders")
        .withIndex("by_customer", (q) => q.eq("customer", args.customer))
        .take(100),
      totals: await ctx.db
        .query("customer_totals")
        .withIndex("by_customer", (q) => q.eq("customer", args.customer))
        .unique(),
    }),
  }),
  {
    tables: ["open_orders", "customer_totals"],
    compatibility: "sameGroup",
    covers: [{ table: "orders", argument: "after" }],
  },
);
```

The requirement names 1 to 8 published `tables` and one `compatibility` rule:

| Requirement | What it guarantees | What it does not guarantee |
| - | - | - |
| `sameGroup` | Every named table is served by the same publisher, group, run and publication. | That the result is recent. |
| `sharedRoots` | Where the named tables were computed from the same input tables, those inputs were captured at the same snapshot or in the same unchanged state. Use it for tables from different pipelines. | That independent sources were observed at the same moment. |
| `covers` | Each named table was computed from input at least as new as the given checkpoint of the input table. | That a particular request was accepted or applied elsewhere. |

`covers` is optional and holds at most 16 entries. Each names an input `table`
and the `argument` that carries a checkpoint; that argument must be a required
`v.string()`. When the requirement is not met, the function fails before its
handler runs, with one of `PublishedUnavailable`, `PublicationUnavailable`,
`PublicationSourceUnavailable`, `PublicationGroupMismatch`,
`PublicationEvidenceUnavailable`, `PublicationRootMismatch`,
`PublicationRootOverlap` or `PublicationCoverage`. A checkpoint that cannot be
used fails with `PublicationCheckpoint`. A query does not return data and a mutation does
not commit.

### Checkpoints

A checkpoint names the current state of an ordinary table. Get one from a query
with `publications.checkpoint`, for example right after your client's mutation
succeeds:

```ts bijection/orders.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export const ordersCheckpoint = query({
  args: {},
  handler: () => publications.checkpoint({ table: "orders" }),
});
```

Pass it as the `after` argument of `currentCustomerSummary`. The query refuses with
`PublicationCoverage` until a publication computed from that state, or a newer
one, is serving.

Call `publications.checkpoint` from a query your client calls directly, not
from a nested query. It names an ordinary table; views and published tables are
refused with `PublicationCheckpoint`.

### Checking inside a handler

`publications.require` checks the same requirement from inside a handler, with
the checkpoint given directly as `after`:

```ts bijection/orders.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
await publications.require({
  tables: ["open_orders", "customer_totals"],
  compatibility: "sameGroup",
  covers: [{ table: "orders", after: args.after }],
});
```

A refusal cannot be caught and ignored: even if your code catches the error,
the query returns no data and the mutation does not commit. Prefer
`publications.consuming` when the requirement applies to the whole function.

### Waiting until a requirement is met

`publications.readiness` takes the same requirement in a query and returns
`{ kind: "ready" }` or `{ kind: "pending", reason }` instead of failing. It is
reactive: a subscribed query updates when the publication changes.

```ts bijection/orders.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export const summaryReady = query({
  args: { after: v.string() },
  handler: (ctx, args) =>
    publications.readiness({
      tables: ["open_orders", "customer_totals"],
      compatibility: "sameGroup",
      covers: [{ table: "orders", after: args.after }],
    }),
});
```

<Note>
  A `ready` result is not permission to act later. A mutation that depends on
  the publication must still check it with `publications.consuming` or
  `publications.require` in its own transaction.
</Note>
