better-auth-effect
v0.3.3
Published
Better Auth adapter for @effect/sql
Maintainers
Readme
better-auth-effect
A Better Auth database adapter for @effect/sql.
Use any Effect SQL provider (PostgreSQL, MySQL, SQLite) with Better Auth.
Installation
npm install better-auth-effect
# or
pnpm add better-auth-effectPeer Dependencies
npm install better-auth effect @effect/sqlAnd one of the database drivers:
# PostgreSQL
npm install @effect/sql-pg
# MySQL
npm install @effect/sql-mysql2
# SQLite
npm install @effect/sql-sqlite-nodeUsage
import { ManagedRuntime } from "effect"
import { PgClient } from "@effect/sql-pg"
import { effectSqlAdapter } from "better-auth-effect"
import { betterAuth } from "better-auth"
// 1. Create your database Layer
const SqlLive = PgClient.layer({
url: "postgresql://postgres:password@localhost:5432/myapp"
})
// Or with individual options:
// const SqlLive = PgClient.layer({
// host: "localhost",
// database: "myapp",
// username: "postgres",
// password: "password",
// })
// 2. Create a ManagedRuntime
const runtime = ManagedRuntime.make(SqlLive)
// 3. Use the adapter with Better Auth
export const auth = betterAuth({
database: effectSqlAdapter({
runtime,
dialect: "pg",
}),
// ... other Better Auth options
})Configuration
interface EffectSqlAdapterConfig {
/**
* ManagedRuntime that provides SqlClient.
* Create with ManagedRuntime.make(YourSqlLayer)
*/
runtime: ManagedRuntime.ManagedRuntime<SqlClient, never>
/**
* Database dialect for SQL differences (RETURNING clause, etc.)
* - "pg": PostgreSQL
* - "mysql": MySQL
* - "sqlite": SQLite
*/
dialect: "pg" | "mysql" | "sqlite"
/**
* Enable debug logging for adapter operations.
* @default false
*/
debugLogs?: boolean
}Why ManagedRuntime?
Using ManagedRuntime allows you to share the same connection pool between Better Auth and your Effect application code:
import { Effect, ManagedRuntime } from "effect"
import { SqlClient } from "@effect/sql"
import { PgClient } from "@effect/sql-pg"
// Single Layer for your entire app
const SqlLive = PgClient.layer({ database: "myapp" })
// Single Runtime - shared connection pool
const runtime = ManagedRuntime.make(SqlLive)
// Better Auth uses the same pool
const auth = betterAuth({
database: effectSqlAdapter({ runtime, dialect: "pg" }),
})
// Your app code uses the same pool
const getUsers = Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient
return yield* sql`SELECT * FROM users`
})
// Run with the same runtime
runtime.runPromise(getUsers)Database Support
| Database | Dialect | RETURNING Support |
|------------|------------|-------------------|
| PostgreSQL | "pg" | Native |
| SQLite | "sqlite" | Native (3.35+) |
| MySQL | "mysql" | Emulated* |
* MySQL doesn't support RETURNING. The adapter uses LAST_INSERT_ID() + SELECT as a fallback. Tables must have an id column as primary key. This is not a problem for Better Auth, which always uses id.
Column Naming (snake_case / camelCase)
Better Auth uses camelCase field names (emailVerified, accessToken). If your database uses snake_case columns (email_verified, access_token), configure transformation in PgClient.layer:
import { PgClient } from "@effect/sql-pg"
const snakeToCamel = (str: string) =>
str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
const camelToSnake = (str: string) =>
str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
const SqlLive = PgClient.layer({
database: "myapp",
// Transform column names automatically
transformResultNames: snakeToCamel, // DB → JS (snake_case → camelCase)
transformQueryNames: camelToSnake, // JS → DB (camelCase → snake_case)
})This is configured once at the client level and applies to all queries automatically.
Supported Operations
All Better Auth adapter methods are implemented:
create- Insert with RETURNINGfindOne- SELECT with WHERE, LIMIT 1findMany- SELECT with WHERE, ORDER BY, LIMIT, OFFSETupdate- UPDATE with RETURNINGupdateMany- UPDATE multiple rowsdelete- DELETE single rowdeleteMany- DELETE multiple rowscount- SELECT COUNT(*)
Error Handling
The adapter maps SQL errors to typed errors:
import {
AdapterError,
ConstraintViolationError,
ConnectionError
} from "better-auth-effect"- ConstraintViolationError - Unique/foreign key violations
- ConnectionError - Database connection issues
- AdapterError - Other SQL errors
License
MIT
