Files
skills/docs/identity/auth/better-auth-surrealdb/README.md
2026-05-29 08:20:56 +01:00

178 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# @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.
<!-- begin:auto-docs -->
## Documentation tree
- [adapter](./src/adapter/adapter.md) — The package's flagship export. `createBetterAuthSurrealAdapter(options)` returns a Better-Auth `Adapter` whose 8 contra…
- [adapter](./src/adapter/adapter.md) — The package's flagship export. `createBetterAuthSurrealAdapter(options)` returns a Better-Auth `Adapter` whose 8 contra…
- [internals](./src/adapter/internals.md) — Shared internal helpers consumed by the adapter shell (`adapter.ts`) and the per-method operation runners (`operations.…
- [model-to-table](./src/adapter/model-to-table.md) — Memoising mapper from Better-Auth model name to SurrealDB table name. The mapper function is invoked once per distinct…
- [operations](./src/adapter/operations.md) — Hosts the five primitive runners (`runCreate`, `runFindMany`, `runUpdateMany`, `runDeleteMany`, `runCount`) that transl…
- [v1.6](./src/adapter/v1.6.md) — `betterAuthSurrealAdapter(options)` is the Better-Auth **v1.6** integration surface for `@suaveplan/better-auth-surreal…
- [compile-select](./src/compile/compile-select.md) — Compiles a Better-Auth `select?: string[]` projection into a SurrealQL column list. Used inside the adapter's `SELECT`…
- [compile-sort](./src/compile/compile-sort.md) — Compiles a Better-Auth `sortBy?: { field; direction }` argument into a SurrealQL `ORDER BY` fragment, validating the fi…
- [compile-where](./src/compile/compile-where.md) — Translates Better-Auth's `Where[]` filter union into a parameterised SurrealQL `WHERE` fragment plus a binding record.…
- [error-codes](./src/error-codes/error-codes.md) — Defines the closed registry of error codes raised from every public path of `@suaveplan/better-auth-surrealdb`. Stable…
- [error-codes](./src/error-codes/error-codes.md) — Defines the closed registry of error codes raised from every public path of `@suaveplan/better-auth-surrealdb`. Stable…
- [id-strategy](./src/id-strategy/id-strategy.md) — Encapsulates the three Better-Auth record-id strategies the adapter supports. SurrealDB's native record-id format is `<…
- [id-strategy](./src/id-strategy/id-strategy.md) — Encapsulates the three Better-Auth record-id strategies the adapter supports. SurrealDB's native record-id format is `<…
- [link-fields](./src/link-fields/link-fields.md) — Encapsulates the central design decision of this package: SurrealDB stores record references as typed `record<target>`…
- [link-fields](./src/link-fields/link-fields.md) — Encapsulates the central design decision of this package: SurrealDB stores record references as typed `record<target>`…
- [otel](./src/otel/instruments.md) — Wires the package's OpenTelemetry metrics surface and exposes a thin `instrument(op)` helper the adapter wraps around e…
- [instruments](./src/otel/instruments.md) — Wires the package's OpenTelemetry metrics surface and exposes a thin `instrument(op)` helper the adapter wraps around e…
- [schema](./src/schema/schema.md) — Bundles the canonical 17-table Better-Auth SurrealQL schema as a frozen string constant `BETTER_AUTH_SURREAL_SCHEMA` an…
- [schema](./src/schema/schema.md) — Bundles the canonical 17-table Better-Auth SurrealQL schema as a frozen string constant `BETTER_AUTH_SURREAL_SCHEMA` an…
- [link-fields](./src/schemas/link-fields.md) — Zod boundary schema asserting the structural shape of a `LinkFieldMap`. Used to validate the optional `linkFields` argu…
- [options](./src/schemas/options.md) — Zod boundary schema for `BetterAuthSurrealAdapterOptions`. Drives the runtime validation step inside `createBetterAuthS…
- [testing](./src/testing/run-adapter-conformance.md) — Exposes `runAdapterConformance(factory)` — the reusable suite that pins every Better-Auth Adapter scenario this package…
- [run-adapter-conformance](./src/testing/run-adapter-conformance.md) — Exposes `runAdapterConformance(factory)` — the reusable suite that pins every Better-Auth Adapter scenario this package…
- [types](./src/types/adapter.md) — Type-only foundation for the entire package. Defines the public option shape (`BetterAuthSurrealAdapterOptions`), the r…
- [adapter](./src/types/adapter.md) — Type-only foundation for the entire package. Defines the public option shape (`BetterAuthSurrealAdapterOptions`), the r…
<!-- end:auto-docs -->
## Quick start
```ts
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:
```ts
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:
```ts
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
```ts
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`.