Skip to main content

TypeScript Order Tutorial

Build understanding around one draft Order: load its complete item list, change Notebook quantity from 2 to 3, save the Order and line totals together, then read the result in a new process. Follow the recorded native SQLite application and learn why each query, expression and transaction boundary is needed. The version and publication boundaries below apply to this specific example.

The retained core suite and deletion readback use a workspace runtime: WORKSPACE PASS — RELEASE PENDING. The nullable generator repair remains pending generator deployment. Node SQLite evidence does not certify browser/TFP execution.

1. Prepare the example​

You need the TypeScript toolchain, the site checkout, and the retained built typescript-app-console workspace from the core Order receipt. Read its build.toolchain, build.runtimeSource, build.runtimePackage and verification.qualification entries for typescript before selecting dependencies. The commands below assume that recorded workspace and its dependencies already exist; they are not a clean-machine bootstrap or a claim that every published runtime passes. For build/replay preparation use the retained workspace instructions.

Start a new model with the TypeScript Quick Start, then use Order-scoped Assist for its generated Q/E/mutation methods. This page reuses the evaluated Order v1.0 model and the three-order fixture. Do not substitute the isolation/stock fixture: those are separately evaluated model revisions.

The complete application contains imports, helpers, startup and all phase handlers. The excerpts below are function-body excerpts from that application, not standalone files. Use the exact package alias and imports in the recorded console: UserContext from teaql-ts, SQLiteTeaQLClient from teaql-ts/sql/sqlite, and application-local Q, E and GENERATED_RUNTIME_MODULE. The required helper and the EDIT audit string are defined at the top of that file.

2. Understand startup and run the first edit​

The console creates SQLiteTeaQLClient(database,{}), installs GENERATED_RUNTIME_MODULE into the provider, registers it as dataService on a server-created UserContext, then awaits context.ensureSchema(). This is the Node SQL path; the browser-safe package profile does not provide this SQLite wiring.

Module installation registers metadata and behavior. Schema reconciliation is an explicit database operation. Keep startup/admin schema work separate from request execution, and obtain tenant/actor/policy from trusted server context.

From the site root, run this sequence with a new disposable database:

export ORDER_WORKSPACE="$PWD/artifacts/order/2026-10-27/typescript/typescript-app-console"
export ORDER_FIXTURE="$PWD/static/examples/order/fixture.json"
export ORDER_DATABASE="$(mktemp -d)/orders.sqlite"
cd "$ORDER_WORKSPACE"
for phase in bootstrap seed draft-graph note-value note-null note-empty note-not-loaded invalid-quantity draft-edit restart-readback; do
node -r ts-node/register src/main.ts "$phase"
done

Run seed once on the fresh database; the fixture program is not an import/upsert service. Each phase starts a fresh process against the same absolute database path. The two NotLoaded probes are intentional negative examples; their handler reports the native error and finishes successfully.

Stop and inspectExpected result
bootstrapExplicit schema setup and ORDER_OK phase=bootstrap.
seed and draft-graphThree Orders, six items; ORD-2026-1001 is DRAFT with total 3850 USD cents.
Note phasesValue, SQL NULL, empty text and NotLoaded remain distinct.
invalid-quantity0, -1, 1.5 and 101 rejected before save.
draft-editNotebook quantity 3, Notebook line total 3750, Order total 5100.
restart-readbackThe same totals in a new process; Order and changed item version 2, untouched item version 1.

The handlers print ORDER_CASE JSON and an ORDER_OK completion line. Find the Order by business number and the line by SKU; generated numeric IDs are not portable fixture keys. Compare the returned graph with these expected values before adapting the operation to your own model.

3. Load the full Order before editing​

const rows=await Q.customerOrders().withOrderNumberIs("ORD-2026-1001")
.selectOrderItemListWith(Q.orderItems().orderBySkuCodeAscending().orderByIdAscending().limit(101)).limit(2)
.comment("Load full draft order and complete bounded items").purpose("Validate and edit one draft order").executeForList(context);

The outer bound of 2 lets the application reject a duplicate business identity. The child bound of 101 is an overflow sentinel for a supported maximum of 100 items. Require exactly one Order, DRAFT status and at most 100 loaded items before continuing. A one-item preview is useful for a list screen, but cannot supply the complete total for an edit. The full source performs these checks after the query; keep them when adapting the excerpt.

comment describes the operation and purpose records the business reason. Both travel with execution through the trusted context. A buyer filter in this v1.0 fixture is a business predicate; it does not authenticate or authorize a buyer. Use the separate buyer/tenant isolation contract when implementing protected access.

4. Validate money and save one atomic edit​

The application validates integral quantity in [1, 100], USD currency, each stored line amount and the complete new Order total. Prices and totals are integer cents. It finds exactly one SKU-NOTEBOOK, computes 3 × 1250 + 3 × 450 = 5100, then changes the selected item and Order together. The following write excerpt assumes those validated, fully loaded variables; it is not a replacement for validation.

