D1 Adapter
The D1 adapter is the primary storage backend for Workers Auth. It persists users, sessions, and role assignments to a Cloudflare D1 SQLite database. Tables are created automatically on first use.
1. Create a D1 database
Section titled “1. Create a D1 database”wrangler d1 create auth2. Add to wrangler.toml
Section titled “2. Add to wrangler.toml”[[d1_databases]]binding = "DB"database_name = "auth"database_id = "your-database-id"3. Use in your app
Section titled “3. Use in your app”import { D1Adapter } from 'workers-auth/adapters/d1';
const auth = WorkersAuth({ database: (binding) => D1Adapter(binding), // ...});The database config takes a factory function. At request time, Workers Auth passes the DB binding from your Worker’s env object.
Auto-migration
Section titled “Auto-migration”On the first call to initialize(), the adapter creates the following tables if they don’t exist:
CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, email TEXT UNIQUE NOT NULL, status TEXT NOT NULL DEFAULT 'active', created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE TABLE IF NOT EXISTS sessions ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, expires_at INTEGER NOT NULL, created_at INTEGER NOT NULL);
CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);
CREATE TABLE IF NOT EXISTS user_roles ( user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, role TEXT NOT NULL, created_at TEXT DEFAULT (datetime('now')), PRIMARY KEY (user_id, role));No manual migration step is needed. Deploy and the tables are created on the first request.
DatabaseAdapter interface
Section titled “DatabaseAdapter interface”The D1 adapter implements the DatabaseAdapter interface:
interface DatabaseAdapter { initialize(): Promise<void>; findUserByEmail(email: string): Promise<User | null>; findUserById(id: string): Promise<User | null>; createUser(email: string): Promise<User>; createSession(userId: string, expiresAt: number): Promise<Session>; getSession(sessionId: string): Promise<Session | null>; deleteSession(sessionId: string): Promise<void>; deleteUserSessions(userId: string): Promise<string[]>; listUsers(limit: number, offset: number): Promise<{ users: User[]; total: number }>; updateUserStatus(userId: string, status: 'active' | 'banned'): Promise<void>; getUserRoles(userId: string): Promise<string[]>; setUserRoles(userId: string, roles: string[]): Promise<void>; addUserRole(userId: string, role: string): Promise<void>; removeUserRole(userId: string, role: string): Promise<void>;}Writing a custom adapter
Section titled “Writing a custom adapter”To use a different database, implement DatabaseAdapter:
import type { DatabaseAdapter } from 'workers-auth';
function MyCustomAdapter(binding: any): DatabaseAdapter { return { async initialize() { // Create tables or run migrations }, async findUserByEmail(email) { // Return User or null }, async findUserById(id) { // Return User or null }, async createUser(email) { // Insert and return User (with status: 'active') }, async createSession(userId, expiresAt) { // Insert and return Session }, async getSession(sessionId) { // Return Session (if not expired) or null }, async deleteSession(sessionId) { // Delete single session }, async deleteUserSessions(userId) { // Delete all sessions for user, return deleted IDs }, async listUsers(limit, offset) { // Return { users: User[], total: number } }, async updateUserStatus(userId, status) { // Update user status to 'active' or 'banned' }, async getUserRoles(userId) { // Return array of role names }, async setUserRoles(userId, roles) { // Replace all roles for user }, async addUserRole(userId, role) { // Add a single role to user }, async removeUserRole(userId, role) { // Remove a single role from user }, };}Then pass it to WorkersAuth:
const auth = WorkersAuth({ database: (binding) => MyCustomAdapter(binding), // ...});