tally-server has two parts. createKyselyAdapter compiles grid queries into SQL through Kysely. createTallyHandler serves any DataSource over HTTP as a Fetch handler.
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.
Dialects
ASqlDialect supplies the functions SQL engines disagree on: text, lower, float, floor, dayNumber, dateBucket, plus encode and decode for values crossing the driver.
- SQLite
- Postgres
- MySQL
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.SqlDialect for SQL Server or another engine and pass it as dialect.
Semantics
The adapter passes thetally-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, exceptneq,notContainsandnotIn, 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-QnorYYYY. - 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,
NULLand''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 FetchRequest, so it mounts anywhere Fetch is the interface. Set basePath to the path it is mounted under.
- Hono
- Next.js
- Node
hono.ts
Conformance
The conformance suite intally-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.