Skip to content
🚀 This documentation is for unreal-orm 1.0.0 alpha which requires SurrealDB 2.0 alpha SDK. For the stable version, see npm.

Migrating 0.x → 1.0.0 alpha

This guide helps you upgrade your UnrealORM codebase to 1.0.0 alpha. The 1.0.0 release includes major improvements including transaction support, SurrealDB JS SDK 2.0 alpha integration, and enhanced update APIs.

Extra fields (fields not defined in your schema) are now assigned directly to model instances instead of being stored in the $dynamic property.

Before (pre-1.0):

const user = await User.select(db, { only: true, limit: 1 });
// Extra fields were in $dynamic
console.log(user.$dynamic.someExtraField);

After (1.0.0 alpha):

const user = await User.select(db, { only: true, limit: 1 });
// Extra fields are now directly on the instance
console.log(user.someExtraField);

Before (pre-1.0):

// Instance methods
await user.update(db, { name: "Jane" });
await user.merge(db, { name: "Jane" });
// Static methods
await User.update(db, "user:123", { name: "Jane" });
await User.merge(db, "user:123", { name: "Jane" });

After (1.0.0 alpha):

// Instance methods - now requires explicit mode
await user.update(db, { data: { name: "Jane" }, mode: "merge" });
await user.update(db, { data: { name: "Jane" }, mode: "content" });
// Static methods - now requires explicit mode
await User.update(db, "user:123", {
data: { name: "Jane" },
mode: "merge",
});

The merge method has been removed. Use update with mode: 'merge' instead.

Before:

await user.merge(db, { name: "Jane" });

After:

await user.update(db, { data: { name: "Jane" }, mode: "merge" });

The from method now supports surql template literals and raw queries in addition to record IDs.

Before (pre-1.0):

// Only record IDs were supported
const user = await User.from(db, "user:123");

After (1.0.0 alpha):

import { surql } from "surrealdb";
// Record ID (still works)
const user = await User.from(db, "user:123");
// SurrealQL query
const users = await User.from(db, surql`SELECT * FROM user WHERE age > 18`);
// Raw query string
const users = await User.from(db, { raw: "SELECT * FROM user WHERE active = true" });

UnrealORM 1.0.0 alpha requires SurrealDB 2.0 alpha. Update your dependencies:

package.json:

{
"dependencies": {
"surrealdb": "^2.0.0-alpha.14",
"@surrealdb/node": "2.3.4" // if you run embedded, such as for internal testing
}
}

Field options now use BoundQuery and Expr types instead of strings for better type safety.

Before:

Field.string({
assert: "$value CONTAINS '@'",
default: "'unknown@example.com'",
});

After:

import { surql } from "surrealdb";
Field.string({
assert: surql`$value CONTAINS "@"`,
default: surql`"unknown@example.com"`,
});

UnrealORM 1.0.0 alpha introduces a new CLI package for schema management:

Terminal window
# Initialize a new project (recommended starting point)
bunx @unreal-orm/cli init
# Or with other package managers
npx @unreal-orm/cli init
pnpm dlx @unreal-orm/cli init
yarn dlx @unreal-orm/cli init

The init command will:

  • Configure your database connection
  • Set up the project structure (unreal/ folder)
  • Install dependencies (unreal-orm, surrealdb, @unreal-orm/cli)
  • Optionally generate sample tables or import from existing database
CommandDescription
initInitialize UnrealORM in your project
pullIntrospect database and generate TypeScript schema
pushApply TypeScript schema to database
diffCompare code schema with database schema
mermaidGenerate ERD diagrams from your schema
viewInteractive TUI for browsing and editing records
Terminal window
# Initialize a new project
bunx @unreal-orm/cli init
# After init, use the CLI directly
unreal pull # Generate TypeScript from database
unreal push # Apply schema to database
unreal diff # Compare code vs database

The pull command now features intelligent merging that preserves your customizations:

  • Adds new fields/indexes with // Added from database comments
  • Comments out removed fields/indexes for review (never deletes your code)
  • Preserves your custom methods, comments, and formatting

See the CLI README for full documentation.

UnrealORM 1.0.0 alpha introduces optional implicit database connections. Configure your database once, then call CRUD methods without passing db explicitly.

Run unreal init to set up your project:

Terminal window
bunx @unreal-orm/cli init

This creates:

  • unreal/surreal.ts - Database connection with Unreal.configure() call
  • unreal.config.json - Project configuration

The generated surreal.ts automatically configures the ORM when imported. Just add this import to your app’s entry point:

// In your main.ts, index.ts, or app entry point
import "./unreal/surreal";

All CRUD methods now support two calling patterns:

// Pattern 1: Explicit db (always works)
const users = await User.select(db, { limit: 10 });
const user = await User.create(db, { name: "John" });
await user.update(db, { data: { name: "Jane" }, mode: "merge" });
await user.delete(db);
// Pattern 2: Implicit db (uses configured default)
const users = await User.select({ limit: 10 });
const user = await User.create({ name: "John" });
await user.update({ data: { name: "Jane" }, mode: "merge" });
await user.delete();
MethodExplicitImplicit
Model.select(db, options)Model.select(options)
Model.create(db, data)Model.create(data)
Model.update(db, id, options)Model.update(id, options)
Model.delete(db, id)Model.delete(id)
instance.update(db, options)instance.update(options)
instance.delete(db)instance.delete()

The easiest way is to run the init command which handles everything:

Terminal window
bunx @unreal-orm/cli init

Or manually update:

