mirror of
https://github.com/better-auth/better-auth.git
synced 2026-05-29 10:26:49 -05:00
docs: cli
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { prismaAdapter } from "better-auth/adapters";
|
||||
import { prismaAdapter, } from "better-auth/adapters";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@@ -133,6 +133,23 @@ export const contents: Content[] = [
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "CLI",
|
||||
icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1.2em"
|
||||
height="1.2em"
|
||||
viewBox="0 0 256 256"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M216 40H40a16 16 0 0 0-16 16v144a16 16 0 0 0 16 16h176a16 16 0 0 0 16-16V56a16 16 0 0 0-16-16m-91 94.25l-40 32a8 8 0 1 1-10-12.5L107.19 128L75 102.25a8 8 0 1 1 10-12.5l40 32a8 8 0 0 1 0 12.5M176 168h-40a8 8 0 0 1 0-16h40a8 8 0 0 1 0 16"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
href: "/docs/concepts/cli",
|
||||
},
|
||||
{
|
||||
title: "Cookies",
|
||||
href: "/docs/concepts/cookies",
|
||||
|
||||
32
docs/content/docs/concepts/cli.mdx
Normal file
32
docs/content/docs/concepts/cli.mdx
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: CLI
|
||||
description: built in CLI for managing your project
|
||||
---
|
||||
|
||||
Better Auth comes with a built-in CLI to help you manage the database schema needed for both core functionality and plugins.
|
||||
|
||||
## Generate
|
||||
|
||||
The `generate` command creates the schema required by Better Auth. If you're using a database adapter like Prisma or Drizzle, this command will generate the right schema for your ORM. If you're using the built-in Kysely adapter, it will generate an SQL file you can run directly on your database.
|
||||
|
||||
```bash title="Terminal"
|
||||
pnpm better-auth generate
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
- `--output` - Where to save the generated schema. For Prisma, it will be saved in prisma/schema.prisma. For Drizzle, it goes to schema.ts in your project root. For Kysely, it’s an SQL file saved as schema.sql in your project root.
|
||||
- `--config` - The path to your Better Auth config file. By default, the CLI will search for a better-auth.ts file in **./**, **./utils**, **./lib**, or any of these directories under `src` directory.
|
||||
|
||||
|
||||
## Migrate
|
||||
|
||||
The migrate command applies the Better Auth schema directly to your database. This is available if you’re using the built-in Kysely adapter.
|
||||
|
||||
```bash title="Terminal"
|
||||
pnpm better-auth migrate
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
- `--config` - The path to your Better Auth config file. By default, the CLI will search for a better-auth.ts file in **./**, **./utils**, **./lib**, or any of these directories under `src` directory.
|
||||
@@ -60,9 +60,42 @@ See <Link href="https://kysely.dev/docs/dialects" target="_blank"> Kysley Dialec
|
||||
</Callout>
|
||||
|
||||
|
||||
## Using Adapters
|
||||
|
||||
If your database is managed by an ORM like Prisma or Drizzle, you can use the corresponding adapter to connect to the database. Better auth comes with built-in adapters for Prisma and Drizzle. You can pass the adapter to the `database` object in the auth options.
|
||||
|
||||
**Example: Prisma**
|
||||
```ts title="auth.ts"
|
||||
import { betterAuth } from "better-auth"
|
||||
import { PrismaAdapter } from "better-auth/adapters/prisma"
|
||||
|
||||
const prisma = new PrismaAdapter({
|
||||
prisma: prismaClient
|
||||
})
|
||||
|
||||
export const auth = await betterAuth({
|
||||
database: prismaAdapter(prisma, {
|
||||
provider: "sqlite" // or "postgres" or "mysql" or any other supported by prisma
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**Example: Drizzle**
|
||||
```ts title="auth.ts"
|
||||
import { betterAuth } from "better-auth"
|
||||
import { db } from "./drizzle"
|
||||
import { drizzleAdapter } from "better-auth/adapters"
|
||||
|
||||
export const auth = await betterAuth({
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "sqlite" // or "pg" or "mysql"
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Running Migrations
|
||||
|
||||
Better auth comes with a CLI tool to manage database migrations. Use the `migrate` command to create or update tables as needed.
|
||||
Better auth comes with a CLI tool to manage database migrations. Use the `migrate` command to create or update tables as needed.
|
||||
|
||||
The cli checks your database and prompts you to add missing tables or update existing ones with new columns.
|
||||
|
||||
@@ -70,7 +103,19 @@ The cli checks your database and prompts you to add missing tables or update exi
|
||||
npx better-auth migrate
|
||||
```
|
||||
|
||||
## Running Migrations Manually and Generting Schema
|
||||
|
||||
Better auth also provides a `generate` command to generate the schema required by better auth. The `generate` command creates the schema required by Better Auth. If you're using a database adapter like Prisma or Drizzle, this command will generate the right schema for your ORM. If you're using the built-in Kysely adapter, it will generate an SQL file you can run directly on your database.
|
||||
|
||||
```bash
|
||||
npx better-auth generate
|
||||
```
|
||||
|
||||
See the [CLI](/docs/concepts/cli) documentation for more information on the CLI.
|
||||
|
||||
<Callout>
|
||||
If you prefer adding tables manually, you can do that as well. The core schema required by better auth is described below and you can find additional schema required by plugins in the plugin documentation.
|
||||
</Callout>
|
||||
|
||||
## Core Schema
|
||||
|
||||
|
||||
@@ -66,40 +66,39 @@ export const auth = betterAuth({
|
||||
|
||||
<Step>
|
||||
### Configure Database
|
||||
|
||||
Better Auth requires a database to store user data. By default, it uses <Link href="https://kysely.dev/">Kysely </Link> under the hood to connect and query your database. `postgresql`, `mysql`, and `sqlite` are supported out of the box.
|
||||
|
||||
```ts title="auth.ts"
|
||||
|
||||
Better Auth requires a database to store user data. By default, it uses [Kysely](https://kysely.dev/) for database connections and queries, with support for `postgresql`, `mysql`, and `sqlite` out of the box.
|
||||
|
||||
```ts title="auth.ts"
|
||||
import { betterAuth } from "better-auth"
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: { // [!code highlight]
|
||||
provider: "sqlite", // or "mysql", "postgresql" // [!code highlight]
|
||||
url: "./db.sqlite", // path to your database or connection string // [!code highlight]
|
||||
} // [!code highlight]
|
||||
database: {
|
||||
provider: "sqlite", // or "mysql", "postgresql"
|
||||
url: "./db.sqlite", // path to your database or connection string
|
||||
}
|
||||
})
|
||||
```
|
||||
You can also pass any dialect that is supported by Kysely to the database configration.
|
||||
|
||||
You can also use any dialect supported by Kysely in the database configuration.
|
||||
|
||||
**Example with LibsqlDialect:**
|
||||
|
||||
```ts title="auth.ts"
|
||||
```ts title="auth.ts"
|
||||
import { betterAuth } from "better-auth";
|
||||
import { LibsqlDialect } from "@libsql/kysely-libsql";
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: new LibsqlDialect({
|
||||
url: process.env.TURSO_DATABASE_URL || "",
|
||||
authToken: process.env.TURSO_AUTH_TOKEN || "",
|
||||
}),
|
||||
database: new LibsqlDialect({
|
||||
url: process.env.TURSO_DATABASE_URL || "",
|
||||
authToken: process.env.TURSO_AUTH_TOKEN || "",
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
**Adapters**
|
||||
|
||||
If your database is not supported by Kysely, you can use an adapter to connect to your database.
|
||||
|
||||
To use an adapter, import the adapter and pass it to the database option.
|
||||
If your database isn’t supported by Kysely, you can use an adapter to connect. Simply import the adapter and pass it into the `database` option.
|
||||
|
||||
<Tabs items={["prisma", "drizzle", "mongodb"]}>
|
||||
<Tab value="prisma">
|
||||
@@ -111,7 +110,9 @@ To use an adapter, import the adapter and pass it to the database option.
|
||||
const prisma = new PrismaClient();
|
||||
export const auth = betterAuth({
|
||||
database: {
|
||||
provider: prismaAdapter(prisma),
|
||||
provider: prismaAdapter(prisma, {
|
||||
provider: "sqlite", // or "mysql", "postgresql", ...etc
|
||||
}),
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -121,10 +122,13 @@ To use an adapter, import the adapter and pass it to the database option.
|
||||
```ts title="auth.ts"
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters";
|
||||
import { db } from "@/db"; // your drizzle instance
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: {
|
||||
provider: drizzleAdapter(db),
|
||||
provider: drizzleAdapter(db, {
|
||||
provider: "pg", // or "mysql", "sqlite"
|
||||
}),
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -148,17 +152,27 @@ To use an adapter, import the adapter and pass it to the database option.
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Migrate Schema
|
||||
### Create Database Tables
|
||||
|
||||
Better Auth includes a CLI tool to help manage your database tables. You can either migrate tables directly or generate an ORM schema or SQL migration file.
|
||||
|
||||
Better Auth includes a CLI tool to migrate the required schema to your database. It introspects the database and creates the required tables. Run the following command to perform the migration:
|
||||
**Migrate**: Creates the necessary tables in your database.
|
||||
|
||||
```bash title="Terminal"
|
||||
npx better-auth migrate
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
If you're using an adapter and your database is not supported by Kysely, you need to create required tables manually. You can find the core schema required in the [database section](/docs/concepts/database#core-schema).
|
||||
</Callout>
|
||||
**Generate**: Generates the required ORM schema (e.g., Prisma, Drizzle) or an SQL migration file.
|
||||
|
||||
```bash title="Terminal"
|
||||
npx better-auth generate
|
||||
```
|
||||
|
||||
see the [CLI documentation](/docs/concepts/cli) for more information.
|
||||
|
||||
<Callout>
|
||||
If you instead want to create the schema manually, you can find the core schema required in the [database section](/docs/concepts/database#core-schema).
|
||||
</Callout>
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
|
||||
@@ -68,6 +68,7 @@ export const drizzleAdapter = (
|
||||
}
|
||||
const databaseType = options?.provider;
|
||||
return {
|
||||
id: "drizzle",
|
||||
async create(data) {
|
||||
const { model, data: val } = data;
|
||||
const schemaModel = getSchema(model, schema);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./prisma-adapter";
|
||||
export * from "./drizzle-adapter";
|
||||
export * from "./mongodb-adapter";
|
||||
export * from "./kysely-adapter";
|
||||
|
||||
89
packages/better-auth/src/adapters/kysely-adapter/dialect.ts
Normal file
89
packages/better-auth/src/adapters/kysely-adapter/dialect.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import Database from "better-sqlite3";
|
||||
import { Kysely } from "kysely";
|
||||
import {
|
||||
type Dialect,
|
||||
MysqlDialect,
|
||||
PostgresDialect,
|
||||
SqliteDialect,
|
||||
} from "kysely";
|
||||
import { createPool } from "mysql2";
|
||||
import type { BetterAuthOptions } from "../../types";
|
||||
import pg from "pg";
|
||||
import { BetterAuthError } from "../../error/better-auth-error";
|
||||
|
||||
const { Pool } = pg;
|
||||
|
||||
export const getDialect = (config: BetterAuthOptions) => {
|
||||
if (!config.database) {
|
||||
return undefined;
|
||||
}
|
||||
if ("createDriver" in config.database) {
|
||||
return config.database;
|
||||
}
|
||||
let dialect: Dialect | undefined = undefined;
|
||||
if ("provider" in config.database) {
|
||||
const provider = config.database.provider;
|
||||
const connectionString = config.database?.url?.trim();
|
||||
if (provider === "postgres") {
|
||||
dialect = new PostgresDialect({
|
||||
pool: new Pool({
|
||||
connectionString,
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (provider === "mysql") {
|
||||
try {
|
||||
const params = new URL(connectionString);
|
||||
const pool = createPool({
|
||||
host: params.hostname,
|
||||
user: params.username,
|
||||
password: params.password,
|
||||
database: params.pathname.split("/")[1],
|
||||
port: Number(params.port),
|
||||
});
|
||||
dialect = new MysqlDialect({ pool });
|
||||
} catch (e) {
|
||||
if (e instanceof TypeError) {
|
||||
throw new BetterAuthError("Invalid database URL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (provider === "sqlite") {
|
||||
const db = new Database(connectionString);
|
||||
dialect = new SqliteDialect({
|
||||
database: db,
|
||||
});
|
||||
}
|
||||
}
|
||||
return dialect;
|
||||
};
|
||||
|
||||
export const createKyselyAdapter = (config: BetterAuthOptions) => {
|
||||
const dialect = getDialect(config);
|
||||
if (!dialect) {
|
||||
return dialect;
|
||||
}
|
||||
const db = new Kysely<any>({
|
||||
dialect,
|
||||
});
|
||||
return db;
|
||||
};
|
||||
|
||||
export const getDatabaseType = (config: BetterAuthOptions) => {
|
||||
if ("provider" in config.database) {
|
||||
return config.database.provider;
|
||||
}
|
||||
if ("dialect" in config.database) {
|
||||
if (config.database.dialect instanceof PostgresDialect) {
|
||||
return "postgres";
|
||||
}
|
||||
if (config.database.dialect instanceof MysqlDialect) {
|
||||
return "mysql";
|
||||
}
|
||||
if (config.database.dialect instanceof SqliteDialect) {
|
||||
return "sqlite";
|
||||
}
|
||||
}
|
||||
return "sqlite";
|
||||
};
|
||||
@@ -1,20 +1,7 @@
|
||||
import Database from "better-sqlite3";
|
||||
import { Kysely } from "kysely";
|
||||
import {
|
||||
type Dialect,
|
||||
MysqlDialect,
|
||||
PostgresDialect,
|
||||
SqliteDialect,
|
||||
} from "kysely";
|
||||
import { createPool } from "mysql2";
|
||||
import type { FieldAttribute } from ".";
|
||||
import type { BetterAuthOptions } from "../types";
|
||||
import type { Adapter, Where } from "../types/adapter";
|
||||
import pg from "pg";
|
||||
import { BetterAuthError } from "../error/better-auth-error";
|
||||
import { getMigrations } from "../cli/utils/get-migration";
|
||||
|
||||
const { Pool } = pg;
|
||||
import type { Kysely } from "kysely";
|
||||
import type { FieldAttribute } from "../../db";
|
||||
import type { Adapter, Where } from "../../types";
|
||||
import { getMigrations } from "../../cli/utils/get-migration";
|
||||
|
||||
function convertWhere(w?: Where[]) {
|
||||
if (!w)
|
||||
@@ -107,6 +94,7 @@ export const kyselyAdapter = (
|
||||
config?: KyselyAdapterConfig,
|
||||
): Adapter => {
|
||||
return {
|
||||
id: "kysely",
|
||||
async create(data) {
|
||||
let { model, data: val, select } = data;
|
||||
if (config?.transform) {
|
||||
@@ -240,78 +228,3 @@ export const kyselyAdapter = (
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getDialect = (config: BetterAuthOptions) => {
|
||||
if (!config.database) {
|
||||
return undefined;
|
||||
}
|
||||
if ("createDriver" in config.database) {
|
||||
return config.database;
|
||||
}
|
||||
let dialect: Dialect | undefined = undefined;
|
||||
if ("provider" in config.database) {
|
||||
const provider = config.database.provider;
|
||||
const connectionString = config.database?.url?.trim();
|
||||
if (provider === "postgres") {
|
||||
dialect = new PostgresDialect({
|
||||
pool: new Pool({
|
||||
connectionString,
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (provider === "mysql") {
|
||||
try {
|
||||
const params = new URL(connectionString);
|
||||
const pool = createPool({
|
||||
host: params.hostname,
|
||||
user: params.username,
|
||||
password: params.password,
|
||||
database: params.pathname.split("/")[1],
|
||||
port: Number(params.port),
|
||||
});
|
||||
dialect = new MysqlDialect({ pool });
|
||||
} catch (e) {
|
||||
if (e instanceof TypeError) {
|
||||
throw new BetterAuthError("Invalid database URL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (provider === "sqlite") {
|
||||
const db = new Database(connectionString);
|
||||
dialect = new SqliteDialect({
|
||||
database: db,
|
||||
});
|
||||
}
|
||||
}
|
||||
return dialect;
|
||||
};
|
||||
|
||||
export const createKyselyAdapter = (config: BetterAuthOptions) => {
|
||||
const dialect = getDialect(config);
|
||||
if (!dialect) {
|
||||
return dialect;
|
||||
}
|
||||
const db = new Kysely<any>({
|
||||
dialect,
|
||||
});
|
||||
return db;
|
||||
};
|
||||
|
||||
export const getDatabaseType = (config: BetterAuthOptions) => {
|
||||
if ("provider" in config.database) {
|
||||
return config.database.provider;
|
||||
}
|
||||
if ("dialect" in config.database) {
|
||||
if (config.database.dialect instanceof PostgresDialect) {
|
||||
return "postgres";
|
||||
}
|
||||
if (config.database.dialect instanceof MysqlDialect) {
|
||||
return "mysql";
|
||||
}
|
||||
if (config.database.dialect instanceof SqliteDialect) {
|
||||
return "sqlite";
|
||||
}
|
||||
}
|
||||
return "sqlite";
|
||||
};
|
||||
@@ -73,6 +73,7 @@ interface MongoClient {
|
||||
export const mongodbAdapter = (mongo: any) => {
|
||||
const db: MongoClient = mongo;
|
||||
return {
|
||||
id: "mongodb",
|
||||
async create(data) {
|
||||
const { model, data: val } = data;
|
||||
const res = await db.collection(model).insertOne({
|
||||
|
||||
@@ -64,6 +64,7 @@ export const prismaAdapter = ({
|
||||
}): Adapter => {
|
||||
const db: PrismaClient = prisma;
|
||||
return {
|
||||
id: "prisma",
|
||||
async create(data) {
|
||||
const { model, data: val, select } = data;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { z } from "zod";
|
||||
import { existsSync } from "fs";
|
||||
import path from "path";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { createKyselyAdapter } from "../../db/kysely";
|
||||
import { createKyselyAdapter } from "../../adapters/kysely-adapter/dialect";
|
||||
import ora from "ora";
|
||||
import chalk from "chalk";
|
||||
import prompts from "prompts";
|
||||
|
||||
@@ -7,7 +7,10 @@ import type { FieldAttribute, FieldType } from "../../db";
|
||||
import { logger } from "../../utils/logger";
|
||||
import type { BetterAuthOptions } from "../../types";
|
||||
import { getSchema } from "./get-schema";
|
||||
import { createKyselyAdapter, getDatabaseType } from "../../db/kysely";
|
||||
import {
|
||||
createKyselyAdapter,
|
||||
getDatabaseType,
|
||||
} from "../../adapters/kysely-adapter/dialect";
|
||||
|
||||
const postgresMap = {
|
||||
string: ["character varying", "text"],
|
||||
|
||||
@@ -3,7 +3,11 @@ import { BetterAuthError } from "../error/better-auth-error";
|
||||
import type { BetterAuthOptions } from "../types";
|
||||
import type { Adapter } from "../types/adapter";
|
||||
import { getAuthTables } from "./get-tables";
|
||||
import { createKyselyAdapter, getDatabaseType, kyselyAdapter } from "./kysely";
|
||||
import {
|
||||
createKyselyAdapter,
|
||||
getDatabaseType,
|
||||
} from "../adapters/kysely-adapter/dialect";
|
||||
import { kyselyAdapter } from "../adapters/kysely-adapter";
|
||||
|
||||
export function getAdapter(options: BetterAuthOptions): Adapter {
|
||||
if (!options.database) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Kysely } from "kysely";
|
||||
import { getAuthTables } from "./db/get-tables";
|
||||
import { createKyselyAdapter } from "./db/kysely";
|
||||
import { createKyselyAdapter } from "./adapters/kysely-adapter/dialect";
|
||||
import { getAdapter } from "./db/utils";
|
||||
import { hashPassword, verifyPassword } from "./crypto/password";
|
||||
import { createInternalAdapter } from "./db";
|
||||
|
||||
@@ -16,6 +16,7 @@ export type Where = {
|
||||
* Adapter Interface
|
||||
*/
|
||||
export interface Adapter {
|
||||
id: string;
|
||||
create: <T, R = T>(data: {
|
||||
model: string;
|
||||
data: T;
|
||||
|
||||
Reference in New Issue
Block a user