Skip to main content
tally-server has two parts. createKyselyAdapter compiles grid queries into SQL through Kysely. createTallyHandler serves any DataSource over HTTP as a Fetch handler.
Kysely is a peer dependency. Bring the driver for your database: pg, mysql2, better-sqlite3, or node:sqlite through the included nodeSqlite bridge.

Adapter

adapter.ts
The adapter filters, searches, sorts, groups, aggregates, facets and pages in SQL. Every request is validated with tally-protocol before any SQL runs. Mutations run in one transaction and return the canonical rows read back from the database. A database error rejects the whole batch with Mutation failed rather than the driver message, and onError receives the original error.
Groups past maxGroups are dropped from the summaries, and their pages come back empty. Raise it when a grouped column can have more distinct values.

Dialects

A SqlDialect supplies the functions SQL engines disagree on: text, lower, float, floor, dayNumber, dateBucket, plus encode and decode for values crossing the driver.
sqliteDialect works with node:sqlite through nodeSqlite, as in the adapter above, and with better-sqlite3 passed straight to Kysely’s SqliteDialect. Text comparisons fold ASCII case only.
Implement SqlDialect for SQL Server or another engine and pass it as dialect.

Semantics

The adapter passes the tally-protocol conformance suite on SQLite, Postgres and MySQL, so results match the in-memory engine:
  • Empty means NULL, and for text columns also ''. Empty values sort last in both directions and never match comparisons, except neq, notContains and notIn, which keep them.
  • Text comparisons are case insensitive through lower().
  • Enum columns sort and facet in option order.
  • Date groups bucket to YYYY-MM-DD, ISO Monday weeks, YYYY-MM, YYYY-Qn or YYYY.
  • Search matches the stored value of searchable columns, not the formatted text.
  • Group order follows the sort on the group column when there is one, otherwise option or natural order, newest dates first.
  • Empty values form one group, NULL and '' alike, which sorts last and pages like any other group.

Handler

createTallyHandler returns (request: Request) => Promise<Response>. It serves the four protocol routes for any DataSource, not only the Kysely adapter. Accepted mutations are broadcast to every open event stream, and patches from a source with the live capability are relayed. The schema route therefore always reports live: true. An event stream whose consumer falls more than 1024 events behind is closed, and the client reconnects.

Mounting

The handler takes a Fetch Request, so it mounts anywhere Fetch is the interface. Set basePath to the path it is mounted under.
hono.ts
The handler does no authentication. Put it in the framework’s middleware, in front of the handler.

Conformance

The conformance suite in tally-protocol runs every case against the adapter and compares the answer with the in-memory executor. Seed a table with conformanceRows and create the adapter with CONFORMANCE_TODAY as its today.
conformance.ts
report.failures lists every case that differed, with one diff per path. Cases that need a capability the adapter does not declare are skipped. Run the same check against a custom SqlDialect before you rely on it.