Skip to content

feat(better-auth): add Effect-native SDK for the better-auth HTTP API - #372

Draft
Butch78 wants to merge 1 commit into
alchemy-run:mainfrom
Butch78:feat/better-auth-sdk
Draft

feat(better-auth): add Effect-native SDK for the better-auth HTTP API#372
Butch78 wants to merge 1 commit into
alchemy-run:mainfrom
Butch78:feat/better-auth-sdk

Conversation

@Butch78

@Butch78 Butch78 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Adds @distilled.cloud/better-auth, an Effect-native client for a self-hosted better-auth server's core HTTP API — email/password auth, sessions, social account linking, and account management — with exhaustive error typing and retry policies, built on the shared sdk-core client/traits/errors conventions.

Unlike the hosted-cloud SDKs, this is a client for a better-auth server the user runs. It talks to the handler's HTTP API (default mount path /api/auth) and does not embed better-auth itself.

Usage

import * as Effect from "effect/Effect";
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
import * as BetterAuth from "@distilled.cloud/better-auth";

const baseUrl = "https://app.example.com/api/auth";

const program = Effect.gen(function* () {
  const { token } = yield* BetterAuth.signInEmail({ email, password });
  return yield* BetterAuth.getSession({}).pipe(
    Effect.provide(BetterAuth.layer({ baseUrl, token })),
  );
}).pipe(
  Effect.provide(BetterAuth.layer({ baseUrl })),
  Effect.provide(FetchHttpClient.layer),
);

Errors are typed and matchable, e.g. Effect.catch("Unauthorized", ...).

What's included

  • 22 core operations: signUpEmail, signInEmail, signInSocial, signOut, getSession, listSessions, revokeSession, revokeSessions, revokeOtherSessions, requestPasswordReset, resetPassword, verifyEmail, sendVerificationEmail, changePassword, verifyPassword, changeEmail, updateUser, deleteUser, listAccounts, linkSocial, unlinkAccount, ok.
  • Credentials: base URL + optional session token, sent as Authorization: Bearer <token> (resolved server-side by the bearer plugin). Built via layer({ baseUrl, token }) or CredentialsFromEnv (BETTER_AUTH_URL / BETTER_AUTH_TOKEN).
  • Typed errors mapped from HTTP status via the shared sdk-core classes, plus UnknownBetterAuthError / BetterAuthParseError.
  • Registered in root tsconfig.json, README.md, AGENTS.md, and CI (test.yml).

Design notes

  • Only the core endpoints (present with email/password + base session/social handling) are covered. Plugin routes (admin, organization, apiKey, twoFactor, magicLink, passkey, ...) are intentionally out of scope.
  • This SDK is hand-written rather than generated from a spec: better-auth exposes no single canonical OpenAPI document for a base install (the surface depends on enabled plugins), so there is no specs/ submodule. The operation contracts were verified against the better-auth route source.

Testing

Tests are fully hermetic — they boot a real in-process better-auth server (memory adapter + bearer plugin) on an ephemeral port and drive the SDK against it, so no external credentials or network access are required. The suite exercises the full happy path (sign-up → get-session → update-user → list-sessions → change-password → sign-in → sign-out) plus the unauthenticated-session and wrong-password error paths.

bun run build   # (core)
bun --filter '@distilled.cloud/better-auth' check
bun --filter '@distilled.cloud/better-auth' test   # 9 passed

Adds @distilled.cloud/better-auth — a client for a self-hosted better-auth
server's core HTTP API (email/password auth, sessions, social account
linking, account management) with exhaustive error typing and retry
policies, following the shared sdk-core client/traits/errors conventions.

- 22 core operations (sign-up/in/out, get/list/revoke sessions, password
  reset + change, email verification/change, update/delete user, list
  accounts, link/unlink social, ok)
- Session-token bearer credentials via `layer()` / `CredentialsFromEnv`
- Hermetic tests: boot a real in-process better-auth server (memory
  adapter + bearer plugin), no external credentials
- Registered in root tsconfig, README, AGENTS.md, and CI (test.yml)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSaEXHU2Qe2PUq55gYQ2Sc
@Butch78
Butch78 marked this pull request as draft July 13, 2026 11:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant