Skip to content

Quick Start — better-auth

  • 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’s TEXT id to UUID:

      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.

--better-auth-ids is required — use uuid (recommended) or mapped:

Terminal window
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:

Terminal window
psql "$DATABASE_URL" -f SMTA-better-auth-<timestamp>.sql

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):

FilePurpose
roles.sqlOptional role-membership wiring (smta.app_login_role / smta.admin_login_role GUCs)
auth_impl_uuid.sql / auth_impl_mapped.sqlImplements core.get_current_user_id() (uuid: direct cast; mapped: core.user_identities lookup)
new_user_trigger_uuid.sql / new_user_trigger_mapped.sqlAuto-creates core.users_meta (and the mapping row in mapped mode) on signup
secrets_pgcrypto_impl.sqlpgcrypto secrets keyed off the app.secrets_key GUC

Install the TypeScript package:

Terminal window
npm install @smta/better-auth pg

Register 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.

In psql or your SQL client:

-- Set the session variable to a known user ID
SET 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.

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 access
CREATE 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));