Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,12 @@ for QueryClient wiring, database adapters, all three frameworks, and auth.

## Database schemas & migrations

Optional CLI to generate schemas and run migrations from enabled plugins:
Generate schemas and run migrations through the v3 codegen CLI. It runs the
aligned Better DB CLI in isolation, so its dependencies and `btst` binary do
not enter your application graph:

```bash
npm install -D @btst/cli
```

Generate drizzle schema:

```bash
npx @btst/cli generate --orm drizzle --config lib/stack.ts --output db/schema.ts
npx @btst/codegen@next generate --orm drizzle --config lib/stack.ts --output db/schema.ts
```

Supports Prisma, Drizzle, MongoDB and Kysely SQL dialects.
Expand Down
55 changes: 21 additions & 34 deletions docs/content/docs/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ BTST has two CLI packages:
- `@btst/codegen` owns `init` scaffolding (`npx @btst/codegen init`)
- `@btst/cli` owns low-level DB schema generation and migrations

`@btst/codegen generate` and `@btst/codegen migrate` are passthrough entrypoints to the existing `@btst/cli` flow.
`@btst/codegen generate` and `@btst/codegen migrate` run the aligned
`@btst/cli@2.2.3` release in isolation. This avoids adding its dependency graph
or competing `btst` binary to the application.

## Init (Codegen)

Expand Down Expand Up @@ -60,35 +62,20 @@ npx @btst/codegen generate --orm=prisma --config=lib/stack.ts --output=schema.pr
npx @btst/codegen migrate --config=lib/stack.ts --database-url=postgres://...
```

When a delegated command fails, fix the underlying issue and run the equivalent `npx @btst/cli ...` command directly.
When a delegated command fails, fix the underlying issue and run the equivalent
`npx @btst/cli@2.2.3 ...` command directly.

## About Better DB

BTST uses [Better DB (`@btst/db`)](https://github.com/better-stack-ai/better-auth/tree/main/packages/btst) as its database abstraction layer—a specialized fork of [better-auth](https://www.better-auth.com)'s database layer optimized for BTST's plugin architecture.

The CLI works with the `dbSchema` exported from your BTST configuration, which is built using Better DB's schema definition API. All plugin schemas are automatically merged into a unified schema that the CLI can process.

Install the CLI as a dev dependency:

<Tabs groupId="installation" items={['npm', 'pnpm', 'yarn']} persist>
<Tab value="npm">
```bash
npm install -D @btst/cli
```
</Tab>

<Tab value="pnpm">
```bash
pnpm add -D @btst/cli
```
</Tab>

<Tab value="yarn">
```bash
yarn add -D @btst/cli
```
</Tab>
</Tabs>
For v3 applications, prefer the codegen passthrough commands above. If a v2
application lists `@btst/cli` in its dependencies, remove it during migration;
the pinned one-off CLI keeps Better DB dependencies from polluting the consumer
graph. You can still invoke the low-level CLI directly with
`npx @btst/cli@2.2.3`.

## Parameters

Expand All @@ -106,13 +93,13 @@ Generate database schemas for your ORM from your BTST `dbSchema`:
<Tabs items={["prisma", "drizzle", "kysely"]}>
<Tab value="prisma">
```bash
npx @btst/cli generate --config=lib/stack.ts --orm=prisma --output=schema.prisma
npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=prisma --output=schema.prisma
```
</Tab>

<Tab value="drizzle">
```bash
npx @btst/cli generate --config=lib/stack.ts --orm=drizzle --output=src/db/schema.ts
npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=drizzle --output=src/db/schema.ts
```
</Tab>

Expand All @@ -122,17 +109,17 @@ Generate database schemas for your ORM from your BTST `dbSchema`:
**Using DATABASE_URL environment variable:**

```bash
DATABASE_URL=sqlite:./dev.db npx @btst/cli generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql
DATABASE_URL=sqlite:./dev.db npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql
```

**Or using --database-url flag:**

```bash
npx @btst/cli generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql --database-url=sqlite:./dev.db
npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql --database-url=sqlite:./dev.db
```

```bash
npx @btst/cli generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql --database-url=postgres://user:pass@localhost:5432/db
npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=kysely --output=migrations/schema.sql --database-url=postgres://user:pass@localhost:5432/db
```
</Tab>
</Tabs>
Expand All @@ -145,25 +132,25 @@ Migrate your database schema directly (Kysely only). For Prisma and Drizzle, use
**Using DATABASE_URL environment variable:**

```bash
DATABASE_URL=sqlite:./dev.db npx @btst/cli migrate --config=lib/stack.ts
DATABASE_URL=sqlite:./dev.db npx @btst/cli@2.2.3 migrate --config=lib/stack.ts
```

**Or using --database-url flag:**

```bash
npx @btst/cli migrate --config=lib/stack.ts --database-url=sqlite:./dev.db
npx @btst/cli@2.2.3 migrate --config=lib/stack.ts --database-url=sqlite:./dev.db
```

```bash
npx @btst/cli migrate --config=lib/stack.ts --database-url=postgres://user:pass@localhost:5432/db
npx @btst/cli@2.2.3 migrate --config=lib/stack.ts --database-url=postgres://user:pass@localhost:5432/db
```

### Generate SQL to File

Instead of running migrations directly, generate SQL to a file:

```bash
npx @btst/cli migrate --config=lib/stack.ts --output=migrations.sql --database-url=sqlite:./dev.db
npx @btst/cli@2.2.3 migrate --config=lib/stack.ts --output=migrations.sql --database-url=sqlite:./dev.db
```

## Gotchas
Expand All @@ -175,11 +162,11 @@ Because the CLI executes your config file to extract the `dbSchema`, there are a
- **Environment variables**: If your config file or its imports have conditional checks for available environment variables (e.g., checking if `process.env.SOME_VAR` exists), you should also pass those environment variables when running CLI commands:

```bash
SOME_VAR=value npx @btst/cli generate --config=lib/stack.ts --orm=prisma --output=schema.prisma
SOME_VAR=value npx @btst/cli@2.2.3 generate --config=lib/stack.ts --orm=prisma --output=schema.prisma
```

or using dotenv-cli:

```bash
npx dotenv-cli -e .env.local -- npx @btst/cli generate --orm drizzle --config lib/stack.ts --output db/btst-schema.ts
npx dotenv-cli -e .env.local -- npx @btst/cli@2.2.3 generate --orm drizzle --config lib/stack.ts --output db/btst-schema.ts
```
2 changes: 1 addition & 1 deletion docs/content/docs/databases/adapters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ BTST 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/cli`** - schema tooling invoked in isolation through `@btst/codegen`
- **`@btst/db`** - Internal database abstraction layer (installed as a dependency of other packages)

See the [Installation guide](/installation) for setup instructions.
Expand Down
82 changes: 58 additions & 24 deletions docs/content/docs/plugins/better-auth-ui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,64 @@ Before starting, ensure you have:
- A database adapter (e.g., Drizzle with `@btst/adapter-drizzle`)
</Callout>

### 1. Install the Package
### 1. Install the v3 RC Dependency Cohort

```bash
pnpm add @btst/better-auth-ui
pnpm add --save-exact \
@btst/stack@next \
@btst/yar@1.3.2 \
@btst/adapter-drizzle@2.2.3 \
@btst/better-auth-ui@2.0.0-rc.1 \
@tanstack/react-query@5.100.14 \
better-auth@1.6.16 \
@better-auth/core@1.6.16 \
@better-auth/api-key@1.6.16 \
@better-auth/drizzle-adapter@1.6.16 \
@better-auth/passkey@1.6.16 \
@better-auth/utils@0.4.1 \
@better-fetch/fetch@1.2.2 \
better-call@1.3.6 \
drizzle-orm@0.45.2
```

Or with npm/yarn:
This exact cohort keeps BTST, Better Auth, and Drizzle on one copy of their
shared types. If you use another database, replace the two Drizzle packages
with the matching `@btst/adapter-*` `2.2.3` and `@better-auth/*-adapter`
`1.6.16` packages. The BTST initializer installs this aligned set when Better
Auth UI is selected.

If you are upgrading from v2, remove a locally installed `@btst/cli`. The v3
codegen commands run the aligned `@btst/cli@2.2.3` release in isolation, which
keeps its database tooling and `btst` binary out of the application dependency
graph:

```bash
npm install @btst/better-auth-ui
# or
yarn add @btst/better-auth-ui
pnpm remove @btst/cli
```

With npm, use the same exact versions:

```bash
npm install --save-exact \
@btst/stack@next \
@btst/yar@1.3.2 \
@btst/adapter-drizzle@2.2.3 \
@btst/better-auth-ui@2.0.0-rc.1 \
@tanstack/react-query@5.100.14 \
better-auth@1.6.16 \
@better-auth/core@1.6.16 \
@better-auth/api-key@1.6.16 \
@better-auth/drizzle-adapter@1.6.16 \
@better-auth/passkey@1.6.16 \
@better-auth/utils@0.4.1 \
@better-fetch/fetch@1.2.2 \
better-call@1.3.6 \
drizzle-orm@0.45.2
```

For an npm-based v2 application, use `npm uninstall @btst/cli` during the
migration.

### 2. Configure the Stack Client

Import and register the auth plugins in your `stack-client.tsx` file:
Expand Down Expand Up @@ -126,8 +170,9 @@ Configure the plugin overrides in your catch-all layout file. The `auth` overrid
```tsx title="app/p/layout.tsx"
"use client"

import { StackProvider, type StackAuthProvider } from "@btst/stack/context"
import { StackProvider } from "@btst/stack/context"
import { nextRouter } from "@btst/stack/next"
import { createBetterAuthProvider } from "@btst/better-auth-ui"
import type {
AuthPluginOverrides,
AccountPluginOverrides,
Expand All @@ -142,15 +187,9 @@ Configure the plugin overrides in your catch-all layout file. The `auth` overrid
organization: OrganizationPluginOverrides
}

const stackAuth = {
getIdentity: async () => {
const { data } = await authClient.getSession()
return data?.user ?? null
},
const stackAuth = createBetterAuthProvider(authClient, {
loginPath: "/p/auth/sign-in",
can: ({ resource, action, identity }) =>
Boolean(identity && authorize(identity, resource, action)),
} satisfies StackAuthProvider
})

export default function PagesLayout({ children }: { children: ReactNode }) {
// Better Auth UI-specific configuration shared by its three plugins
Expand Down Expand Up @@ -277,10 +316,10 @@ The exact sub-paths come from the view paths constants in the library and match

### Auth Plugin (`AuthPluginOverrides`)

`Link`, `navigate`, and `replace` in this table are optional APIs of the
external `@btst/better-auth-ui` package. They are not the removed built-in BTST
override fields; the recommended setup above uses `StackProvider.router` and
the package defaults instead.
The standalone upstream UI provider still supports its own navigation
overrides. BTST plugin overrides intentionally omit those fields: the bridge
reads navigation, notifications, localization, and session refresh from the
top-level `StackProvider` instead.

| Option | Type | Default | Description |
|--------|------|---------|-------------|
Expand Down Expand Up @@ -313,11 +352,6 @@ the package defaults instead.
| `optimistic` | `boolean` | `false` | Optimistic user updates |
| `hooks` | `Partial<AuthHooks>` | — | Custom data fetching hooks |
| `mutators` | `Partial<AuthMutators>` | — | Custom mutation handlers |
| `Link` | `Link` | `<a>` | Custom link component |
| `navigate` | `(href: string) => void` | `location.href` | Navigation function |
| `replace` | `(href: string) => void` | `navigate` | Replace navigation |
| `toast` | `RenderToast` | Sonner | Custom toast renderer |
| `onSessionChange` | `() => void` | — | Session change callback |
| `onRouteError` | `(name, error, ctx) => void` | — | Route error callback |
| `pageProps` | See [Per-Page Props](#per-page-props) | — | Per-page className/classNames/localization |

Expand Down
25 changes: 10 additions & 15 deletions packages/cli/scripts/test-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -125,26 +125,21 @@ STACK_PEERS=$(node -e 'const fs=require("fs");const p=JSON.parse(fs.readFileSync
# installInitDependencies() in package-installer.ts does at runtime.
PLUGIN_EXTRA_PACKAGES=$(node -e '
const { PLUGINS } = require("./node_modules/@btst/codegen/dist/lib.cjs");
const extras = PLUGINS.flatMap(p => p.extraPackages || []);
const extras = PLUGINS.flatMap(p => p.extraInstallSpecs || p.extraPackages || []);
process.stdout.write([...new Set(extras)].join(" "));
')
# Install adapter, plugin extras (includes @btst/better-auth-ui, better-auth, and any
# plugin-specific deps like @ai-sdk/openai + ai), and @btst/stack peers.
# next-themes is generated by shadcn init (mode-toggle.tsx, sonner.tsx) but not auto-installed.
# @btst/better-auth-ui must be present before the next step for peer resolution.
npm install @btst/adapter-memory next-themes $PLUGIN_EXTRA_PACKAGES $STACK_PEERS --legacy-peer-deps
BETTER_AUTH_UI_PEERS=$(node -e '
const fs=require("fs");
const p=JSON.parse(fs.readFileSync("node_modules/@btst/better-auth-ui/package.json","utf8"));
const skip=new Set(["react","react-dom","tailwindcss","@btst/stack","@btst/yar","better-auth","@tanstack/react-query"]);
const optionalPrefixes=["@triplit","@instantdb","@daveyplate"];
const keys=Object.keys(p.peerDependencies||{}).filter(d=>!skip.has(d)&&!optionalPrefixes.some(pre=>d.startsWith(pre)));
process.stdout.write(keys.join(" "));
')
if [ -n "$BETTER_AUTH_UI_PEERS" ]; then
npm install $BETTER_AUTH_UI_PEERS --legacy-peer-deps
fi
success "Installed runtime deps (adapter + plugin extras + @btst/stack and @btst/better-auth-ui peers)"
# Re-enable strict peer resolution here so this fixture catches incompatible cohorts.
rm .npmrc
npm install --save-exact @btst/adapter-memory@2.2.3 next-themes $PLUGIN_EXTRA_PACKAGES $STACK_PEERS
success "Installed aligned runtime deps with strict peer resolution"

BTST_CLI_VERSION=$(npx --yes @btst/cli@2.2.3 --version)
test "$BTST_CLI_VERSION" = "2.2.3"
test ! -e node_modules/@btst/cli
success "Ran @btst/cli@2.2.3 without adding it to the consumer graph"

step "Asserting generated files and patches"
test -f "lib/stack.ts"
Expand Down
19 changes: 13 additions & 6 deletions packages/cli/src/templates/nextjs/pages-layout.tsx.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import { ChatLayout } from "@btst/stack/plugins/ai-chat/client"
{{/if}}
import { QueryClientProvider } from "@tanstack/react-query"
{{#if hasBetterAuthUi}}
import { useRouter{{#if hasAiChat}}, usePathname{{/if}} } from "next/navigation"
{{else}}
import { createBetterAuthProvider } from "@btst/better-auth-ui"
{{/if}}
{{#if hasAiChat}}
import { usePathname } from "next/navigation"
{{/if}}
{{/if}}
import { getOrCreateQueryClient } from "{{alias}}lib/query-client"

function getBaseURL() {
Expand All @@ -27,14 +26,19 @@ function getBaseURL() {
return "http://localhost:3000"
}

{{#if hasBetterAuthUi}}
// TODO: replace this placeholder with your Better Auth client import.
const authClient = undefined as any
const stackAuth = createBetterAuthProvider(authClient, {
loginPath: "/pages/auth/sign-in",
})

{{/if}}
export default function BtstPagesLayout({
children,
}: {
children: React.ReactNode
}) {
{{#if hasBetterAuthUi}}
const router = useRouter()
{{/if}}
const queryClient = getOrCreateQueryClient()
{{#if hasAiChat}}
const hasApiKey = typeof process !== "undefined" && !!process.env.NEXT_PUBLIC_HAS_OPENAI_KEY
Expand All @@ -49,6 +53,9 @@ export default function BtstPagesLayout({
basePath="/pages"
router={nextRouter()}
api={{{providerApiLiteral}}}
{{#if hasBetterAuthUi}}
auth={stackAuth}
{{/if}}
{{#if pagesLayoutOverrides}}
overrides={
{
Expand Down
Loading
Loading