🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@parcae/model

Package Overview
Dependencies
Maintainers
1
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@parcae/model

Parcae Model — typed ORM base class with adapter pattern

npmnpm
Version
0.7.0
Version published
Weekly downloads
10
-85.51%
Maintainers
1
Weekly downloads
 
Created
Source

@parcae/model

The core Model system for Parcae. Class properties are the schema. Direct property access via Proxy with change tracking, lazy-loading references, and a pluggable adapter pattern that runs the same code on frontend and backend.

Install

npm install @parcae/model

Define a Model

import { Model } from "@parcae/model";

class Post extends Model {
  static type = "post" as const;

  user!: User;              // reference -> VARCHAR storing ID
  title: string = "";       // string -> VARCHAR
  body: PostBody = {};      // object -> JSONB
  tags: string[] = [];      // array -> JSONB
  published: boolean = false; // boolean -> BOOLEAN
  views: number = 0;        // number -> DOUBLE PRECISION
}

No decorators, no separate schema definition, no Zod. The class properties are the schema.

Property Access

The Model constructor returns a Proxy. Data properties read/write to an internal store with automatic change tracking.

const post = await Post.findById("abc");

post.title;              // "Hello" — reads from data store
post.title = "Updated";  // change tracked automatically
post.published;          // false — typed as boolean

await post.save();       // flushes tracked changes

References

Properties typed as another Model class become lazy-loading proxies. The $ prefix gives raw ID access.

post.user;   // User proxy — loads on property access, Suspense-compatible
post.$user;  // "user_k8f2m9x" — raw string ID, no loading

// Setting a reference accepts a Model instance or raw ID
post.user = someUser;        // extracts someUser.id
post.$user = "user_abc123";  // sets raw ID directly

The reference proxy throws a Promise on first property access for React Suspense integration:

<Suspense fallback={<span>Loading...</span>}>
  <span>{post.user.name}</span>
</Suspense>

Static Query Methods

All query methods go through the global adapter set via Model.use().

// Find by ID
const post = await Post.findById("abc");

// Query builder
const published = await Post.where({ published: true })
  .orderBy("createdAt", "desc")
  .limit(10)
  .find();

// Other entry points
Post.whereIn("id", ["a", "b", "c"]);
Post.whereNot({ published: false });
Post.whereNotIn("status", ["draft", "archived"]);
Post.whereRaw("views > ?", 100);
Post.select("title", "views");
Post.count();

// Convenience: paginated, sorted
Post.basic(25, "createdAt", "desc", 0);

Instance Methods

// Create
const post = Post.create({ title: "New Post" });
post.id;  // auto-generated 20-char ID

// Save (insert or update)
await post.save();

// Save with debounce (frontend batching)
post.__debounceMs = 500;
post.title = "A";
post.title = "AB";    // batched into single save
await post.save();

// Atomic JSON Patch (RFC 6902)
await post.patch([
  { op: "replace", path: "/title", value: "Patched" },
  { op: "add", path: "/body/blocks/-", value: { type: "text" } },
]);

// Delete
await post.remove();

// Reload from adapter (skips in-flight changes)
await post.refresh();

// Serialize
post.toJSON();           // { type, id, title, ... }
await post.sanitize(user); // override in subclass to strip fields

Query Chain

The QueryChain<T> interface supports 40+ chainable methods:

Filtering: where, andWhere, orWhere, whereIn, whereNot, whereNotIn, whereNull, whereNotNull, whereBetween, whereRaw, orWhereRaw, orWhereIn, orWhereNull, whereExists

Ordering & Pagination: orderBy, orderByRaw, limit, offset

Selection & Grouping: select, distinct, distinctOn, groupBy, groupByRaw, having, havingRaw

Joins: join, innerJoin, leftJoin, rightJoin

Aggregates: sum, avg, min, max, increment, decrement

Terminal: find(), first(), count()

On the backend, each method directly mutates a Knex query. On the frontend, each method records a serializable QueryStep sent to the server for execution.

Adapter Pattern

The ModelAdapter interface decouples the Model from persistence:

interface ModelAdapter {
  createStore(data): Record<string, any>;
  save(model, changes): Promise<void>;
  remove(model): Promise<void>;
  findById(modelClass, id): Promise<T | null>;
  query(modelClass): QueryChain<T>;
  patch(model, ops): Promise<void>;
}
AdapterStorePersistence
FrontendAdapterValtio proxy (reactive)Transport RPC (Socket.IO / SSE)
BackendAdapterPlain objectKnex + PostgreSQL

Set the adapter once at startup:

import { Model } from "@parcae/model";

Model.use(adapter);

FrontendAdapter

Included in this package. Wraps a Transport to handle client-side persistence.

import { FrontendAdapter } from "@parcae/model";

const adapter = new FrontendAdapter(transport);
Model.use(adapter);

The Transport interface is protocol-agnostic:

interface Transport {
  get(path, data?): Promise<any>;
  post(path, data?): Promise<any>;
  put(path, data?): Promise<any>;
  patch(path, data?): Promise<any>;
  delete(path, data?): Promise<any>;
  subscribe?(event, handler): () => void;
  unsubscribe?(event, handler?): void;
  send?(event, ...args): void;
  readonly isConnected?: boolean;
  readonly isLoading?: boolean;
  on?(event, handler): void;
  off?(event, handler?): void;
  disconnect?(): void;
  reconnect?(): Promise<void>;
}

Static Properties

PropertyTypeDescription
typestringModel identifier. Used for table naming and routing.
pathstring?Custom API path. Defaults to /v1/{type}s.
scopeModelScope?Row-level security rules.
indexesIndexDefinition[]?Database index definitions.
managedbooleanfalse for externally managed tables (e.g. auth). Default: true.
__schemaSchemaDefinition?Resolved at startup by RTTIST. Maps properties to column types.

Type Mapping

TypeScriptColumnTypePostgres
string"string"VARCHAR(2048)
string (long)"text"TEXT
number (int)"integer"INTEGER
number (float)"number"DOUBLE PRECISION
boolean"boolean"BOOLEAN
Date"datetime"TIMESTAMP
SomeModel{ kind: "ref" }VARCHAR (foreign key)
object / array"json"JSONB

Exports

import { Model, generateId, FrontendAdapter } from "@parcae/model";
import type {
  Transport,
  ModelAdapter,
  ModelConstructor,
  ChangeSet,
  QueryChain,
  QueryStep,
  SchemaDefinition,
  ColumnType,
  PrimitiveColumnType,
  IndexDefinition,
  ModelScope,
  ScopeContext,
  ScopeResult,
  ScopeFunction,
  PatchOp,
} from "@parcae/model";

Deep imports also available:

import { FrontendAdapter } from "@parcae/model/adapters/client";
import type { ModelAdapter } from "@parcae/model/adapters/types";

License

MIT

FAQs

Package last updated on 06 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts