JUSTAOS’s ORM
JUSTAOS’s ORM (Object Relational Mapping) tool is built for Deno and provides transparent persistence for JavaScript objects to Postgres database.
- Supports all primitive data types (string, integer, float, boolean, date, object, array, etc).
- Supports custom data types.
- Supports table with multi-level inheritance.
- Also supports interception on operations (create, read, update and delete).
deno add @justaos/orm
import { ORM } from "@justaos/orm";
Database connection
const odm = new ORM({
database: "collection-service",
username: "postgres",
password: "postgres",
hostname: "localhost",
port: 5432,
});
let conn: ORMConnection | undefined;
try {
conn = await odm.connect(true /* create database if not exists */);
console.log("connection success");
} catch (_error) {
console.log("connection failed");
} finally {
if (conn) await conn.closeConnection();
}
Defining models
await orm.defineTable({
name: "blog_post",
columns: [
{
name: "title",
type: "string",
},
{
name: "content",
type: "string",
},
],
});
await orm.defineTable({
name: "comment",
columns: [
{
name: "blog_post",
type: "reference",
},
{
name: "message",
type: "string",
},
],
});
Querying
await conn.defineTable({
name: "teacher",
columns: [
{
name: "name",
type: "string",
},
{
name: "roll_no",
type: "integer",
},
],
});
const teacherTable = conn.table("teacher");
for (let i = 0; i < 10; i++) {
const teacher = teacherTable.createNewRecord();
teacher.set("name", "a" + (i + 1));
teacher.set("roll_no", i + 1);
await teacher.insert();
}
const records = await teacherTable
.select()
.orderBy("roll_no", "DESC")
.toArray();
records.forEach(async function (rec) {
console.log(`${await rec.get("name")} :: ${await rec.get("roll_no")}`);
console.log(JSON.stringify(await rec.toJSON(), null, 4));
});
const count = await teacherTable.count();
console.log("COUNT :: " + count);
await conn.closeConnection();
Intercepting database operations
odm.addInterceptor(
new (class extends DatabaseOperationInterceptor {
getName() {
return "my-intercept";
}
async intercept(
collectionName: string,
operation: DatabaseOperationType,
when: DatabaseOperationWhen,
records: Record[],
_context: DatabaseOperationContext,
) {
if (collectionName === "student") {
if (operation === "INSERT") {
console.log(
`[collectionName=${collectionName}, operation=${operation}, when=${when}]`,
);
if (when === "BEFORE") {
for (let record of records) {
console.log(
"computed field updated for :: " + record.get("name"),
);
record.set("computed", record.get("name") + " +++ computed");
}
}
}
if (operation === "SELECT") {
console.log(
`[collectionName=${collectionName}, operation=${operation}, when=${when}]`,
);
if (when === "AFTER") {
for (const record of records) {
console.log(JSON.stringify(record.toJSON(), null, 4));
}
}
}
}
return records;
}
})(),
);
await conn.defineTable({
name: "student",
columns: [
{
name: "name",
type: "string",
},
{
name: "computed",
type: "string",
},
],
});
const studentTable = conn.table("student");
const studentRecord = studentTable.createNewRecord();
studentRecord.set("name", "John " + new Date().toISOString());
await studentRecord.insert();
await studentTable.select().toArray();
/* This will print the following:
[collectionName=student, operation=CREATE, when=BEFORE]
computed field updated for :: John 2023-12-05T13:57:21.418Z
[collectionName=student, operation=CREATE, when=AFTER]
[collectionName=student, operation=READ, when=BEFORE]
[collectionName=student, operation=READ, when=AFTER]
{
"id": "e5d8a03e-7511-45c6-96ad-31a6fa833696",
"_table": "student",
"name": "John 2023-12-05T13:31:13.313Z",
"computed": "John 2023-12-05T13:31:13.313Z +++ computed"
}
*/
Define custom field type
After connection established, you can define custom field type.
odm.addDataType(
new (class extends DataType {
constructor() {
super("email", "VARCHAR");
}
toJSONValue(value: string | null): string | null {
return value;
}
validateDefinition(_columnDefinition: ColumnDefinition) {
return true;
}
setValueIntercept(newValue: any): any {
return newValue;
}
async validateValue(value: unknown): Promise<void> {
const pattern = "(.+)@(.+){2,}\\.(.+){2,}";
if (
value &&
typeof value === "string" &&
!new RegExp(pattern).test(value)
) {
throw new Error("Not a valid email");
}
}
})(),
);
await conn.defineTable({
name: "student",
columns: [
{
name: "name",
type: "string",
},
{
name: "personal_contact",
type: "email",
},
{
name: "emp_no",
type: "uuid",
},
{
name: "salary",
type: "integer",
},
{
name: "birth_date",
type: "date",
},
{
name: "gender",
type: "boolean",
},
],
});
const studentTable = conn.table("student");
const student = studentTable.createNewRecord();
student.set("personal_contact", "test");
student.set("birth_date", new Date());
try {
await student.insert();
console.log("Student created");
} catch (error) {
console.log(error.toJSON());
}
Inheritance
await conn.defineTable({
name: "animal",
columns: [
{
name: "name",
type: "string",
},
],
});
const animalTable = conn.table("animal");
const animal = animalTable.createNewRecord();
animal.set("name", "Puppy");
await animal.insert();
await conn.defineTable({
name: "dog",
inherits: "animal",
final: true,
columns: [
{
name: "breed",
type: "string",
},
],
});
const dogTable = conn.table("dog");
const husky = dogTable.createNewRecord();
husky.set("name", "Jimmy");
husky.set("breed", "Husky");
await husky.insert();
const animals = await animalTable.select().toArray();
animals.forEach((animal) => {
console.log(animal.toJSON());
});
Data type | get | getJSONValue |
---|---|---|
string | string | string |
integer | number | number |
date | Temporal.PlainDate | string |
Check the examples >> here <<