Files
skills/docs/ui/react/react-auth/src/lib/better-auth-adapter.md
2026-05-29 08:20:56 +01:00

3.4 KiB

better-auth-adapter

(no submodules)

Purpose

better-auth-adapter.ts provides the response-narrowing bridge between better-auth/client (which returns a BetterAuthError | BetterAuthData<T> union with native Date timestamps) and the package's internal User / Session / AuthResponse shapes (which carry ISO-string timestamps for serialisation safety). The module exposes two structural type guards (isBetterAuthError, hasBetterAuthData) that narrow unknown to the appropriate branch without any, plus four transform* functions that map a successful payload onto an internal shape and surface the error branch's message when the call failed. No any types appear anywhere — every guard accepts unknown and narrows through explicit field checks.

Features

  • Two type guards: isBetterAuthError(unknown), hasBetterAuthData<T>(unknown).
  • Four transforms: transformSessionResponse, transformAuthResponse, transformGenericResponse, transformUpdateProfileResponse.
  • ISO-string normalisation for every Date field on the way out.
  • Robust to null / undefined / malformed payloads — returns a default with a message string when the shape doesn't match.

Usage: Basic

import { transformSessionResponse } from "@suaveplan/react-auth";

const raw = await client.getSession();
const { user, session, error } = transformSessionResponse(raw);

Usage: Advanced

Chain the guards directly when you need to act differently on each branch:

import {
    hasBetterAuthData,
    isBetterAuthError,
    transformAuthResponse,
} from "@suaveplan/react-auth";

const raw = await client.signIn({ email, password });
if (isBetterAuthError(raw)) {
    logger.warn({ code: raw.error, message: raw.message }, "sign-in rejected");
    return;
}
if (hasBetterAuthData<{ user: unknown }>(raw)) {
    const out = transformAuthResponse(raw);
    if (out.user) await persistSession(out.user);
}

API Reference

  • function isBetterAuthError(response: unknown): response is { error: string; message: string }
  • function hasBetterAuthData<T = unknown>(response: unknown): response is { data: T }
  • function transformSessionResponse(response: unknown): { session: Session | null; user: User | null; error?: string }
  • function transformAuthResponse(response: unknown): AuthResponse
  • function transformGenericResponse(response: unknown): AuthResponse
  • function transformUpdateProfileResponse(response: unknown): AuthResponse

Implementation Notes

The two top-level guards use typeof value === "object" && value !== null plus structural field checks. They do not use instanceof because the underlying client may return plain objects parsed from JSON. The user-shape guard checks id, email, emailVerified, name, createdAt, and updatedAt are present and of the expected primitive class (Date | string); the session-shape guard checks id, userId, and expiresAt. When the payload includes a Date instance for any timestamp, the transform calls .toISOString(); when it's already a string, it is passed through. The transformUser helper hard-codes roles: 0 and status: 0 because those fields belong to the internal User type but are not modelled by better-auth's session payload — call sites that need richer role data must read them from the Session row directly (Wave 2 wires this through the form components).