Skip to content

@ereo/db-surrealdb ​

SurrealDB adapter for the EreoJS database abstraction layer. SurrealDB is a multi-model database that supports SQL-like queries, graph relationships, and real-time subscriptions.

Installation ​

bash
bun add @ereo/db-surrealdb @ereo/db surrealdb

Quick Start ​

ts
import { createSurrealAdapter, defineSurrealConfig } from '@ereo/db-surrealdb'
import { createDatabasePlugin } from '@ereo/db'
import { defineConfig } from '@ereo/core'

const adapter = createSurrealAdapter(defineSurrealConfig({
  url: 'http://localhost:8000',
  namespace: 'test',
  database: 'test',
  auth: {
    username: 'root',
    password: 'root',
  },
}))

export default defineConfig({
  plugins: [createDatabasePlugin(adapter)],
})

Import ​

ts
import {
  // Adapter
  createSurrealAdapter,

  // Configuration
  defineSurrealConfig,
  validateConfig,

  // Authentication helpers
  rootAuth,
  namespaceAuth,
  databaseAuth,
  recordAccessAuth,

  // URL helpers
  buildSurrealUrl,
  parseSurrealUrl,

  // Configuration presets
  localConfig,
  cloudConfig,
  envConfig,

  // Query helpers
  select,
  create,
  update,
  deleteFrom,

  // Type guards
  isRootAuth,
  isNamespaceAuth,
  isDatabaseAuth,
  isRecordAccessAuth,
  isScopeAuth,

  // Types
  type SurrealClient,
  type SurrealDBConfig,
  type SurrealAuth,
  type RootAuth,
  type NamespaceAuth,
  type DatabaseAuth,
  type RecordAccessAuth,
  type ScopeAuth,
  type ConnectionProtocol,
  type SurrealEngine,
  type SurrealQueryResult,
  type SurrealRawResponse,
  type RecordId,
  type SurrealRecord,
  type LiveAction,
  type LiveNotification,

  // Re-exports from @ereo/db
  createDatabasePlugin,
  useDb,
  useAdapter,
  getDb,
  withTransaction,
} from '@ereo/db-surrealdb'

createSurrealAdapter ​

Creates a SurrealDB database adapter implementing the DatabaseAdapter interface.

Signature ​

ts
function createSurrealAdapter(config: SurrealDBConfig): DatabaseAdapter<SurrealClient>

Example ​

ts
const adapter = createSurrealAdapter({
  url: 'http://localhost:8000',
  namespace: 'myapp',
  database: 'production',
  auth: rootAuth('root', 'password'),
  timeout: 30000,
  debug: false,
})

Adapter Properties ​

PropertyValueDescription
name'surrealdb'Adapter identifier
edgeCompatibletrueWorks in edge runtimes via HTTP/WebSocket

Configuration ​

defineSurrealConfig ​

Type-safe configuration builder with sensible defaults.

ts
function defineSurrealConfig(config: SurrealDBConfig): SurrealDBConfig

SurrealDBConfig ​

ts
interface SurrealDBConfig {
  // Required
  url: string           // Connection URL
  namespace: string     // Target namespace
  database: string      // Target database

  // Optional
  auth?: SurrealAuth    // Authentication credentials
  timeout?: number      // Connection timeout in ms (default: 30000)
  debug?: boolean       // Enable debug logging (default: false)
}

URL Formats ​

FormatDescription
http://host:8000HTTP connection
https://hostHTTPS connection
ws://host:8000WebSocket connection
wss://hostSecure WebSocket
mem://In-memory (requires @surrealdb/node)
surrealkv://pathPersistent storage (requires @surrealdb/node)

Authentication ​

Root Authentication ​

Full access to all namespaces and databases.

ts
import { rootAuth } from '@ereo/db-surrealdb'

const auth = rootAuth('root', 'password')
// { username: 'root', password: 'password' }

Namespace Authentication ​

Access to a specific namespace.

ts
import { namespaceAuth } from '@ereo/db-surrealdb'

const auth = namespaceAuth('myns', 'admin', 'password')
// { namespace: 'myns', username: 'admin', password: 'password' }

Database Authentication ​

Access to a specific database within a namespace.

ts
import { databaseAuth } from '@ereo/db-surrealdb'

const auth = databaseAuth('myns', 'mydb', 'admin', 'password')
// { namespace: 'myns', database: 'mydb', username: 'admin', password: 'password' }

Record Access Authentication (SurrealDB 2.x+) ​

User-defined access control.

ts
import { recordAccessAuth } from '@ereo/db-surrealdb'

const auth = recordAccessAuth('myns', 'mydb', 'user', {
  email: 'user@example.com',
  password: 'secret',
})
// { namespace: 'myns', database: 'mydb', access: 'user', variables: {...} }

Type Guards ​

ts
import {
  isRootAuth,
  isNamespaceAuth,
  isDatabaseAuth,
  isRecordAccessAuth,
  isScopeAuth,
} from '@ereo/db-surrealdb'

if (isRootAuth(auth)) {
  console.log('Root auth:', auth.username)
} else if (isDatabaseAuth(auth)) {
  console.log('Database auth for:', auth.database)
}

Configuration Presets ​

localConfig ​

Quick setup for local development.

ts
import { localConfig, rootAuth } from '@ereo/db-surrealdb'

const config = localConfig('test', 'test', {
  auth: rootAuth('root', 'root'),
})
// url: 'http://127.0.0.1:8000', debug: true

cloudConfig ​

Production configuration for SurrealDB Cloud or remote instances.

ts
import { cloudConfig, rootAuth } from '@ereo/db-surrealdb'

const config = cloudConfig(
  'https://cloud.surrealdb.com',
  'production',
  'mydb',
  rootAuth('user', 'password')
)
// debug: false

envConfig ​

Load configuration from environment variables.

ts
import { envConfig } from '@ereo/db-surrealdb'

const config = envConfig()

Required environment variables:

  • SURREAL_URL - Connection URL
  • SURREAL_NAMESPACE - Target namespace
  • SURREAL_DATABASE - Target database

Optional environment variables:

  • SURREAL_USERNAME - Auth username
  • SURREAL_PASSWORD - Auth password
  • SURREAL_DEBUG - Enable debug ('true')

URL Helpers ​

buildSurrealUrl ​

Build a connection URL from components.

ts
import { buildSurrealUrl } from '@ereo/db-surrealdb'

const url = buildSurrealUrl('localhost', 8000, 'http')
// 'http://localhost:8000'

const secureUrl = buildSurrealUrl('cloud.surrealdb.com', 443, 'wss')
// 'wss://cloud.surrealdb.com:443'

parseSurrealUrl ​

Parse a connection URL into components.

ts
import { parseSurrealUrl } from '@ereo/db-surrealdb'

const parts = parseSurrealUrl('https://cloud.surrealdb.com:443/rpc')
// { protocol: 'https', host: 'cloud.surrealdb.com', port: 443, path: '/rpc' }

Query Helpers ​

Helper functions to build SurrealQL query strings.

select ​

ts
import { select } from '@ereo/db-surrealdb'

select('users')
// 'SELECT * FROM users'

select('users', { where: 'active = true', orderBy: 'created DESC', limit: 10 })
// 'SELECT * FROM users WHERE active = true ORDER BY created DESC LIMIT 10'

select('posts', { start: 20, limit: 10 })
// 'SELECT * FROM posts LIMIT 10 START 20'

create ​

ts
import { create } from '@ereo/db-surrealdb'

create('users')
// 'CREATE users'

create('users', 'john')
// 'CREATE users:john'

update ​

ts
import { update } from '@ereo/db-surrealdb'

update('users')
// 'UPDATE users'

update('users', 'john')
// 'UPDATE users:john'

deleteFrom ​

ts
import { deleteFrom } from '@ereo/db-surrealdb'

deleteFrom('users')
// 'DELETE users'

deleteFrom('users', 'john')
// 'DELETE users:john'

Using in Routes ​

In Loaders ​

ts
import { createLoader } from '@ereo/data'
import { useDb } from '@ereo/db-surrealdb'

export const loader = createLoader({
  load: async ({ context }) => {
    const db = useDb(context)

    // Raw SurrealQL query
    const result = await db.client.query<User[]>(
      'SELECT * FROM users WHERE active = true'
    )

    // Or use the client's select method
    const users = await db.client.select('users')

    return { users }
  },
})

In Actions ​

When using the options-object form of createAction, formData is already parsed and available in the handler arguments — no need to call request.formData() yourself:

ts
import { createAction } from '@ereo/data'
import { useDb } from '@ereo/db-surrealdb'

export const action = createAction({
  handler: async ({ context, formData }) => {
    const db = useDb(context)

    await db.client.create('posts', {
      title: formData.get('title'),
      content: formData.get('content'),
      created: new Date(),
    })

    return redirect('/posts')
  },
})

Transactions ​

Use withTransaction from @ereo/db to run multiple operations atomically. It takes the request context as the first argument and automatically clears the dedup cache after the transaction completes:

ts
import { withTransaction } from '@ereo/db-surrealdb'

export const action = createAction({
  handler: async ({ context }) => {
    return withTransaction(context, async (tx) => {
      await tx.query('UPDATE users:john SET balance -= 100')
      await tx.query('UPDATE users:jane SET balance += 100')
      return { success: true }
    })
  },
})

Type Definitions ​

SurrealAuth ​

ts
type SurrealAuth =
  | RootAuth
  | NamespaceAuth
  | DatabaseAuth
  | RecordAccessAuth
  | ScopeAuth

RecordId ​

ts
interface RecordId<T extends string = string> {
  tb: T        // Table name
  id: string | number | object  // Record ID
}

SurrealRecord ​

ts
interface SurrealRecord {
  id: RecordId | string
}

SurrealQueryResult ​

ts
interface SurrealQueryResult<T = unknown> {
  result: T
  status: 'OK' | 'ERR'
  time: string
}

LiveNotification ​

ts
type LiveAction = 'CREATE' | 'UPDATE' | 'DELETE'

interface LiveNotification<T = unknown> {
  action: LiveAction
  result: T
}

ConnectionProtocol ​

ts
type ConnectionProtocol = 'http' | 'https' | 'ws' | 'wss'

SurrealEngine ​

ts
type SurrealEngine =
  | 'remote'     // Remote SurrealDB instance
  | 'memory'     // In-memory (requires @surrealdb/node)
  | 'surrealkv'  // Persistent (requires @surrealdb/node)

SurrealQL Examples ​

CRUD Operations ​

ts
// Create
await db.client.create('user:john', {
  name: 'John Doe',
  email: 'john@example.com',
})

// Read
const users = await db.client.query('SELECT * FROM users WHERE age > $0', [18])

// Update
await db.client.query('UPDATE user:john SET name = $0', ['John Smith'])

// Delete
await db.client.query('DELETE user:john')

Graph Relationships ​

ts
// Create relationship
await db.client.query(`
  RELATE user:john->follows->user:jane
  SET created = time::now()
`)

// Query relationships
const followers = await db.client.query(`
  SELECT <-follows<-user.* AS followers
  FROM user:jane
`)

// Traverse graph
const recommendations = await db.client.query(`
  SELECT ->follows->user->follows->user AS suggested
  FROM user:john
  WHERE suggested != user:john
`)

Computed Fields ​

ts
await db.client.query(`
  SELECT
    *,
    string::concat(first_name, ' ', last_name) AS full_name,
    math::sum(->orders->product.price) AS total_spent
  FROM users
`)

Re-exports from @ereo/db ​

For convenience, the following are re-exported:

ts
export {
  createDatabasePlugin,
  useDb,
  useAdapter,
  getDb,
  withTransaction,
  type DatabaseAdapter,
  type RequestScopedClient,
  type QueryResult,
  type MutationResult,
  type DedupResult,
  type DedupStats,
  type TransactionOptions,
} from '@ereo/db'

Validation ​

validateConfig ​

Validates configuration and throws on errors.

ts
import { validateConfig } from '@ereo/db-surrealdb'

validateConfig(config) // Throws if invalid

Validates:

  • url is required and valid URL format
  • namespace is required
  • database is required

Released under the MIT License.