Skip to content

arsn-cc/surorm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SurORM

A Drizzle-style ORM for SurrealDB built with TypeScript. Type-safe, chainable queries with zero runtime overhead.

Why SurORM?

  • Type-safe - Full TypeScript support with compile-time type inference
  • Zero runtime reflection - No decorators, no proxies hiding behavior
  • SurrealQL-first - Direct SQL compilation with parameter binding
  • Lightweight - Minimal dependencies, fast compilation

Installation

bun add surorm
npm install surorm

Quick Start

import { table, id, string, datetime, select, eq, compileSelect } from 'surorm';

// Define schema
const users = table("user", {
  id: id("id"),
  email: string("email"),
  createdAt: datetime("created_at"),
});

// Build and compile query
const query = select()
  .from(users)
  .where(eq(users.email, "test@example.com"))
  .limit(1);

const { sql, params } = compileSelect(query);
// sql: "SELECT * FROM user WHERE email = $1 LIMIT 1"
// params: ["test@example.com"]

Schema DSL

Define your database schema with type-safe column definitions.

Column Types

import { table, id, string, datetime } from 'surorm';

const users = table("user", {
  // ID field - maps to SurrealDB record ID
  id: id("id"),
  
  // String field
  name: string("name"),
  
  // Datetime field  
  createdAt: datetime("created_at"),
});

Column Options

const users = table("user", {
  id: id("id"),
  
  // Unique constraint
  email: string("email").unique(),
  
  // Optional field
  bio: string("bio").optional(),
  
  // Default value
  createdAt: datetime("created_at").default("time::now()"),
});

Type Inference

SurORM automatically infers types for select and insert operations:

const users = table("user", {
  id: id("id"),
  email: string("email"),
  createdAt: datetime("created_at"),
});

// Type for SELECT queries - includes all fields
type User = typeof users.$inferSelect;
// { id: string, email: string, createdAt: string }

// Type for INSERT - excludes fields with defaults
type InsertUser = typeof users.$inferInsert;
// { id: string, email: string }

Query Builder

Select

import { select, from, eq, and, or, gt, lt } from 'surorm';

// Basic select
select().from(users)

// With where clause
select().from(users).where(eq(users.email, "test@example.com"))

// With multiple conditions
select().from(users).where(and(
  eq(users.status, "active"),
  gt(users.createdAt, "2024-01-01")
))

// With OR
select().from(users).where(or(
  eq(users.role, "admin"),
  eq(users.role, "moderator")
))

// With limit and offset
select().from(users).limit(10).start(20)

// Order by
select().from(users).orderBy("createdAt", "DESC")

Insert

import { insert, compileInsert } from 'surorm';

// Single record
insert(users).values({ 
  id: "user:1", 
  email: "test@example.com" 
})

// Multiple records
insert(users).values([
  { id: "user:1", email: "a@test.com" },
  { id: "user:2", email: "b@test.com" }
])

const { sql, params } = compileInsert(query);
// sql: "INSERT INTO user [{ id: $1, email: $2 }, { id: $3, email: $4 }]"

Update

import { update, compileUpdate } from 'surorm';

// Update all records
update(users).set({ status: "inactive" })

// Update with where clause
update(users)
  .set({ email: "new@example.com" })
  .where(eq(users.id, "user:1"))

const { sql, params } = compileUpdate(query);
// sql: "UPDATE user SET email = $1 WHERE id = $2"

Delete

import { remove, compileDelete } from 'surorm';

// Delete all
remove(users)

// Delete with condition
remove(users).where(eq(users.status, "deleted"))

const { sql, params } = compileDelete(query);
// sql: "DELETE FROM user WHERE status = $1"

Conditions

Comparison Operators

import { eq, ne, gt, gte, lt, lte, like } from 'surorm';

eq(users.id, "user:1")          // =
ne(users.status, "active")      // !=
gt(users.age, 18)                // >
gte(users.age, 18)               // >=
lt(users.age, 65)                // <
lte(users.age, 65)               // <=
like(users.name, "%john%")       // LIKE

Logical Operators

import { and, or } from 'surorm';

// AND - all conditions must match
and(
  eq(users.status, "active"),
  gt(users.age, 18)
)

// OR - any condition can match
or(
  eq(users.role, "admin"),
  eq(users.role, "moderator")
)

Graph (RELATE)

Create relationships between records using SurrealDB's RELATE statements.

import { edge, relate, compileRelate } from 'surorm';

// Define tables
const users = table("user", { id: id("id") });
const posts = table("post", { id: id("id"), title: string("title") });

// Define edge relationship
const authored = edge("authored", users, posts);

// Create relationship
relate(users.id("user:1"), authored, posts.id("post:42"))

// With data
relate(users.id("user:1"), authored, posts.id("post:42"))
  .set({ createdAt: "time::now()" })

const { sql, params } = compileRelate(query);
// sql: "RELATE user:1->authored->post:42 SET createdAt = $1"

Transactions

Execute multiple operations atomically with transaction support.

import { SurrealORMClient } from 'surorm';
import Surreal from 'surrealdb';

const db = new Surreal();
await db.connect("ws://localhost:8000");
await db.use({ namespace: "test", database: "test" });

const client = new SurrealORMClient(db);

await client.transaction(async (tx) => {
  // All operations in transaction
  await tx.query("INSERT INTO user { id: 'user:1', email: 'test@example.com' }");
  await tx.query("INSERT INTO post { title: 'Hello World' }");
  
  // If no error, transaction commits automatically
});

Migrations

Schema Snapshots

Capture current schema state:

import { createSnapshot } from 'surorm';

const users = table("user", {
  id: id("id"),
  email: string("email").unique(),
});

const posts = table("post", {
  id: id("id"),
  title: string("title"),
});

const snapshot = createSnapshot([users, posts]);
// snapshot.tables: [{ name: "post", columns: [...] }, { name: "user", columns: [...] }]

Diff Engine

Generate migration SQL from schema changes:

import { generateDiff, createSnapshot } from 'surorm';

const oldSnapshot = createSnapshot([users]);  // Before
const newSnapshot = createSnapshot([users, posts]);  // After

const diff = generateDiff(oldSnapshot, newSnapshot);
// diff.up: ["DEFINE TABLE post SCHEMAFULL;", "DEFINE FIELD id ON post TYPE string;", ...]
// diff.down: ["REMOVE TABLE post;"]

Migration Runner

Apply migrations automatically:

import { MigrationRunner } from 'surorm';

const runner = new MigrationRunner(db);

// Run migrations
const result = await runner.runMigrations(
  [users, posts],
  [{ name: "initial", sql: "..." }]
);
// result.applied: ["initial"]
// result.skipped: []

API Reference

Schema Functions

Function Description
table(name, columns) Define a table
id(name) ID column
string(name) String column
datetime(name) Datetime column

Query Functions

Function Description
select() Create select query
from(table) Specify source table
insert(table) Create insert query
update(table) Create update query
remove(table) Create delete query

Condition Functions

Function Description
eq(a, b) Equal
ne(a, b) Not equal
gt(a, b) Greater than
gte(a, b) Greater or equal
lt(a, b) Less than
lte(a, b) Less or equal
like(a, b) Like pattern
and(...c) AND conditions
or(...c) OR conditions

Compiler Functions

Function Description
compileSelect(query) Compile select to SQL
compileInsert(query) Compile insert to SQL
compileUpdate(query) Compile update to SQL
compileDelete(query) Compile delete to SQL
compileRelate(query) Compile relate to SQL

Examples

Full CRUD Example

import { 
  table, id, string, datetime,
  select, insert, update, remove,
  eq, compileSelect, compileInsert, compileUpdate, compileDelete
} from 'surorm';

// Schema
const users = table("user", {
  id: id("id"),
  email: string("email").unique(),
  name: string("name"),
  createdAt: datetime("created_at").default("time::now()"),
});

// CREATE
const insertResult = compileInsert(
  insert(users).values({ id: "user:1", email: "test@example.com", name: "John" })
);
console.log(insertResult);
// { sql: "INSERT INTO user { id: $1, email: $2, name: $3 }", params: ["user:1", "test@example.com", "John"] }

// READ
const selectResult = compileSelect(
  select().from(users).where(eq(users.email, "test@example.com"))
);
console.log(selectResult);
// { sql: "SELECT * FROM user WHERE email = $1", params: ["test@example.com"] }

// UPDATE
const updateResult = compileUpdate(
  update(users).set({ name: "Jane" }).where(eq(users.id, "user:1"))
);
console.log(updateResult);
// { sql: "UPDATE user SET name = $1 WHERE id = $2", params: ["Jane", "user:1"] }

// DELETE
const deleteResult = compileDelete(
  remove(users).where(eq(users.id, "user:1"))
);
console.log(deleteResult);
// { sql: "DELETE FROM user WHERE id = $1", params: ["user:1"] }

Testing

bun test

All 41 tests passing.


License

MIT


OwO

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published