Skip to content

Entities

Every entity you define gets a generated TypeScript class, importable from platform:entities. The class is the contract between your schema and your code.

// Generated from your schema. Always in sync.
export class Issue {
record_id: string;
issue_id: string;
title: string;
type: string;
status: string;
priority: string;
severity: string;
assignee_id: string | null;
component_id: string;
due_date: string | null;
created_at: string;
}

Nullable fields are typed | null, so the compiler makes you handle the case rather than discovering it at runtime.

Read into the type:

const { rows } = await sql.query<Issue>(`SELECT * FROM ${i}`);

Write by instantiating:

const issue = new Issue();
issue.title = "Disk nearly full";
issue.component_id = componentId;
await sql.insertOrThrow([issue]);

The platform fills record_id and created_at. You do not set them.

The runtime resolves platform:entities when your code executes, but your editor cannot — it needs files on disk. polymesa types writes them:

Terminal window
polymesa types ./my-workspace

Definitions land in .polymesa/ inside that directory. Re-run after any schema change.

Some entities are physical tables rather than JSONB records. They are prefixed with a double underscore:

EntityContents
__userUser accounts
__groupGroups and their hierarchy

Records can reference them like any other entity. They are also how you seed users, since users are deliberately not configuration:

Terminal window
polymesa upsert --entity __user --file ./users.csv

The groups column on that CSV takes a ;-separated list of group names.

tableRef turns an entity class into safe SQL references — see Querying with SQL.

const i = tableRef(Issue);
`${i.title}` // table-qualified column reference

Only data fields are exposed, so ${i.save} will not compile.