Constructive ORM
Use the generated Prisma-like ORM for stable custom-domain schemas. It is optional and must remain endpoint-scoped. Console Kit uses runtime discovery, standalone Data introspects through Sheets, and the other standalone feature packs receive resources and actions from their host.
When to Apply
Use this skill when:
- A project has deliberately generated an ORM from a stable domain schema.
- Implementing typed queries, mutations, relations, or pagination in server code.
- Creating a client for one explicit endpoint and request/session scope.
- Debugging generated ORM output or regeneration drift.
Use constructive-blocks for Console Kit and feature-pack data. Use constructive-frontend for bespoke runtime _meta UI.
Blocks Package Gate
The pinned Blocks snapshot is branch-only with
release.publicRegistryReady: false. Before importing the current
@constructive-io/data contract from an ORM workflow, run the
constructive-blocks source preflight and pinned local package-consumption
workflow. The same rule applies whenever an ORM-adjacent task reaches for the
current @constructive-io/ui, @constructive-io/schema-builder, or
@constructive-io/sheets package or an @constructive registry root. Public
installation becomes valid only after an updated Blocks snapshot points to a
released commit, sets publicRegistryReady: true, and passes its checker.
Per-Scope Client
Create the ORM client from an explicit endpoint for the current request or tenant. Do not export a mutable process-wide client when endpoints or identities can change.
import { createClient } from '@/generated/orm';
export function createDomainClient(endpoint: string, accessToken: string) {
return createClient({
endpoint,
headers: { Authorization: `Bearer ${accessToken}` }
});
}
Keep the token in the host session or server request boundary and create/dispose the binding with that scope.
Query and Mutation Shape
const db = createDomainClient(dataEndpoint, accessToken);
const projectsResult = await db.project.findMany({
select: { id: true, name: true, completed: true },
first: 20
}).unwrap();
const projects = projectsResult.projects.nodes;
await db.project.update({
where: { id: projectId },
data: { completed: true },
select: { id: true, completed: true }
}).unwrap();
Every model method returns a QueryBuilder. Use .execute() for the QueryResult discriminated union or call .unwrap(), .unwrapOr(), or .unwrapOrElse() directly on the builder. List data remains under the generated GraphQL field, such as projectsResult.projects.nodes. Never interpret an empty collection as proof that the schema lacks rows—the active identity's RLS policy may be filtering them.
Pagination
Prefer cursor pagination for mutable datasets and stable traversal. Offset pagination is suitable for bounded administrative views where page-number navigation matters and drift is acceptable.
See pagination.md for generated pagination patterns.
Runtime Metadata
The generated ORM is not the owner of the current _meta contract. After the
pinned local package workflow is active, import the contract documents, types,
compatibility guards, and operation-analysis helpers from
@constructive-io/data, then reconcile _meta with standard introspection.
See query-meta-introspection.md for the evidence model. Use the Data feature pack instead of rebuilding generic table CRUD.
References
| File | Content |
|---|---|
| codegen-orm-output.md | Generated ORM output |
| codegen-orm-patterns.md | Advanced custom-domain patterns |
| pagination.md | Cursor and offset pagination |
| query-generators-api.md | Runtime operation generators |
| query-runtime.md | Runtime query construction |
| query-meta-introspection.md | Current _meta and introspection boundary |
| codegen-query-keys.md | Generated query keys |
Cross-References
constructive-codegen— generation and regeneration.constructive-hooks— optional fixed-endpoint React Query layer.constructive-security— RLS and effective authorization.