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: "employee-database",
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 established successfully");
} catch (error) {
console.log("Error while establishing connection", error);
}
if (conn) await conn.closeConnection();
Defining tables
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
Intercept and compute student full name before insert and print all records after
const conn = await odm.connect(true);
await conn.defineTable({
name: "student",
columns: [
{
name: "first_name",
type: "string",
},
{
name: "last_name",
type: "string",
},
{
name: "full_name" /* Value computed in intercept */,
type: "string",
},
],
});
class FullNameIntercept extends DatabaseOperationInterceptor {
getName() {
return "full-name-intercept";
}
async intercept(
collectionName: string,
operation: DatabaseOperationType,
when: DatabaseOperationWhen,
records: Record[],
_context: DatabaseOperationContext,
) {
if (collectionName === "student") {
console.log(
`[collectionName=${collectionName}, operation=${operation}, when=${when}]`,
);
if (operation === "INSERT") {
if (when === "BEFORE") {
for (const record of records) {
console.log(
`Full name field updated for :: ${record.get("first_name")}`,
);
record.set(
"full_name",
`${record.get("first_name")} ${record.get("last_name")}`,
);
}
}
}
if (operation === "SELECT") {
if (when === "AFTER") {
for (const record of records) {
console.log(JSON.stringify(record.toJSON(), null, 4));
}
}
}
}
return records;
}
}
odm.addInterceptor(new FullNameIntercept());
const studentTable = conn.table("student");
const studentRecord = studentTable.createNewRecord();
studentRecord.set("first_name", "John");
studentRecord.set("last_name", "Doe");
await studentRecord.insert();
await studentTable.select().toArray();
/* This will print the following:
[collectionName=student, operation=INSERT, when=BEFORE]
Full name field updated for :: John
[collectionName=student, operation=INSERT, when=AFTER]
[collectionName=student, operation=SELECT, when=BEFORE]
[collectionName=student, operation=SELECT, when=AFTER]
{
"id": "653c21bb-7d92-435e-a742-1da749f914dd",
"_table": "student",
"first_name": "John",
"last_name": "Doe",
"full_name": "John Doe"
}
*/
await conn.closeConnection();
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 <<