Files
suaveplan/openspec/archive/2026-05-18-extend-better-auth-surrealdb-v1.6-adapter/proposal.md
2026-05-27 23:29:53 +01:00

5.3 KiB

Extend @suaveplan/better-auth-surrealdb with Better-Auth v1.6 DBAdapterInstance factory

Package: @suaveplan/better-auth-surrealdb Status: PROPOSED Type: feature (additive — preserves shipped v1.4-shape adapter)

  • Spec delta: ./specs/better-auth-surrealdb/spec.md

Why

@suaveplan/better-auth-surrealdb (shipped commit 2ed843a5, ~hours ago) exports createBetterAuthSurrealAdapter returning a 10-method Adapter matching Better-Auth v1.4's contract. The installed better-auth (catalog ^1.4.0 resolves to 1.6.11) expects BetterAuthOptions.database: DBAdapterInstance = (options: BetterAuthOptions) => DBAdapter<Options> — a factory returning a Kysely-flavored adapter with many more methods (transaction, createSchema, getDefaultIdField, etc.). The shapes are structurally incompatible, so createBetterAuth({ database: createBetterAuthSurrealAdapter(...) }) fails to type-check AND fails at runtime when Better-Auth invokes the missing methods.

Fix: add a v1.6-compatible factory export betterAuthSurrealAdapter(opts) that returns a DBAdapterInstance. Internally it builds the same 10-method underlying adapter we already ship and wraps it via createAdapterFactory from @better-auth/core/db — the official escape hatch Better-Auth provides for custom adapters. The existing createBetterAuthSurrealAdapter export stays unchanged (still useful for direct conformance harness usage and for hypothetical v1.4 callers).

What changes

Additive — no breaking changes to the existing surface. ADDED:

  • betterAuthSurrealAdapter(options): DBAdapterInstance — the v1.6 factory. Exported from root barrel + new subpath ./adapter/v1.6. Signature mirrors Better-Auth's DBAdapterInstance type: takes the parsed BetterAuthOptions at call time (inside createBetterAuth), returns the full DBAdapter.
  • Internally:
    1. Constructs the 10-method underlying adapter via createBetterAuthSurrealAdapter(options) (the existing surface).
    2. Wraps via createAdapterFactory({ ... }) from @better-auth/core/db. The wrapper presents the v1.6 DBAdapter to Better-Auth and forwards every operation through our adapter's create/findOne/findMany/update/updateMany/delete/deleteMany/count methods (the 10th method, consumeOne, is wired for atomic verification-token consumption).
    3. Translates the getDefaultIdField / transaction / createSchema methods Better-Auth expects:
      • getDefaultIdField: returns "id" (default — operators override via BetterAuthSurrealAdapterOptions.modelToTable only affects table names).
      • transaction: forwards to SurrealDB's runSurrealTransaction helper from @suaveplan/surrealdb.
      • createSchema: returns BETTER_AUTH_SURREAL_SCHEMA as a string so Better-Auth's CLI flows (@better-auth/cli generate) can emit the canonical schema. Idempotent per DEFINE … OVERWRITE.
  • BetterAuthSurrealAdapterV16Options — type alias for the v1.6 factory's options object. Identical to BetterAuthSurrealAdapterOptions except wraps the per-call BetterAuthOptions argument the factory receives. Zod-validated via the same BetterAuthSurrealAdapterOptionsSchema.
  • Subpath export: ./adapter/v1.6.
  • README quick-start updated to show v1.6 usage as the default. The v1.4-shape adapter is documented as "advanced — direct conformance harness usage."

Out of scope

  • Better-Auth v1.4 support drop. The existing v1.4-shape export is preserved (no caller uses it yet, but the public surface stability is worth preserving for the conformance harness path).
  • Schema generation CLI integration. Better-Auth's CLI (@better-auth/cli generate --adapter @suaveplan/better-auth-surrealdb/adapter/v1.6) will work via the createSchema translation, but bundling the CLI itself is a separate package.
  • Transaction nesting / savepoints. SurrealDB v3's transaction model is flat; nested transaction calls degrade to the outermost transaction (matches Better-Auth's documented expectation).
  • Custom ID field strategies via Better-Auth's customIdField. Better-Auth's customIdField option flows through to the underlying adapter's idStrategy: "plain" behaviour (id stored as a field, SurrealDB auto-generates the record's storage id). Operators using customIdField should pass idStrategy: "plain" explicitly.

Source mapping

Genesis-original — surfaced by the add-api-plugin-auth scaffold-time blocker.

Requirement Source
DBAdapterInstance factory shape Better-Auth v1.6 BetterAuthOptions.database type
createAdapterFactory wrapper @better-auth/core/db — official custom-adapter escape hatch
Transaction forwarding @suaveplan/surrealdb's runSurrealTransaction helper
createSchema returning DDL BETTER_AUTH_SURREAL_SCHEMA shipped at ./schema

Impact

Affected dependents

  • @suaveplan/api-plugin-auth (in-flight) — unblocks. Auth plugin's onInit will use createBetterAuth({ database: betterAuthSurrealAdapter({ client: db, ... }), ...options.betterAuth }).
  • Existing createBetterAuthSurrealAdapter callers (the conformance harness in tests/integration/) — unchanged.

Risk: low. Additive on the package; the new factory is a thin wrapper over the existing internal 10-method adapter, exercised by the same conformance harness against the same testcontainer.