3.6 KiB
adapter
(no submodules)
Purpose
The package's flagship export. createBetterAuthSurrealAdapter(options)
returns a Better-Auth Adapter whose 8 contract methods plus
consumeOne and id are wired against a SurrealDB v3 connection. The
adapter validates options through Zod at the boundary, dispatches every
CRUD method through OTel-instrumented spans, applies link-field
prefix/strip on writes/reads, and compiles Where[] filters into
parameterised SurrealQL — no string interpolation, no mocks, no
vi.mock.
Features
- Stable
idconstant"@suaveplan/better-auth-surrealdb". - All 8 Better-Auth methods:
create,findOne,findMany,count,update,updateMany,delete,deleteMany, plus theconsumeOneprimitive Better-Auth v1.6+ adds atop the older 8 methods. - Link-field-aware writes and reads —
userId: "u1"becomesuserId: "user:u1"at rest forrecord<user>columns. - Three id-strategies (
record-iddefault,plain,ulid). - OTel counter + histogram via
instrument(). - Zod-validated options at the boundary; failures raise
BetterAuthSurrealError(INVALID_INPUT)with theZodErrorascause. - Connection-readiness check: every method validates
client.isConnectedand raisesCONNECTION_NOT_READYwhen the underlying SurrealAdapter has not connected. - Underlying
SurrealErrorfailures wrap inQUERY_FAILEDpreserving thecausechain.
Usage: Basic
import { SurrealAdapter } from "@suaveplan/surrealdb";
import { applyBetterAuthSchema } from "@suaveplan/better-auth-surrealdb/schema";
import { createBetterAuthSurrealAdapter } from "@suaveplan/better-auth-surrealdb/adapter";
const client = new SurrealAdapter({ url: "ws://localhost:8000" });
await client.connect();
await applyBetterAuthSchema(client);
const adapter = createBetterAuthSurrealAdapter({ client });
await adapter.create({
model: "session",
data: { id: "s1", userId: "u1", token: "t" },
});
Usage: Advanced
import { createBetterAuthSurrealAdapter } from "@suaveplan/better-auth-surrealdb/adapter";
const adapter = createBetterAuthSurrealAdapter({
client: surrealAdapter,
idStrategy: "ulid",
modelToTable: (m) => `bauth_${m}`,
linkFields: {
// override default — match a flat-string schema:
session: {},
member: { organizationId: "organization" },
},
});
const user = await adapter.findOne({
model: "user",
where: [{ field: "email", value: "x@y" }],
select: ["id", "email"],
});
API Reference
createBetterAuthSurrealAdapter(options): BetterAuthSurrealAdapterBETTER_AUTH_SURREAL_ADAPTER_ID = "@suaveplan/better-auth-surrealdb"BetterAuthSurrealAdapter— structural type satisfying Better-Auth'sDBAdapterminustransaction/createSchema(the host's own transaction primitive remains reachable viaSurrealAdapter.transaction).
Implementation Notes
The adapter's client.query() calls pass the binding record as
[vars] so both SurrealAdapter (which flattens a trailing record) and
hand-rolled SurrealClientView implementations receive the same shape.
update is implemented atop updateMany's RETURN AFTER row stream and
returns the first row — count() FROM <table> WHERE … GROUP ALL returns
a { c: <num> } row by aliasing; bare count() returns the count
unnamed which trips SurrealDB v3's stricter row shape. consumeOne
fetches the row first, then deletes by the same predicate; this is the
fallback path Better-Auth's factory installs when an adapter does not
expose a native consume primitive, and gives the same race semantics
under SurrealDB's read-then-delete pipeline.