> ## 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.

# Your First Table Computation

> Turn rows of one table into another table that stays current, then read it like any table

> [!NOTE]
>
> These examples describe the current Bijection CLI and SDK source. Use builds
> from the same repository revision for these APIs. The public CLI and npm SDK
> are released independently and may lag this reference.

This page builds the smallest useful pipeline: per-customer order totals,
computed by Bijection from the `orders` table, kept current as orders change,
and read with an ordinary query and `useQuery`. Nothing runs on your own
infrastructure; [customer compute](https://docs.bijection.com/datasets/customer-compute.md) comes later.

## 1. Share the row validator

Declare the row once. The table and the computation's input both use it.

```ts bijection/rows.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { v } from "bijection/values";

export const order = v.object({ customer: v.string(), amount: v.int64() });
```

## 2. Declare the computation

A table computation declares `args`, its `inputs` (row validators), its
`returns` and a `handler`. `groupBy` runs the handler once per customer:
`ctx.inputs.orders` holds that customer's rows and `ctx.groupKey` their key.
The handler reads only these captured rows; there is no `ctx.db`. When a
customer's orders all disappear, the handler runs with an empty input and
returning no row removes that customer's total.

`maintain` keeps the latest result current: refresh when inputs change, at
most once a second, and replace the readable result when a new one is
accepted.

```ts bijection/totals.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { computation, rows } from "bijection/computations";
import {
  maintain,
  type MaintainedComponent,
} from "@bijection/pipelines/maintained";
import { componentsGeneric, makeFunctionReference } from "bijection/server";
import { v } from "bijection/values";
import { query } from "./_generated/server";
import { order } from "./rows";

// One row per customer, recomputed only for customers whose orders changed.
export const totals = computation({
  args: {},
  inputs: { orders: order },
  returns: {
    totals: rows(v.object({ customer: v.string(), amount: v.int64() }), {
      key: ["customer"],
    }),
  },
  groupBy: (order) => order.customer,
  handler: (ctx) => ({
    totals: ctx.inputs.orders.length
      ? [
          {
            customer: ctx.groupKey!,
            amount: ctx.inputs.orders.reduce((sum, o) => sum + o.amount, 0n),
          },
        ]
      : [],
  }),
});

// Keep the latest result current as orders change.
export const currentTotals = maintain(
  componentsGeneric().pipelines as unknown as MaintainedComponent,
  totals,
  {
    access: makeFunctionReference("application:computationAccess"),
    args: {},
    refresh: { when: "invalidated", minInterval: "1s" },
    select: { by: "admission", replace: "onAcceptance" },
    retention: "1d",
    serve: { maxResultAge: "1m", stale: "allow" },
  },
);

export const list = query({
  args: {},
  handler: (ctx) => ctx.db.query("customer_totals").visible().take(100),
});
```

The `orders` input reads the `orders` table, because they share a name. To
read another table, add `bindExecution(nativeExecution(totals, { inputs }))`
from `@bijection/pipelines`.

## 3. Publish the result as a table

`defineSyncedTable` turns the maintained output into a read-only table keyed by
the output's `key`. Mutations cannot write it; each accepted result replaces
its rows in one transaction.

```ts bijection/schema.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { defineSchema, defineTable, defineSyncedTable } from "bijection/server";
import { readAccess } from "./policies";
import { order } from "./rows";
import { currentTotals } from "./totals";

export default defineSchema({
  orders: defineTable(order).access(readAccess),
  customer_totals: defineSyncedTable({
    source: currentTotals.outputs.totals.key("customer"),
  }).access(readAccess),
});
```

## 4. Say who may run it

Declare the application's computation policy once. It decides who may start,
inspect and maintain computations, including the maintenance program itself.
Reading `orders` and `customer_totals` still follows their own `access`.

```ts bijection/application.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { computationPolicy } from "bijection/computations";
import { currentTotals } from "./totals";

// Who may start, inspect and maintain this application's computations.
export const computationAccess = computationPolicy({
  handler: async (ctx, request) =>
    (await currentTotals.isProgram(ctx, request.actor))
      ? { scope: request.actor }
      : null,
});
```

## 5. Read it in React

The `list` query in `totals.ts` reads `customer_totals` like any table, and the
component subscribes to it. Subscribers see each new result as soon as it is
selected.

```tsx src/Totals.tsx theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { useQuery } from "bijection/react";
import { api } from "../bijection/_generated/api";

export function Totals() {
  const totals = useQuery(api.totals.list, {});
  if (totals === undefined) return <p>Loading…</p>;
  return (
    <ul>
      {totals.map((row) => (
        <li key={row.customer}>
          {row.customer}: {row.amount.toString()}
        </li>
      ))}
    </ul>
  );
}
```

## Next steps

* [Published tables](https://docs.bijection.com/publications/published-tables.md) covers keys, indexes,
  freshness, retention and explicit selection.
* [Pipelines](https://docs.bijection.com/publications/pipelines.md) covers partitioning, folds over large
  inputs, checks before acceptance and temporal computations.
* [Customer compute](https://docs.bijection.com/datasets/customer-compute.md) runs Python, Spark or dbt on
  your own infrastructure under the same contract.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.