CL ORM
Why
- Supports both Cloudflare D1 and Durable Objects SQL Storage.
- Unified Connection interface across different database drivers.
- User-friendly ORM for INSERT, SELECT, UPDATE, DELETE and WHERE clauses.
- Automatic Prepared Statements.
Installation
npm i cl-orm
Getting Started
Connect
- D1
- Durable Objects
src/index.ts
import { useD1 } from 'cl-orm';
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const db = useD1(env.DB);
await db.query('SELECT 1');
// ...
},
};
wrangler.jsonc
{
"name": "my-worker",
"main": "src/index.ts",
"d1_databases": [
{
"binding": "DB",
"database_name": "my-database",
"database_id": "<your-database-id>"
}
]
}
src/index.ts
import { useDO } from 'cl-orm';
import { DurableObject } from 'cloudflare:workers';
export class MyDurableObject extends DurableObject {
async fetch(request: Request): Promise<Response> {
const db = useDO(this.ctx.storage.sql);
await db.query('SELECT 1');
// ...
}
}
wrangler.jsonc
{
"name": "my-worker",
"main": "src/index.ts",
"durable_objects": {
"bindings": [
{
"name": "MY_DO",
"class_name": "MyDurableObject"
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["MyDurableObject"]
}
]
}
Documentation
See detailed specifications and usage in Documentation section.
D1 vs Durable Objects
CL ORM provides a unified Connection interface across both drivers. The same API works identically regardless of the underlying database.
The following examples are based on TypeScript and ES Modules.
Returning a single user
- OP
- Manual
import type { Connection } from 'cl-orm';
import { OP } from 'cl-orm';
const { eq } = OP;
export const getUser = async (db: Connection, id: number) => {
const user = await db.select({
from: 'users',
where: eq('id', id),
limit: 1,
});
return user;
};
// Usage: await getUser(db, 15);
import type { Connection } from 'cl-orm';
export const getUser = async (db: Connection, id: number) => {
const user = await db.select({
from: 'users',
where: '`id` = ?',
params: [id],
limit: 1,
});
return user;
};
// Usage: await getUser(db, 15);
Due to
limit: 1, it returns a direct object with the row result ornull.
OP Explanation
OP = Operation
OP.eq: is column equal to value
See all available operators here.
Returning enabled users based on their specializations
- OP.AND
- Array
- Manual
import type { Connection, Param } from 'cl-orm';
import { OP } from 'cl-orm';
const { AND, eq, in: IN } = OP;
export const getUsersBySpecializations = async (
db: Connection,
specializations: Param[]
) => {
const users = await db.select({
columns: ['name', 'age'],
from: 'users',
where: AND(eq('status', true), IN('specialization', specializations)),
orderBy: ['name'],
limit: 10,
});
return users;
};
// Usage: await getUsersBySpecializations(db, ['frontend', 'backend']);
import type { Connection, Param } from 'cl-orm';
import { OP } from 'cl-orm';
const { eq, in: IN } = OP;
export const getUsersBySpecializations = async (
db: Connection,
specializations: Param[]
) => {
const users = await db.select({
columns: ['name', 'age'],
from: 'users',
where: [eq('status', true), 'AND', IN('specialization', specializations)],
orderBy: ['name'],
limit: 10,
});
return users;
};
// Usage: await getUsersBySpecializations(db, ['frontend', 'backend']);
import type { Connection, Param } from 'cl-orm';
export const getUsersBySpecializations = async (
db: Connection,
specializations: Param[]
) => {
const users = await db.select({
columns: ['name', 'age'],
from: 'users',
where: `\`status\` = ? AND \`specialization\` IN (${specializations.map(() => '?').join(', ')})`,
params: [true, ...specializations],
orderBy: ['name'],
limit: 10,
});
return users;
};
// Usage: await getUsersBySpecializations(db, ['frontend', 'backend']);
OP Explanation
OP = Operation
OP.eq: is column equal to value
OP.in: is in column [values,]
See all available operators here.
Acknowledgements
- The operator names eq, ne, gt, lt, gte and lte are inspired by Sequelize.