@suaveplan/better-auth-surrealdb - Better-Auth Adapter for SurrealDB v3 — bundles the canonical 17-table SurrealQL schema, compiles Better-Auth Where[] filters to parameterised SurrealQL, auto-prefixes record-link fields on write, strips prefixes on read, supports record-id/plain/ulid id strategies, and ships an OTel-instrumented conformance harness for cross-package consumption.
Better-Auth Adapter for SurrealDB v3 — bundles the canonical 17-table
SurrealQL schema, compiles Better-Auth Where[] filters to parameterised
SurrealQL, auto-prefixes record-link fields on write, strips prefixes on
read, supports record-id / plain / ulid id strategies, and ships an
OTel-instrumented conformance harness for cross-package reuse.
Documentation tree
- adapter — The package's flagship export.
createBetterAuthSurrealAdapter(options)returns a Better-AuthAdapterwhose 8 contra…- adapter — The package's flagship export.
createBetterAuthSurrealAdapter(options)returns a Better-AuthAdapterwhose 8 contra… - internals — Shared internal helpers consumed by the adapter shell (
adapter.ts) and the per-method operation runners (`operations.… - model-to-table — Memoising mapper from Better-Auth model name to SurrealDB table name. The mapper function is invoked once per distinct…
- operations — Hosts the five primitive runners (
runCreate,runFindMany,runUpdateMany,runDeleteMany,runCount) that transl… - v1.6 —
betterAuthSurrealAdapter(options)is the Better-Auth v1.6 integration surface for `@suaveplan/better-auth-surreal…
- adapter — The package's flagship export.
- compile-select — Compiles a Better-Auth
select?: string[]projection into a SurrealQL column list. Used inside the adapter'sSELECT… - compile-sort — Compiles a Better-Auth
sortBy?: { field; direction }argument into a SurrealQLORDER BYfragment, validating the fi… - compile-where — Translates Better-Auth's
Where[]filter union into a parameterised SurrealQLWHEREfragment plus a binding record.… - error-codes — Defines the closed registry of error codes raised from every public path of
@suaveplan/better-auth-surrealdb. Stable…- error-codes — Defines the closed registry of error codes raised from every public path of
@suaveplan/better-auth-surrealdb. Stable…
- error-codes — Defines the closed registry of error codes raised from every public path of
- id-strategy — Encapsulates the three Better-Auth record-id strategies the adapter supports. SurrealDB's native record-id format is `<…
- id-strategy — Encapsulates the three Better-Auth record-id strategies the adapter supports. SurrealDB's native record-id format is `<…
- link-fields — Encapsulates the central design decision of this package: SurrealDB stores record references as typed
record<target>…- link-fields — Encapsulates the central design decision of this package: SurrealDB stores record references as typed
record<target>…
- link-fields — Encapsulates the central design decision of this package: SurrealDB stores record references as typed
- otel — Wires the package's OpenTelemetry metrics surface and exposes a thin
instrument(op)helper the adapter wraps around e…- instruments — Wires the package's OpenTelemetry metrics surface and exposes a thin
instrument(op)helper the adapter wraps around e…
- instruments — Wires the package's OpenTelemetry metrics surface and exposes a thin
- schema — Bundles the canonical 17-table Better-Auth SurrealQL schema as a frozen string constant
BETTER_AUTH_SURREAL_SCHEMAan…- schema — Bundles the canonical 17-table Better-Auth SurrealQL schema as a frozen string constant
BETTER_AUTH_SURREAL_SCHEMAan…
- schema — Bundles the canonical 17-table Better-Auth SurrealQL schema as a frozen string constant
- link-fields — Zod boundary schema asserting the structural shape of a
LinkFieldMap. Used to validate the optionallinkFieldsargu… - options — Zod boundary schema for
BetterAuthSurrealAdapterOptions. Drives the runtime validation step inside `createBetterAuthS… - testing — Exposes
runAdapterConformance(factory)— the reusable suite that pins every Better-Auth Adapter scenario this package…- run-adapter-conformance — Exposes
runAdapterConformance(factory)— the reusable suite that pins every Better-Auth Adapter scenario this package…
- run-adapter-conformance — Exposes
- types — Type-only foundation for the entire package. Defines the public option shape (
BetterAuthSurrealAdapterOptions), the r…- adapter — Type-only foundation for the entire package. Defines the public option shape (
BetterAuthSurrealAdapterOptions), the r…
- adapter — Type-only foundation for the entire package. Defines the public option shape (
Quick start
import { SurrealAdapter } from "@suaveplan/surrealdb";
import { applyBetterAuthSchema } from "@suaveplan/better-auth-surrealdb/schema";
import { betterAuthSurrealAdapter } from "@suaveplan/better-auth-surrealdb/adapter/v1.6";
import { betterAuth } from "better-auth";
const client = new SurrealAdapter({
url: "ws://localhost:8000",
namespace: "auth",
database: "auth",
username: "root",
password: process.env.SURREAL_PASSWORD,
});
await client.connect();
await applyBetterAuthSchema(client);
export const auth = betterAuth({
database: betterAuthSurrealAdapter({ client }),
secret: process.env.BETTER_AUTH_SECRET,
baseURL: "http://localhost:3000",
emailAndPassword: { enabled: true },
});
Advanced — direct conformance harness usage
The v1.4-shape createBetterAuthSurrealAdapter returns the 10-method
adapter directly (without the createAdapterFactory wrapper). This is the
surface the ./testing conformance harness consumes and the right
choice when you want to bypass Better-Auth's field-transformation layer:
import { createBetterAuthSurrealAdapter } from "@suaveplan/better-auth-surrealdb/adapter";
const adapter = createBetterAuthSurrealAdapter({ client });
await adapter.create({ model: "user", data: { id: "u1", email: "a@b" } });
Bundled schema — 17 Better-Auth tables
Mirrored byte-for-byte from
/mnt/f/surrealdb/surql/features/02-auth/02-better-auth.surql. Schema
version moves in lockstep with this package version. Upstream changes to
the canonical schema require a coordinated package version bump.
| Table | userId shape |
Notes |
|---|---|---|
user (extended) |
n/a | Extends host's user table with emailVerified/name/image |
session |
record<user> |
Has default linkFields.session.userId = "user" |
account |
string |
Flat-string userId — NOT in DEFAULT_BETTER_AUTH_LINK_FIELDS |
verification |
n/a | Single-use token storage |
passkey |
record<user> |
WebAuthn credentials |
jwks |
n/a | Server JWK pairs |
sso_provider |
option<record<user>> |
Optional record-link |
oauth_application |
string (option) |
Flat-string userId |
oauth_access_token |
string (option) |
Flat-string userId |
oauth_consent |
string (option) |
Flat-string userId |
organization |
n/a | Slug-unique tenant root |
member |
record × 2 | userId+organizationId both record-links |
invitation |
record × 3 | inviterId/organizationId/teamId record-links |
team |
record<organization> |
Belongs to org |
team_member |
record × 2 | userId+teamId |
two_factor |
record<user> |
TOTP secret + backup codes |
api_key |
record<user> |
Long-lived API keys with rate-limit fields |
Plus 13 indexes (account provider, verification identifier, SSO provider id, oauth client/access/refresh tokens, organization slug, passkey credential, user phone, api_key prefix/user, member/team uniqueness, invitation email/org).
linkFields — overriding the default record-link map
DEFAULT_BETTER_AUTH_LINK_FIELDS covers every record<...> column in
the bundled schema. Pass your own linkFields when your schema differs:
import { createBetterAuthSurrealAdapter } from "@suaveplan/better-auth-surrealdb/adapter";
// Flat-string schema everywhere — disable prefix manipulation:
createBetterAuthSurrealAdapter({ client, linkFields: {} });
// Mixed override:
createBetterAuthSurrealAdapter({
client,
linkFields: {
session: { userId: "user" }, // keep default
member: { userId: "user", organizationId: "tenant" }, // rename target
},
});
idStrategy — record-id format
| Strategy | Behaviour |
|---|---|
record-id (default) |
data.id becomes the SurrealDB record component (<table>:<id>). Rejects id containing :. Returned id is bare. |
plain |
id stored as a normal field; SurrealDB auto-generates the storage id. |
ulid |
When data.id missing, generates a 26-char Crockford-base32 ULID and applies record-id. |
Conformance harness
import { SurrealDBFixture } from "@suaveplan/testing-integration-surrealdb";
import { runAdapterConformance } from "@suaveplan/better-auth-surrealdb/testing";
const fixture = new SurrealDBFixture();
let adapter: ReturnType<typeof createBetterAuthSurrealAdapter>;
let client: SurrealAdapter;
beforeAll(async () => {
const cfg = await fixture.start();
client = new SurrealAdapter(cfg);
await client.connect();
await applyBetterAuthSchema(client);
adapter = createBetterAuthSurrealAdapter({ client });
}, 120_000);
afterAll(async () => {
await client.disconnect();
await fixture.stop();
});
runAdapterConformance(() => ({ client, adapter }));
Peer deps
better-auth^1.4.0 — supplied by the consuming app. Type-only at this package's boundary; never imported at runtime so the adapter ships cleanly to apps using any 1.4+ minor.
Subpath exports
., ./adapter, ./adapter/v1.6, ./compile, ./id-strategy,
./link-fields, ./schema, ./schemas/options, ./schemas/link-fields,
./testing, ./error-codes, ./otel, ./types.