Once you have a Cluster instance, you can create Ent classes to access the data.
Copy import { PgSchema } from "ent-framework/pg";
import { ID, BaseEnt, GLOBAL_SHARD, AllowIf, OutgoingEdgePointsToVC } from "ent-framework";
import { cluster } from "./cluster";
const schema = new PgSchema(
"users",
{
id: { type: ID, autoInsert: "nextval('users_id_seq')" },
email: { type: String },
is_admin: { type: Boolean, autoInsert: "false" },
},
["email"]
);
export class EntUser extends BaseEnt(cluster, schema) {
static override configure() {
return new this.Configuration({
shardAffinity: GLOBAL_SHARD,
privacyInferPrincipal: async (_vc, row) => row.id,
privacyLoad: [new AllowIf(new OutgoingEdgePointsToVC("id"))],
privacyInsert: [],
});
}
}
If your app uses UUID type for IDs, replace { type: ID, autoInsert: "nextval('users_id_seq')" }
with something like:
Copy id: { type: String, autoInsert: "gen_random_uuid()" }
(Notice that you need to use type String
and not ID
for UUID fields. Read more about ID formats and microsharding aspects in Locating a Shard and ID Format article.)
Each Ent may also have one optional "unique key" (possible composite) which is treated by the engine in a specific optimized way. In the above example, it's email
.
Copy import { PgSchema } from "ent-framework/pg";
import {
ID,
BaseEnt,
GLOBAL_SHARD,
AllowIf,
OutgoingEdgePointsToVC,
Require,
} from "ent-framework";
import { cluster } from "./cluster";
const schema = new PgSchema(
"topics",
{
id: { type: ID, autoInsert: "nextval('topics_id_seq')" },
created_at: { type: Date, autoInsert: "now()" },
updated_at: { type: Date, autoUpdate: "now()" },
slug: { type: String },
creator_id: { type: ID },
subject: { type: String, allowNull: true },
},
["slug"]
);
export class EntTopic extends BaseEnt(cluster, schema) {
static override configure() {
return new this.Configuration({
shardAffinity: GLOBAL_SHARD,
privacyInferPrincipal: async (_vc, row) => row.creator_id,
privacyLoad: [new AllowIf(new OutgoingEdgePointsToVC("creator_id"))],
privacyInsert: [new Require(new OutgoingEdgePointsToVC("creator_id"))],
});
}
}
By default, all fields are non-nullable (unless you provide allowNull
option).
Disregard privacy rules for now, it's a more complicated topic which will be covered later. For now, the code should be obvious enough.
Copy import { PgSchema } from "ent-framework/pg";
import {
ID,
BaseEnt,
AllowIf,
CanReadOutgoingEdge,
OutgoingEdgePointsToVC,
Require,
} from "ent-framework";
import { cluster } from "./cluster";
import { EntTopic } from "./EntTopic";
const schema = new PgSchema(
"comments",
{
id: { type: ID, autoInsert: "nextval('comments_id_seq')" },
created_at: { type: Date, autoInsert: "now()" },
topic_id: { type: ID },
creator_id: { type: ID },
message: { type: String },
},
[]
);
export class EntComment extends BaseEnt(cluster, schema) {
static override configure() {
return new this.Configuration({
shardAffinity: GLOBAL_SHARD,
privacyInferPrincipal: async (_vc, row) => row.creator_id,
privacyLoad: [
new AllowIf(new CanReadOutgoingEdge("topic_id", EntTopic)),
new AllowIf(new OutgoingEdgePointsToVC("creator_id")),
],
privacyInsert: [new Require(new OutgoingEdgePointsToVC("creator_id"))],
});
}
}
Since we have no microshards yet, shardAffinity
basically does nothing. We'll talk about microsharding in Locating a Shard and ID Format .