Clients and sign-in flows
Use Better Auth's browser client for authentication, preserve session cookies, and distinguish auth endpoints from RELKIT's generated client.
Use Better Auth’s browser client to sign users up, sign them in, and manage their sessions. Use the RELKIT generated client for your application’s function-backed routes. They serve different APIs; mounting auth does not generate typed RELKIT methods for its raw endpoints.
Create a browser auth client
Before installing, merge these selected declaration repairs into the existing package manifest, preserving unrelated overrides:
{
"overrides": {
"@better-fetch/fetch": "1.3.2"
},
"pnpm": {
"overrides": {
"@better-fetch/fetch": "1.3.2"
}
}
}
From this guide’s application/package root, install only its selected requirements:
bun add better-auth@1.7.1
bun add --dev @cloudflare/workers-types@4.20250121.0
Keep these owner-specific versions and the lockfile together. Runtime SDKs belong in dependencies; build/type tooling belongs in devDependencies. Installing an adapter does not supply its optional SDK peers or enable the feature’s configuration.
For RELKIT’s React convenience leaf, use the separate React authentication setup.
That setup includes a copyable AuthProvider connecting the native session hook to RELKIT’s
private query cache. Start with the Frontend guide for the shared
contract and provider workflow; auth endpoints themselves use the native client below.
The framework-independent client and a sign-in action are:
import { createAuthClient } from "better-auth/client";
// Browser code: use the public backend origin, never the server secret or service.
export const authClient = createAuthClient({
baseURL: "http://127.0.0.1:3000",
basePath: "/api/auth",
fetchOptions: { credentials: "include" },
});
export async function signIn(email: string, password: string) {
const { data, error } = await authClient.signIn.email({ email, password });
if (error !== null) throw new Error(error.message ?? "Sign-in failed");
return data;
}
Use your actual public backend origin instead of the loopback URL. basePath on this client must
match the filesystem auth mount; it is not the reserved server-service option. Do not import
src/services/auth/service.ts, database code, or server secrets into the browser. A public backend URL is
configuration, not a credential.
The action checks Better Auth’s returned error before using data. Your UI should also handle
rejected requests, display an appropriate failure, and clear loading state after both success and
failure. Do not log passwords, cookies, or the complete auth response.
Build the account flow
With email/password enabled, the native client maps these operations to the mounted API:
| User action | Client method | Native endpoint under /api/auth |
Main input |
|---|---|---|---|
| Create an account | authClient.signUp.email |
POST /sign-up/email |
name, email, password |
| Sign in | authClient.signIn.email |
POST /sign-in/email |
email, password |
| Read the native session | authClient.getSession |
GET /get-session |
No credentials in the body |
| Sign out | authClient.signOut |
POST /sign-out |
No account password |
The example enables automatic sign-in after a successful sign-up and does not require email verification. If you change those policies, show the verification/sign-in step instead of assuming every successful sign-up already has an active session. See Customization.
Let the browser store and send the session cookie. Do not copy the session token into local storage
or manually construct a Cookie header in browser JavaScript. Use Better Auth’s sign-out endpoint to
invalidate the session; clearing a UI flag alone does not sign a user out on the server.
After sign-in, call your public /session operation or protected /account/profile route to verify
the application sees the same identity. GET /session is the example’s small application response;
GET /api/auth/get-session is Better Auth’s native session response. They are not interchangeable shapes.
Keep cookies and origins aligned
The simplest local flow serves the page and API from http://127.0.0.1:3000. Use that hostname
consistently: switching between localhost and 127.0.0.1 changes the host to which cookies belong.
For a separate frontend origin, configure all three boundaries:
- The client sends requests to the public backend URL with credentials included.
- Better Auth’s
trustedOriginsallows the exact frontend origin for the auth flow. - The HTTP server or gateway supplies credentialed CORS for that origin, including preflight handling.
trustedOrigins is not CORS configuration, and CORS does not override cookie domain, SameSite, or
secure-cookie rules. Do not use a wildcard allowed origin with credentialed requests or disable
CSRF/origin checks to work around a configuration error. Test real browser requests over your intended
HTTPS domains; the in-process harness does not enforce browser cookie policy.
When using server-side rendering, forward only the current incoming request’s relevant credentials to the correct backend. A server-side fetch does not inherit the user’s browser cookie jar. Likewise, any auth response cookies must reach the browser through your framework’s supported response handling. Never share a mutable cookie/header object across users in a long-lived server client.
Do not confuse bearer headers with authentication
After installing the RELKIT client, its transport can mutate request headers:
import { createClient } from "@relkit/client";
export const bearerHeaders = new Headers();
export const client = createClient({ baseUrl: "http://127.0.0.1:3000", headers: bearerHeaders });
export function setBearerToken(token?: string): void {
if (token === undefined) bearerHeaders.delete("authorization");
else bearerHeaders.set("authorization", `Bearer ${token}`);
}
This example does not enable Better Auth bearer-token support. Adding an Authorization header
does not teach the server to validate it, and a Better Auth session is not automatically a JWT.
If you need non-cookie clients, configure and test the corresponding native auth capability on the
server and its client integration. Keep cookie authentication as the verified flow in this example.
For a framework-free browser demonstration, run examples/auth-drizzle using its README and open the
served page. The page uses the same native HTTP endpoints. For a reproducible command-line flow,
continue with Your first auth flow.