Terminal window
# Update SurrealDB to 2.0 alpha
bun add surrealdb@alpha
bun add @surrealdb/node@alpha # if using embedded mode
# Update UnrealORM and install CLI
bun add unreal-orm@latest
bun add -D @unreal-orm/cli@latest

Find all instances of .update() and .merge() calls and update them:

Terminal window
# Search for update calls (manual review required)
grep -r "\.update(" src/
grep -r "\.merge(" src/

Migration patterns:

// Pattern 1: Simple updates
// Before
await user.update(db, { name: "Jane" });
// After
await user.update(db, { data: { name: "Jane" }, mode: "merge" });
// Pattern 2: Merge calls
// Before
await user.merge(db, { name: "Jane" });
// After
await user.update(db, { data: { name: "Jane" }, mode: "merge" });
// Pattern 3: Static updates
// Before
await User.update(db, "user:123", { name: "Jane" });
// After
await User.update(db, "user:123", {
data: { name: "Jane" },
mode: "content",
});

Update field options to use surql templates:

import { surql } from "surrealdb";
// Before
class User extends Table.normal({
name: "user",
fields: {
email: Field.string({
assert: "$value CONTAINS '@'",
default: "'unknown@example.com'",
}),
active: Field.bool({ default: "true" }),
},
}) {}
// After
class User extends Table.normal({
name: "user",
fields: {
email: Field.string({
assert: surql`$value CONTAINS "@"`,
default: surql`"unknown@example.com"`,
}),
active: Field.bool({ default: surql`true` }),
},
}) {}
import { Surreal } from "surrealdb";
const db = new Surreal();
await db.connect("memory");
// Start a transaction
const tx = await db.beginTransaction();
try {
// All operations now use the transaction instead of db
const user = await User.create(tx, { name: "Alice" });
const post = await Post.create(tx, { title: "Hello", author: user.id });
await user.update(tx, { data: { name: "Alice Smith" }, mode: "merge" });
// Commit the transaction
await tx.commit();
} catch (error) {
// Rollback on error
await tx.cancel();
throw error;
}

Check if transactions are supported in your SurrealDB version:

import { Features } from "surrealdb";
if (db.isFeatureSupported(Features.Transactions)) {
// Transactional operations
const tx = await db.beginTransaction();
// ... use transaction
} else {
// Fallback to non-transactional operations
console.warn("Transactions not supported, using regular operations");
}
ModeDescriptionUse Case
contentFull document replacementComplete record updates
mergePartial field updatesIncremental changes
replaceFull document replacement (alias)Same as content
patchJSON Patch operationsComplex field-level changes
// 1. Simple field updates (most common)
// Before
await user.update(db, { name: "Jane" });
await user.merge(db, { email: "jane@example.com" });
// After
await user.update(db, { data: { name: "Jane" }, mode: "merge" });
await user.update(db, { data: { email: "jane@example.com" }, mode: "merge" });
// 2. Complete record replacement
// Before (update with all required fields)
await user.update(db, { name: "Jane", email: "jane@example.com", age: 30 });
// After
await user.update(db, {
data: { name: "Jane", email: "jane@example.com", age: 30 },
mode: "content",
});
// 3. JSON Patch operations (new in 1.0.0)
const user = await User.update(db, "user:123", {
data: [
{ op: "replace", path: "/name", value: "Jane" },
{ op: "add", path: "/age", value: 30 },
],
mode: "patch",
});
import { surql, BoundQuery, Expr } from "surrealdb";
// Before (string-based queries)
const users = await User.select(db, {
where: "age > 18 AND status = 'active'",
});
// After (syntax highlighted and automatic variable binding)
const users = await User.select(db, {
// using surql template
where: surql`age > 18 AND status = 'active'`,
// or using Expr api
where: and(eq("active", true), gte("age", age)),
// or both!
where: surql`${eq("active", true)} AND age >= ${age}`,
});
// Before
Field.string({
permissions: {
select: "WHERE $auth.id = owner",
},
});
// After
Field.string({
permissions: {
select: surql`WHERE $auth.id = owner`,
},
});

Error: Argument of type '{ name: string }' is not assignable to parameter of type 'UpdateOptions<...>'

Solution: Wrap data in data property and specify mode:

// Wrong
await user.update(db, { name: "Jane" });
// Correct
await user.update(db, { data: { name: "Jane" }, mode: "merge" });

Error: Type 'string' is not assignable to type 'BoundQuery | Expr'

Solution: Use surql template literals:

import { surql } from "surrealdb";
// Wrong
Field.string({ default: "default_value" });
// Correct
Field.string({ default: surql`default_value` });

Error: beginTransaction is not a function

Solution: Check SurrealDB version and feature support:

import { Features } from "surrealdb";
if (!db.isFeatureSupported(Features.Transactions)) {
console.log("Transactions require SurrealDB v3 (alpha)");
// Use regular db operations instead
}
  • Update SurrealDB to 2.0 alpha
  • Update UnrealORM to 1.0.0 alpha
  • Run bunx @unreal-orm/cli init or install CLI manually
  • Replace .$dynamic.field with .field (direct property access)
  • Replace all .merge() calls with .update({ mode: 'merge' })
  • Update all .update() calls to use new signature
  • Update .from() calls if using raw queries (now supports surql and { raw: ... })
  • Convert field options to use surql templates
  • Add transaction support where needed
  • Test all CRUD operations
  • Verify type safety with TypeScript compiler
  • Use unreal diff to verify schema sync
  • (Optional) Run unreal init to enable implicit database support