npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

better-auth-effect

v0.3.3

Published

Better Auth adapter for @effect/sql

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-effect

Peer Dependencies

npm install better-auth effect @effect/sql

And one of the database drivers:

# PostgreSQL
npm install @effect/sql-pg

# MySQL
npm install @effect/sql-mysql2

# SQLite
npm install @effect/sql-sqlite-node

Usage

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 RETURNING
  • findOne - SELECT with WHERE, LIMIT 1
  • findMany - SELECT with WHERE, ORDER BY, LIMIT, OFFSET
  • update - UPDATE with RETURNING
  • updateMany - UPDATE multiple rows
  • delete - DELETE single row
  • deleteMany - DELETE multiple rows
  • count - 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