Core Concepts
Fundamental concepts in Max.
Ref
A Ref is a typed reference to an entity.
const userRef = AcmeUser.ref("u123");
userRef.entityDef; // AcmeUseruserRef.entityType; // "AcmeUser"userRef.id; // "u123"userRef.scope; // LocalScope or SystemScopeRefs are rich objects that carry:
- The entity type (runtime)
- The entity ID
- Scope information (local vs system)
Creating refs:
AcmeUser.ref("u123") // Local scope (default)AcmeUser.ref("u123", Scope.system("inst_456")) // System scopeUsing refs:
// Load an entityconst user = await engine.load(userRef, Fields.ALL);
// Follow referencesconst teamRef = user.fields.team; // Ref<AcmeTeam>const team = await engine.load(teamRef, Fields.select("name"));Scope
Scope defines the installation context for refs and entities.
LocalScope
Single installation - no installation ID needed.
Scope.local()SystemScope
Multi-installation/multi-tenant - requires installation ID.
Scope.system("inst_456")Why scope matters:
In local mode (developer laptop), everything is local scope - one installation.
In system mode (enterprise deployment), refs carry installation IDs to distinguish entities across tenants.
Scope upgrade:
const localRef = AcmeUser.ref("u1"); // Local scopeconst systemRef = localRef.upgradeScope(Scope.system("inst_456")); // System scopeDefault: Most code uses local scope. System scope is for multi-tenant deployments.
EntityDef
An EntityDef defines an entity type and its fields.
interface AcmeUser extends EntityDef<{ name: ScalarField<"string">; email: ScalarField<"string">;}> {}
const AcmeUser: AcmeUser = EntityDef.create("AcmeUser", { name: Field.string(), email: Field.string(),});Pattern: Interface + const with same name (Type + Companion Object).
AcmeUser works as:
- Type:
Ref<AcmeUser>,EntityInput<AcmeUser> - Value:
AcmeUser.ref("u1"),EntityDef.create(...)
EntityInput
A complete upsert request - ref + fields.
const input = EntityInput.create(AcmeUser.ref("u1"), { name: "Alice", email: "alice@example.com",});
await engine.store(input);Why it’s useful:
- Can be passed around / returned from functions
- Loaders return EntityInput
- Self-contained (has ref + data)
EntityResult
Wrapper around loaded entity data with type-safe field access.
const result = await engine.load(userRef, Fields.select("name", "email"));
// Access via .get()result.get("name"); // stringresult.get("email"); // string
// Access via .fields (proxy)result.fields.name; // stringresult.fields.email; // string
// @ts-error - 'age' not loadedresult.get("age");Only loaded fields are accessible - type-safe partial loading.