Skip to content

Client

Client

A page imports one value: import { bolt } from '$bolt'. $bolt is the generated alias of @norbital-ai/bolt/page, typed by the workspace’s names index with the same types as server code, so an unknown collection, field, query, action, input or message key is a type error in bolt check, exactly as on the server.

API reference: bolt/client

<script lang="ts">
  // src/data/collection/job_assignments/+representation.svelte (templates/field-operations, excerpt)
  import { bolt, live } from '$bolt';
  import type { RecordView } from '@norbital-ai/ui';

  let { view }: { view: RecordView<'job_assignments'> } = $props();
  const record = $derived(view.mode === 'update' ? view.record : null);

  // one row by id, with the fields it names
  const site = live(() => record === null ? null
    : bolt.live(bolt.get('sites', record.site_id, { name: true })));
  // a page of rows: every read states limit or all
  const variations = live(() =>
    record == null
      ? null
      : bolt.live(bolt.read('variation_requests', {
          where: { job_assignment_id: { eq: record.id } },
          orderBy: { requested_at: 'desc' },
          limit: 50
        }))
  );
</script>

Reads

  • read(collection, { where?, select?, orderBy?, search?, limit | all }) — a page of rows. There is no default page size: every read states limit or all: true.
  • get(collection, id, select?) — one row, or null.
  • aggregate(collection, { count?, sum?, avg?, min?, max?, by?, where? }) — one row over the scope, or pages of groups.
  • similar(collection, '<search>', input, options) — a named similarity search.
  • history(collection, id, { at? }) — a row’s revisions, or its state at an instant, a revision, or an approval’s restore point.
  • query('<c>.<q>', input) — a collection query.

Each returns a query that has not run: await reads once, and bolt.live(q) keeps it live. Reads reject only on failure.

Writes and runs

// src/app/field_ops_controller/+dispatch.page.svelte (templates/field-operations, excerpt)
import { Effect } from 'effect';
import { bolt, fork } from '$bolt';
import type { Id } from '@norbital-ai/bolt';
import { downloadTextFiles } from '@norbital-ai/ui';

let review = $state<{ id: Id<'sys_run'> } | null>(null);
function runReview() {
  const started = bolt.start('review_job_assignment_suspicion', {});
  review = { id: started.id }; // only plain data enters $state
  fork(started);
}

// src/app/hr_controller/payroll/+payroll.page.svelte (templates/hr-payroll, excerpt)
const exportPayroll = (ids: readonly Id<'payroll_run'>[]) =>
  bolt.query('payroll_run.export_payroll', { ids }).pipe(
    Effect.tap(({ documents }) => Effect.sync(() => downloadTextFiles(documents, 'payroll-exports.zip')))
  ); // return the Effect to toolbar.actions
  • act('<c>.create' | '<c>.update' | '<c>.delete' | '<c>.<action>', input) — every write. It answers an Outcome and never rejects.
  • start(automation, input) — starts an automation run and returns its RunHandle, which can be awaited.
  • upload(file, '<c>.<field>') — stores a file for a file field, checking accept and max before the bytes are stored. fileUrl(ref) is its download link, served only while the viewer may read the field that holds it.
  • approvals.process(requestId, decision) and approvals.withdraw(requestId) — approval decisions.

Who and where

bolt.actor is who is looking at the page; key rows by actor.id, never by a label. bolt.t(key, vars?) translates a catalog key, and bolt.locale is the viewer’s locale. Sign-in, sign-out and invitations are shell pages, not part of bolt.

Views do most of this for you

@norbital-ai/ui views read, page, filter, write and open records on their own from one of prop. Reach for bolt directly for a computed panel, a dashboard figure or a custom button: see UI components.