Database Adapters
Learn about database adapters and the Better Stack data layer
Better Stack uses a flexible adapter system that allows you to connect to any database through your preferred ORM. The adapter acts as a bridge between Better Stack's unified data layer and your database of choice.
Package Overview
Better Stack consists of separate npm packages under the @btst namespace:
@btst/stack- Core package (install this first)@btst/adapter-*- Database adapters (install one based on your ORM)@btst/cli- CLI tools for schema generation (dev dependency)@btst/db- Internal database abstraction layer (installed as a dependency of other packages)
See the Installation guide for setup instructions.
Available Adapters
Better Stack provides official adapters for popular ORMs:
- Prisma -
@btst/adapter-prisma- Supports PostgreSQL, MySQL, SQLite, CockroachDB, and MongoDB - Drizzle -
@btst/adapter-drizzle- Supports PostgreSQL, MySQL, SQLite, and more - Kysely -
@btst/adapter-kysely- Supports PostgreSQL, MySQL, SQLite, and more - MongoDB -
@btst/adapter-mongodb- Native MongoDB driver support - Memory -
@btst/adapter-memory- In-memory adapter for development and testing
Installation
Adapters are separate packages that must be installed alongside @btst/stack:
# Install core package
npm install @btst/stack
# Install your chosen adapter
npm install @btst/adapter-prismaSee the Installation guide for detailed adapter setup instructions.
Usage
When you configure Better Stack, the betterStack() function collects all plugin database schemas and merges them into a unified schema. The adapter function receives this merged schema and returns an adapter that translates Better Stack's database operations to your ORM.
import { betterStack } from "@btst/stack"
import { createPrismaAdapter } from "@btst/adapter-prisma"
import { PrismaClient } from "@prisma/client"
const prisma = new PrismaClient()
const { handler, dbSchema } = betterStack({
basePath: "/api/data",
plugins: {
// Your plugins here
},
// The adapter receives the merged db schema from all plugins
adapter: (db) => createPrismaAdapter(prisma, db, {
provider: "postgresql" // or "mysql", "sqlite", "cockroachdb", "mongodb"
})
})
export { handler, dbSchema }import { betterStack } from "@btst/stack"
import { createDrizzleAdapter } from "@btst/adapter-drizzle"
import { drizzle } from "drizzle-orm/postgres-js" // or "drizzle-orm/mysql2", "drizzle-orm/better-sqlite3", etc.
import postgres from "postgres"
const client = postgres(process.env.DATABASE_URL!)
const drizzleDb = drizzle(client)
const { handler, dbSchema } = betterStack({
basePath: "/api/data",
plugins: {
// Your plugins here
},
adapter: (db) => createDrizzleAdapter(drizzleDb, db, {})
})
export { handler, dbSchema }import { betterStack } from "@btst/stack"
import { createKyselyAdapter } from "@btst/adapter-kysely"
import { Kysely, PostgresDialect } from "kysely"
import { Pool } from "pg"
const kyselyDb = new Kysely({
dialect: new PostgresDialect({
pool: new Pool({ connectionString: process.env.DATABASE_URL })
})
})
const { handler, dbSchema } = betterStack({
basePath: "/api/data",
plugins: {
// Your plugins here
},
adapter: (db) => createKyselyAdapter(kyselyDb, db, {})
})
export { handler, dbSchema }import { betterStack } from "@btst/stack"
import { createMongodbAdapter } from "@btst/adapter-mongodb"
import { MongoClient } from "mongodb"
const client = new MongoClient(process.env.MONGODB_URI!)
const mongoDb = client.db()
const { handler, dbSchema } = betterStack({
basePath: "/api/data",
plugins: {
// Your plugins here
},
adapter: (db) => createMongodbAdapter(mongoDb, db, {})
})
export { handler, dbSchema }// IMPORTANT: Memory adapter is used for development and testing only
import { betterStack } from "@btst/stack"
import { createMemoryAdapter } from "@btst/adapter-memory"
const { handler, dbSchema } = betterStack({
basePath: "/api/data",
plugins: {
// Your plugins here
},
adapter: (db) => createMemoryAdapter(db, {})
})
export { handler, dbSchema }The adapter function receives the merged database schema (db) containing all tables and relationships from your plugins, and returns an adapter instance that implements the common adapter interface.
To learn more about generating database schemas and running migrations, see the CLI documentation.
How the Adapter Function Works
The adapter pattern follows this flow:
betterStack()merges all plugin schemas into a unifieddbobject- Your
adapterfunction receives thisdbobject - The adapter creator function (e.g.,
createPrismaAdapter,createDrizzleAdapter) returns an adapter instance - This adapter instance implements the common interface (create, update, findOne, etc.)
The adapter function signature allows the adapter to be configured with both Better Stack's schema and any ORM-specific options.
Database Schema Generation
After configuring your adapter, you'll need to generate database schemas and migrations. Better Stack provides a CLI tool to help with this process.
See the CLI documentation for detailed information on generating schemas for Prisma, Drizzle, and Kysely, as well as running migrations.
How Data Layer Works
Better Stack's data layer is built on Better DB, a fork of better-auth's database layer. Better DB is a type-safe database abstraction layer that provides:
- Unified Schema Definition: Plugins define their database schemas using Better DB's schema definition API
- Schema Composition: All plugin schemas are automatically merged into a single unified schema
- Type-Safe Operations: Full TypeScript support for all database operations
- Adapter Abstraction: Works with any database through adapters
How Adapters Work
Adapters implement a common interface that provides methods for:
create- Insert new recordsupdate- Update existing recordsupdateMany- Bulk update operationsdelete- Delete recordsdeleteMany- Bulk delete operationsfindOne- Find a single recordfindMany- Query multiple recordscount- Count records matching criteria
Each adapter translates these operations to the appropriate ORM calls (Prisma, Drizzle, Kysely, MongoDB, etc.).
Using Other Better Auth Compatible Adapters
Better DB is a fork of better-auth's database layer, which means existing better-auth adapters can work with Better Stack with a small wrapper modification. The wrapper is necessary because Better Stack's plugin system merges schemas differently than better-auth plugins.
This allows you to leverage the wide ecosystem of better-auth adapters. The wrapper function needs to inject the Better DB schema into the better-auth adapter's plugin system so it can find your models. Here's an example of how @btst/adapter-prisma wraps better-auth's Prisma adapter:
export * from "better-auth/adapters/prisma";
import type { Adapter, DatabaseDefinition } from "@btst/db";
import { prismaAdapter, type PrismaConfig } from "better-auth/adapters/prisma";
import type { BetterAuthOptions } from "better-auth/types";
/**
* Helper function to create a Prisma adapter with Better DB schema
*
* This handles passing the Better DB schema to the prismaAdapter
* by injecting it as a plugin so Better Auth can find your models.
*/
export function createPrismaAdapter(
prisma: any,
db: DatabaseDefinition,
config: PrismaConfig,
options: BetterAuthOptions = {},
): (options: BetterAuthOptions) => Adapter {
return (adapterOptions: BetterAuthOptions = {}) => {
const mergedOptions = {
...options,
...adapterOptions,
plugins: [
...(options.plugins || []),
...(adapterOptions.plugins || []),
{
id: "better-db-schema",
schema: db.getSchema(),
},
],
};
return prismaAdapter(prisma, config)(mergedOptions);
};
}You can apply the same pattern to wrap other better-auth compatible adapters that are currently not exported by better-db.
Better DB Package
Better DB is a fork of better-auth's database layer. For more information about the underlying data layer, see the Better DB package on GitHub. This package provides the core database abstraction that powers Better Stack's adapter system.