SurrealDB Adapter
Resources
Setup
Installation
npm install @auth/surrealdb-adapter surrealdb
Environment Variables
A valid authentication combination must be provided. The following authentication combinations are supported:
- RootAuth
- NamespaceAuth
- DatabaseAuth
- ScopeAuth
AUTH_SURREAL_URL (required)
AUTH_SURREAL_NS
AUTH_SURREAL_DB
AUTH_SURREAL_USER
AUTH_SURREAL_PW
AUTH_SURREAL_SCOPE
SURREAL_NS (required when using RootAuth or NamespaceAuth)
SURREAL_DB (required when using RootAuth or NamespaceAuth)
Configuration
import NextAuth from "next-auth"
import { SurrealDBAdapter } from "@auth/surrealdb-adapter"
import clientPromise from "./lib/surrealdb"
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [],
adapter: SurrealDBAdapter(clientPromise),
})
The SurrealDB adapter does not handle connections automatically, so you will have to make sure that you pass the Adapter a SurrealDBClient
that is connected already. Below you can see an example how to do this.
Utils File
import type { ConnectOptions, AnyAuth } from "surrealdb"
import { Surreal, ConnectionStatus } from "surrealdb"
/**
* Maintains a single instance of surrealdb.
* Automatically reconnects unless options.reconnect is set to false manually.
*/
export class MySurreal {
// A single instantiation of a surreal connection
private _surrealdb: Surreal | undefined
private _url: string | URL
private _opts: ConnectOptions | undefined
constructor(
url: Parameters<InstanceType<typeof Surreal>["connect"]>[0],
opts?: ConnectOptions
) {
this._url = url
if (opts && opts.reconnect === undefined) {
opts.reconnect = true
}
this._opts = opts
}
async surrealdb(): Promise<Surreal> {
// Init Surreal
if (!this._surrealdb) {
this._surrealdb = new Surreal()
}
if (this._surrealdb.status == ConnectionStatus.Connected) {
return this._surrealdb
} else if (this._surrealdb.status == ConnectionStatus.Disconnected) {
try {
// Connect as a database user
await this._surrealdb.connect(this._url, this._opts)
if (process.env.NODE_ENV === "development") {
const str = this.toConnectionString(
this._surrealdb.status,
this._opts
)
console.info(str)
}
} catch (error) {
if (error instanceof Error) throw error
throw new Error(error as unknown as string)
}
}
return this._surrealdb
}
private toConnectionString(status: ConnectionStatus, opts?: ConnectOptions) {
let str = `${status}`
const auth = opts?.auth
if (auth && typeof auth !== "string") {
if ("username" in auth) {
str += ` as ${auth.username}`
}
if ("database" in auth && "namespace" in auth) {
str += ` for ${auth.namespace} ${auth.database}`
} else if ("namespace" in auth) {
str += ` for ${auth.namespace}`
} else if ("database" in auth) {
str += ` for ${auth.database}`
}
}
return str
}
}
/**
* Converts environment variables to an AnyAuth type
* to connect with the database
* @param param0 - environment variables
* @returns {RootAuth | NamespaceAuth | DatabaseAuth | ScopeAuth}
*/
export function toAnyAuth({
username,
password,
namespace,
database,
scope,
}: Record<string, string | undefined>) {
let auth: AnyAuth
if (username && password && namespace && database) {
auth = {
database,
namespace,
username,
password,
}
} else if (username && password && namespace) {
auth = {
namespace,
username,
password,
}
} else if (username && password) {
auth = {
username,
password,
}
} else if (scope) {
auth = {
namespace,
database,
username,
password,
scope,
}
} else {
throw new Error("unsupported any auth configuration")
}
return auth
}
Authorization
The clientPromise provides a connection to the database. You could use any connect option you wish. For quick setup, use the DatabaseAuth method. For best security, we recommend creating a Record Access method if you know how to properly setup access table permissions.
import { type Surreal } from "surrealdb"
import { handleDisconnect, MySurreal, toAnyAuth } from "./lib/surrealdb_utils"
const clientPromise = new Promise<Surreal>(async (resolve, reject) => {
try {
const {
AUTH_SURREAL_URL: auth_url,
AUTH_SURREAL_NS: auth_ns,
AUTH_SURREAL_DB: auth_db,
AUTH_SURREAL_USER: auth_user,
AUTH_SURREAL_PW: auth_pw,
AUTH_SURREAL_SCOPE: auth_scope,
SURREAL_NS: namespace,
SURREAL_DB: database,
} = process.env
if (!auth_url) throw new Error("required auth_url")
const auth = toAnyAuth({
namespace: auth_ns,
database: auth_db,
username: auth_user,
password: auth_pw,
scope: auth_scope,
})
const surreal = new MySurreal(auth_url, {
namespace,
database,
auth,
})
const db = await surreal.surrealdb()
resolve(db)
} catch (e) {
reject(e)
}
})
HTTP ENGINE
With this configuration, we can use the database’s http endpoint. Thus, the AUTH_SURREAL_URL
should begin with http
or https
.
Websocket ENGINE
With this configuration, we can use the database’s websocket endpoint. Thus, the AUTH_SURREAL_URL
should begin with ws
or wss
.