Better Auth
@apso/better-auth-adapter lets a Better Auth server store users, sessions, accounts, and verification records through an Apso-generated REST API. A common setup runs Better Auth in a Next.js backend-for-frontend and keeps the Apso API key on that server.
Architecture
Product client
session cookieSign-up, sign-in, sign-out, and session requests go to the application's auth route.
Next.js + Better Auth
@apso/better-auth-adapterThe adapter applies the private service key and translates Better Auth operations.
Apso service + PostgreSQL
User • Session • AccountThe generated REST resources persist identity records behind the server boundary.
APSO_API_KEY. Tenant authorization remains a separate application decision.The browser communicates with the Better Auth route. The adapter makes server-side calls to Apso. The Apso service key never reaches the browser.
Prepare the Apso backend
The adapter expects generated resources for these Better Auth models:
| Entity | Core fields |
|---|---|
User | id, email, emailVerified, name, image |
Session | id, token, userId, expiresAt, ipAddress, userAgent |
Account | id, userId, providerId, providerAccountId, provider token fields |
VerificationToken | id, identifier, value, expiresAt |
Use exact field names because the adapter maps Better Auth operations to the plural Apso routes for these entities. Add User relationships to Session and Account, then generate and deploy the backend.
Create a service API key in the dashboard after deployment. Store the deployed URL and key in the Next.js server environment.
Install the server packages
npm install better-auth @apso/better-auth-adapterConfigure Better Auth
import 'server-only';
import { apsoAdapter } from '@apso/better-auth-adapter';
import { betterAuth } from 'better-auth';
export const auth = betterAuth({
database: apsoAdapter({
baseUrl: process.env.APSO_API_URL!,
apiKey: process.env.APSO_API_KEY!,
}),
emailAndPassword: {
enabled: true,
},
trustedOrigins: [process.env.APP_URL!],
});APSO_API_URL=https://your-service.example.com
APSO_API_KEY=replace-with-a-server-side-key
APP_URL=http://localhost:3000
BETTER_AUTH_SECRET=replace-with-a-long-random-secretKeep APSO_API_KEY private. Do not prefix it with NEXT_PUBLIC_.
Mount the Auth handler
import { toNextJsHandler } from 'better-auth/next-js';
import { auth } from '@/lib/auth';
export const { GET, POST } = toNextJsHandler(auth);Create the browser client
import { createAuthClient } from 'better-auth/react';
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL,
});'use client';
import { authClient } from '@/lib/auth-client';
export function AccountMenu() {
const { data: session, isPending } = authClient.useSession();
if (isPending) return null;
if (!session) return <span>Signed out</span>;
return (
<button onClick={() => authClient.signOut()}>
Sign out {session.user.email}
</button>
);
}Verify the flow
- Start the Apso service and the Next.js application.
- Sign up with a new email address.
- Confirm that records appear in
Users,Accounts, andSessions. - Refresh a protected page and confirm that the session persists.
- Sign out and confirm that the session can no longer authorize the request.
- Test a second user and any tenant-isolation rule your application applies.
Better Auth handles identity and sessions. Your application still needs authorization rules for roles, workspaces, and record ownership.