Skip to main content

TypeScript Business Scenarios

TeaQL TypeScript has a Node SQL profile and a browser-safe federal client profile. Keep those entry points separate; importing the root/browser client must not pull pg, mysql2, or better-sqlite3 into a browser bundle.

Shared query capability​

The Node runtime implements the complete seven-runtime native query profile: typed scalar and nested relation predicates, typed relation selection through child Requests, projection and ordering, grouping and portable aggregates, relation statistics, facets, ordinary/continuous/ID-set pagination, and exact per-parent Top-N. See the cross-language query contract for the capability catalog and audited source revision. The browser TFP client is a separate, intentionally narrower transport profile.

Start with the common data-operations guide for the distinction between row queries, grouped analytics, relation metrics, and Facet sidecars. The examples below use generated Order and School fixtures.

Query a Node SQL provider​

const result = await Q.customerOrders()
.comment("Find matching orders")
.withOrderNumberContaining("WEB-")
.orderByIdAscending()
.offset(0)
.limit(20)
.purpose("Prepare the authorized order review page")
.executeForList(context);

purpose() returns ExecutableCustomerOrderRequest. The ordinary builder cannot execute. Context provides the data service and trusted runtime state.

Relations and per-parent Top-N​

Generated reverse relations expose select...With(childRequest). Configure order and limit on the child Request, attach it to the parent before purpose, and inspect the SQL trace for ROW_NUMBER() OVER (PARTITION BY ...). Do not report an application slice or overfetch as an exact database plan.

Aggregates and facets​

Aggregate-only Requests describe the complete filtered population. Add a generated group method for one row per group:

const groups = await Q.schools()
.withNameContaining("School")
.groupBySchoolType()
.countAs("schoolCount")
.sumStudentCapacityAs("capacityTotal")
.avgStudentCapacityAs("capacityAverage")
.minStudentCapacityAs("capacityMinimum")
.maxStudentCapacityAs("capacityMaximum")
.comment("Aggregate schools by type")
.purpose("Build the capacity dashboard")
.executeForRows(context);

const firstCount = groups[0].schoolCount;

executeForRows keeps group keys and aliases as records. Aliases are not writable entity properties. Relation statistics decorate each parent:

const types = await Q.schoolTypesWithMinimalFields()
.selectCode()
.countSchoolsAs("schoolCount")
.sumStudentCapacityOfSchoolsAs("capacityTotal", Q.schools())
.comment("Calculate statistics for every school type")
.purpose("Render type summary cards")
.executeForList(context);

Include-all and matched-only facets​

const rows = await Q.schools()
.withNameContaining("Primary")
.facetBySchoolTypeAs(
"schoolTypes",
Q.schoolTypesWithMinimalFields()
.selectCode()
.selectName()
.countSchoolsAs("schoolCount"),
true,
)
.comment("Search schools and calculate type buckets")
.purpose("Render the school search page")
.executeForList(context);

const buckets = rows.facet("schoolTypes");
const firstCount = buckets?.[0].schoolCount;

The third argument defaults to true: all allowed SchoolTypes remain visible, including zero-count buckets. Pass false for matched-only buckets. Restrict the allowed bucket domain on the child Request with Q.schoolTypesWithMinimalFields().withCodeIn("PRIMARY", "SECONDARY"). Attach multiple named Facets to the same outer Request when a page needs several filter panels:

const rows = await Q.schools()
.facetBySchoolTypeAs(
"schoolTypes", Q.schoolTypesWithMinimalFields().selectCode().countSchoolsAs("count"), true)
.facetByPlatformAs(
"platforms", Q.platformsWithMinimalFields().selectName().countSchoolsAs("count"), false)
.comment("Calculate school type and platform facets")
.purpose("Render two authorized filter panels")
.executeForList(context);

rows still contains the matching Schools; rows.facet(name) reads a named sidecar. PostgreSQL, MySQL, and SQLite executions use native aggregation. Keep the same active filter on page rows, count, totals, and Facets when they describe one screen.

Create and audited save​

let order = Q.customerOrders()
.comment("Create a validated order")
.purpose("Persist an authorized checkout")
.newEntity(context);

order.updateOrderNumber("WEB-10001");
order = await order.auditAs("Create validated checkout order").save(context);

order.updateOrderNumber("WEB-10001-R1");
order = await order.auditAs("Approve revised order number").save(context);

order.markForDeletion();
await order.auditAs("Cancel duplicate checkout order").save(context);

Query the complete current entity before a normal update so its optimistic version is loaded. The runtime rejects missing audit intent and stale versions and emits both mutation-audit paths.

Federate to Rust​

The generated domain client can serialize its SelectQuery through the TeaQL Federal Protocol /query endpoint. The server—not client JSON—owns tenant, authenticated user, permissions, request policy, approved purpose policy and provider selection.

Treat unknown fields/operators, deep paths, excessive IN lists/page sizes and forbidden sorts as protocol errors. Do not silently ignore them or degrade to an unfiltered query. Compare a federated response with a direct backend query when qualifying a new protocol/provider combination.

Failure checklist​

Check the generated Request source, current runtime package resolution, explicit Node SQL subpath, UserContext resources and native trace. A copied node_modules runtime is not reproducible evidence; pin the generated project to the intended package version or current workspace artifact.

Continue with TypeScript advanced data operations for a composed search, relation graph, analytics, Facet, and audited update workflow.