The audit reason is Edit draft Order quantity and totals atomically, named EDIT in most examples and editReason/EditReason in Go, Swift and .NET.

changed.updateQuantity(quantity); changed.updateLineTotalMinor(quantity*required(E.orderItem(changed).unitPriceMinor().eval()));
order.updateTotalMinor(total); console.log("ORDER_GRAPH_ATTEMPT");
await order.auditAs(EDIT).save(context);

The edit rejects unsafe or non-integral JavaScript numbers before calculating integer cents. The loaded item objects belong to the Order graph; mutate the chosen item and save the audited Order once. Preserve the workspace package resolution rather than replacing its recorded alias with a guessed npm dependency.

A successful audited save returns the persisted typed entity. The core example reads again after mutation, rather than treating a pre-save object as independent evidence of committed state. A rejected stale write requires reloading authoritative state and a business decision before retrying; it is not permission to overwrite the winner or retry every provider error.

5. Use projections, nulls and pages deliberately​

TaskTypeScript API in this exampleResult / responsibility
Execute a bounded queryawait query.executeForList(context)Typed SmartList; preserve native errors and check cardinality.
Read selected moneyE.customerOrder(order).totalMinor().eval()Loaded integer cents; not a formatting or currency conversion API.
Access selected itemsorder.orderItemList()Only the relation that was explicitly loaded.
Default a selected SQL NULL noteorderNote().orElse("No note")Null-only fallback; an empty string stays empty and NotLoaded still reports a missing field.
Save an audited mutationawait order.auditAs(EDIT).save(context)Persisted typed entity; the required transaction includes all changed entities.
Request an exact page totaloffset(offset).limit(limit).count() → page.totalCountTotal of the filtered set, independent of page length.

The note accessors in the table are called on the corresponding E expression. required, report, pageRows, load, edit and prepare_edit are application helpers where present; they are not additional generated terminal methods.

Note stateExpected behavior
Selected valueLeave at reception stays that value.
Selected SQL NULLA null-only default produces No note.
Selected empty textRemains the empty string.
Omitted fieldNative NotLoaded diagnostic, including when a null-only default is attempted.

For a read-only list screen, the existing bounded-page handler validates offset >= 0 and 1 <= limit <= 3, sets a stable total/ID order and explicitly selects one item preview per Order. With validated offset, limit and buyer, its native query is:

const page=await Q.customerOrders().withBuyerReferenceIs(buyer).orderByTotalMinorAscending().orderByIdAscending()
.selectOrderItemListWith(Q.orderItems().orderBySkuCodeAscending().orderByIdAscending().limit(1)).offset(offset).limit(limit).count()
.comment("Read bounded Order pages").purpose("Compare filtered totals and per-order item previews").executeForList(context);

This query only reads. Do not save its one-item preview as if it were a full Order edit or infer deletion of omitted items. The pagination fixture has eight matching BUYER-EXPORT Orders, whereas the first-edit fixture above has three Orders overall. See the page API decision guide for fixture selection, continuous export, retained-ID jumps and aggregate results.

6. Diagnose a failure before changing code​

A better-sqlite3 load/ABI error is a Node/native-addon setup problem. Match the recorded Node toolchain and build the addon for it; do not classify the failure as a query or optimistic conflict.

SymptomFirst action
Workspace or package missingConfirm the retained artifact prerequisite; follow the Quick Start for fresh generation instead of assuming this checkout ships compiled outputs.
Empty Order or duplicate rowConfirm absolute DB path, fixture and business number; preserve the cardinality check.
Wrong total after editCheck complete item loading and integer validation before the audited save.
Missing field errorReload the field/relation explicitly; do not substitute a null default.
Version conflictReload authoritative state and decide whether the requested edit is still valid.
Busy/locked, commit or cancellation failureInspect native provider error and transaction boundary; follow the TypeScript Order troubleshooting table.

Continue with the Order API usage guide, TypeScript advanced data operations, version and qualification table and documentation coverage matrix. The School regression baseline and all prior Order receipts remain unchanged.

Continue with advanced Order writes​

Follow TypeScript Advanced Order Mutations for explicit line deletion, legal state transitions and stale-version recovery. Each recipe explains its starting state, transaction boundary, native errors and independent readback using the existing qualified example. Start a fresh recipe database; do not reuse the quantity-edited database above.

Continue with Order queries​

Use TypeScript Order Queries for continuous export, retained-ID page jumps and provider summaries. The query recipes use separate nine-/ten-order datasets; keep the fixture and context/store lifetime with the operation you are learning.

Continue with buyer and tenant isolation​

Use TypeScript Order Isolation to upgrade the model to v1.1 and build root/buyer scope from an application-owned trusted session.