
Product
PHP and Composer Support Is Now in Beta
Socket’s PHP and Composer support is now in Beta for all customers, with PHP reachability analysis generally available.
@parcae/model
Advanced tools
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.
npm install @parcae/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.
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
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>
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);
// 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
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.
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>;
}
| Adapter | Store | Persistence |
|---|---|---|
FrontendAdapter | Valtio proxy (reactive) | Transport RPC (Socket.IO / SSE) |
BackendAdapter | Plain object | Knex + PostgreSQL |
Set the adapter once at startup:
import { Model } from "@parcae/model";
Model.use(adapter);
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>;
}
| Property | Type | Description |
|---|---|---|
type | string | Model identifier. Used for table naming and routing. |
path | string? | Custom API path. Defaults to /v1/{type}s. |
scope | ModelScope? | Row-level security rules. |
indexes | IndexDefinition[]? | Database index definitions. |
managed | boolean | false for externally managed tables (e.g. auth). Default: true. |
__schema | SchemaDefinition? | Resolved at startup by RTTIST. Maps properties to column types. |
| TypeScript | ColumnType | Postgres |
|---|---|---|
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 |
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";
MIT
FAQs
Parcae Model — typed ORM base class with adapter pattern
We found that @parcae/model demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Product
Socket’s PHP and Composer support is now in Beta for all customers, with PHP reachability analysis generally available.

Product
Socket is bringing experimental protection to Firefox, scanning 97,000+ extensions in Mozilla's official directory for malware and risky updates.

Research
/Security News
Three compromised Rust crates pulled in a malicious dependency that downloaded and executed cross-platform malware during Cargo builds.