Quick Start — better-auth
1. Prerequisites
Section titled “1. Prerequisites”- A better-auth v1.x project connected to a PostgreSQL database
- A choice of id mode, passed to the CLI as
--better-auth-ids:-
uuid(recommended) — configure better-auth to emit UUID ids. SMTA’s primary keys are UUID and the uuid-mode trigger casts better-auth’sTEXTid toUUID:export const auth = betterAuth({generateId: () => crypto.randomUUID(),// ...}) -
mapped— keep better-auth’s default string ids; SMTA mints a UUID per user and maps it. No better-auth configuration required.
-
2. Generate and Apply the SQL
Section titled “2. Generate and Apply the SQL”--better-auth-ids is required — use uuid (recommended) or mapped:
npx @smta/cli --adapter better-auth --better-auth-ids uuid# → SMTA-better-auth-<timestamp>.sql (61 SQL files combined)Apply the generated script to your database:
psql "$DATABASE_URL" -f SMTA-better-auth-<timestamp>.sql3. What the Adapter Provides
Section titled “3. What the Adapter Provides”The better-auth deployment adds these adapter files on top of @smta/core (the auth-impl and trigger files depend on the id mode you chose):
| File | Purpose |
|---|---|
roles.sql | Optional role-membership wiring (smta.app_login_role / smta.admin_login_role GUCs) |
auth_impl_uuid.sql / auth_impl_mapped.sql | Implements core.get_current_user_id() (uuid: direct cast; mapped: core.user_identities lookup) |
new_user_trigger_uuid.sql / new_user_trigger_mapped.sql | Auto-creates core.users_meta (and the mapping row in mapped mode) on signup |
secrets_pgcrypto_impl.sql | pgcrypto secrets keyed off the app.secrets_key GUC |
4. Register the Plugin
Section titled “4. Register the Plugin”Install the TypeScript package:
npm install @smta/better-auth pgRegister smtaPlugin in your better-auth config:
import { betterAuth } from 'better-auth'import { smtaPlugin } from '@smta/better-auth'import { pool } from '@/lib/db' // your pg Pool instance
export const auth = betterAuth({ generateId: () => crypto.randomUUID(), plugins: [smtaPlugin({ pool })],})Use withSMTA() in Next.js Route Handlers or Server Actions to set the user context before querying SMTA functions:
import { auth } from '@/lib/auth'import { withSMTA } from '@smta/better-auth'import { pool } from '@/lib/db'
export async function GET(request: Request) { const session = await auth.api.getSession({ headers: request.headers })
return withSMTA(pool, session?.user?.id, async (client) => { const { rows } = await client.query( 'SELECT * FROM public.list_my_organizations()' ) return Response.json(rows) })}withSMTA() sets app.current_user_id using SET LOCAL, scoping it to the transaction. SMTA’s RLS policies read this value on every query.
5. Verify
Section titled “5. Verify”In psql or your SQL client:
-- Set the session variable to a known user IDSET app.current_user_id = 'your-user-uuid-here';
-- Should return an empty array (no orgs yet for this user)SELECT public.list_my_organizations();If the function returns without error, SMTA is deployed and the auth wiring is in place.
6. Add Your App Schema
Section titled “6. Add Your App Schema”Create your app schema tables referencing core.organizations or core.units, enable RLS, and write policies that call core.get_current_user_id().
CREATE SCHEMA IF NOT EXISTS app;
-- Org-scoped table: any member of the org can accessCREATE TABLE app.projects ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), org_id UUID NOT NULL REFERENCES core.organizations(id), name TEXT NOT NULL, created_at TIMESTAMPTZ DEFAULT now());
ALTER TABLE app.projects ENABLE ROW LEVEL SECURITY;
CREATE POLICY "org members" ON app.projects FOR ALL USING (core.is_org_member(org_id));