功能特性数据库 ORM

数据库 ORM

ShipAny 默认使用 Supabase 作为数据库依赖,你可以参考:数据库 一章配置数据库连接。

如果你需要使用其他数据库,比如 PostgreSQL / MySQL / SQLite 等,可以参考本章,配置 ORM 连接。

新项目初始化

ShipAny 在 drizzle 分支下,提供了支持 drizzle ORM 的代码。在开发新项目时,你可以选择使用此分支初始化项目。

此分支不支持 edge-runtime,不能部署到 cloudflare pages,适合部署到 Vercel 或自建服务器。

此分支代码改动较大,如果是旧项目,不建议同步此分支进行升级。

  1. 拉取代码
Terminal
git clone -b drizzle [email protected]:shipanyai/shipany-template-one.git my-shipany-project
  1. 创建配置文件
Terminal
cp .env.example .env.development
  1. 配置数据库连接

ShipAny 默认支持 SQLite / MySQL / PostgreSQL 三种数据库,根据你的项目需求,填写其中一个数据库的连接信息即可。

.env.development
SQLITE_URL = "file:data/shipany.db"
MYSQL_URL = "mysql://aigc:[email protected]:3306/shipany"
POSTGRES_URL = "postgresql://aigc:[email protected]:5432/shipany"

使用 PostgreSQL

如果你使用 PostgreSQL 或者 Supabase / Neon 等类 PostgreSQL 数据库,可以以下流程配置 ORM 连接。

  1. 定义数据结构

你可以按需修改默认的数据结构定义:

./drizzle/postgres/schema.ts
import {
  index,
  integer,
  pgTable,
  text,
  uniqueIndex,
  varchar,
} from "drizzle-orm/pg-core";
 
export const users = pgTable(
  "users",
  {
    id: integer().primaryKey().generatedAlwaysAsIdentity(),
    uuid: varchar({ length: 255 }).unique().notNull(),
    email: varchar({ length: 255 }).notNull(),
    created_at: varchar({ length: 255 }),
    nickname: varchar({ length: 255 }),
    avatar_url: varchar({ length: 255 }),
    locale: varchar({ length: 50 }),
    signin_type: varchar({ length: 50 }),
    signin_ip: varchar({ length: 255 }),
    signin_provider: varchar({ length: 50 }),
    signin_openid: varchar({ length: 255 }),
  },
  (table) => [
    uniqueIndex("email_provider_unique_idx").on(
      table.email,
      table.signin_provider
    ),
  ]
);
 
export const orders = pgTable("orders", {
  id: integer().primaryKey().generatedAlwaysAsIdentity(),
  order_no: varchar({ length: 255 }).unique().notNull(),
  created_at: varchar({ length: 255 }),
  user_uuid: varchar({ length: 255 }).notNull().default(""),
  user_email: varchar({ length: 255 }).notNull().default(""),
  amount: integer().notNull(),
  interval: varchar({ length: 50 }),
  expired_at: varchar({ length: 255 }),
  status: varchar({ length: 50 }).notNull(),
  stripe_session_id: varchar({ length: 255 }),
  credits: integer().notNull(),
  currency: varchar({ length: 50 }),
  sub_id: varchar({ length: 255 }),
  sub_interval_count: integer(),
  sub_cycle_anchor: integer(),
  sub_period_end: integer(),
  sub_period_start: integer(),
  sub_times: integer(),
  product_id: varchar({ length: 255 }),
  product_name: varchar({ length: 255 }),
  valid_months: integer(),
  order_detail: text(),
  paid_at: varchar({ length: 255 }),
  paid_email: varchar({ length: 255 }),
  paid_detail: text(),
});
 
export const apikeys = pgTable("apikeys", {
  id: integer().primaryKey().generatedAlwaysAsIdentity(),
  api_key: varchar({ length: 255 }).unique().notNull(),
  title: varchar({ length: 100 }),
  user_uuid: varchar({ length: 255 }).notNull(),
  created_at: varchar({ length: 255 }),
  status: varchar({ length: 50 }),
});
 
export const credits = pgTable("credits", {
  id: integer().primaryKey().generatedAlwaysAsIdentity(),
  trans_no: varchar({ length: 255 }).unique().notNull(),
  created_at: varchar({ length: 255 }),
  user_uuid: varchar({ length: 255 }).notNull(),
  trans_type: varchar({ length: 50 }).notNull(),
  credits: integer().notNull(),
  order_no: varchar({ length: 255 }),
  expired_at: varchar({ length: 255 }),
});
 
export const posts = pgTable(
  "posts",
  {
    id: integer().primaryKey().generatedAlwaysAsIdentity(),
    uuid: varchar({ length: 255 }).unique().notNull(),
    slug: varchar({ length: 255 }),
    title: varchar({ length: 255 }),
    description: text(),
    content: text(),
    created_at: varchar({ length: 255 }),
    updated_at: varchar({ length: 255 }),
    status: varchar({ length: 50 }),
    cover_url: varchar({ length: 255 }),
    author_name: varchar({ length: 255 }),
    author_avatar_url: varchar({ length: 255 }),
    locale: varchar({ length: 50 }),
  },
  (table) => [index("slug_locale_idx").on(table.slug, table.locale)]
);
  1. 导出数据表结构

数据读写操作时,默认导入 ./drizzle/schema.ts 文件里的数据表结构,你需要根据你的需求,导出数据表结构。

./drizzle/schema.ts
export * from "./postgres/schema";
  1. 修改数据操作客户端

默认的 PostgreSQL 数据操作客户端定义在 ./drizzle/postgres/db.ts 文件里,你可以按需修改:

./drizzle/postgres/db.ts
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
 
let globalPool: postgres.Sql | null = null;
 
export async function getDb() {
  if (!process.env.POSTGRES_URL) {
    throw new Error("POSTGRES_URL is not set");
  }
 
  if (!globalPool) {
    globalPool = postgres(process.env.POSTGRES_URL, {
      max: 10,
      idle_timeout: 20,
      connect_timeout: 10,
    });
  }
 
  const db = drizzle(globalPool);
 
  return db;
}
  1. 导出数据操作客户端

你需要导出实际使用的数据操作客户端,给到模型操作使用。

./drizzle/db.ts
export * from "./postgres/db";
  1. 导出 ORM 配置

按需修改 ORM 的配置,根据你选择的数据库,做对应的调整。

./drizzle/config.ts
import { config } from "dotenv";
import { defineConfig } from "drizzle-kit";
 
config({ path: ".env" });
config({ path: ".env.development" });
config({ path: ".env.local" });
 
export default defineConfig({
  out: "./drizzle/postgres/migration",
  schema: "./drizzle/postgres/schema.ts",
  dialect: "postgresql",
  dbCredentials: {
    url: process.env.POSTGRES_URL!,
  },
});
  1. 创建数据表

在终端运行以下命令:

Terminal
pnpm db:generate
pnpm db:migrate

根据 ./drizzle/postgres/schema.ts 文件里定义的数据表结构,生成迁移文件。 自动执行迁移文件中的 SQL 语句,创建数据表。

  1. 数据操作示例

使用 const db = await getDb() 获取数据操作客户端,然后根据你的需求,进行数据操作。 数据表类型从 ./drizzle/postgres/schema.ts 文件里导出。

./models/post.ts
import { and, desc, eq } from "drizzle-orm";
 
import { getDb } from "@/drizzle/db";
import { posts } from "@/drizzle/schema";
 
// insert data
export async function insertPost(post: typeof posts.$inferInsert) {
  const db = await getDb();
  await db.insert(posts).values(post);
 
  return post;
}
 
// update data
export async function updatePost(
  uuid: string,
  post: Partial<typeof posts.$inferInsert>
) {
  const db = await getDb();
  await db.update(posts).set(post).where(eq(posts.uuid, uuid));
 
  return post;
}
 
// select data
export async function findPostByUuid(
  uuid: string
): Promise<typeof posts.$inferSelect | undefined> {
  const db = await getDb();
  const data = await db
    .select()
    .from(posts)
    .where(eq(posts.uuid, uuid))
    .limit(1);
 
  return data ? data[0] : undefined;
}

使用 MySQL

如果你使用 MySQL 或 其他 MySQL 兼容数据库,可以以下流程修改 ORM 配置。

  1. 配置 MySQL 数据库连接信息
.env.development
MYSQL_URL = "mysql://aigc:[email protected]:3306/shipany"
  1. 导出 MySQL 数据表结构
./drizzle/schema.ts
export * from "./mysql/schema";
  1. 导出 MySQL 数据操作客户端
./drizzle/db.ts
export * from "./mysql/db";
  1. 导出 MySQL ORM 配置
./drizzle/config.ts
import { config } from "dotenv";
import { defineConfig } from "drizzle-kit";
 
config({ path: ".env" });
config({ path: ".env.development" });
config({ path: ".env.local" });
 
export default defineConfig({
  out: "./drizzle/mysql/migration",
  schema: "./drizzle/mysql/schema.ts",
  dialect: "mysql",
  dbCredentials: {
    url: process.env.MYSQL_URL!,
  },
});
  1. 创建数据表

在终端运行以下命令:

Terminal
pnpm db:generate
pnpm db:migrate

根据 ./drizzle/mysql/schema.ts 文件里定义的数据表结构,生成迁移文件。 自动执行迁移文件中的 SQL 语句,创建数据表。

  1. 自定义

数据表结构定义在 ./drizzle/mysql/schema.ts 文件里,你可以按需修改。

数据库连接客户端定义在 ./drizzle/mysql/db.ts 文件里,你可以按需修改。

数据读写等操作跟 PostgreSQL 写法一致。

使用 SQLite

如果你使用 SQLite 数据库,可以按以下流程修改 ORM 配置。

  1. 配置 SQLite 数据库连接信息
.env.development
SQLITE_URL = "file:data/shipany.db"

data/shipany.db 是你的本地文件,可以使用相对路径或绝对路径。

  1. 导出 SQLite 数据表结构
./drizzle/schema.ts
export * from "./sqlite/schema";
  1. 导出 SQLite 数据操作客户端
./drizzle/db.ts
export * from "./sqlite/db";
  1. 导出 SQLite ORM 配置
./drizzle/config.ts
import { config } from "dotenv";
import { defineConfig } from "drizzle-kit";
 
config({ path: ".env" });
config({ path: ".env.development" });
config({ path: ".env.local" });
 
export default defineConfig({
  out: "./drizzle/sqlite/migration",
  schema: "./drizzle/sqlite/schema.ts",
  dialect: "sqlite",
  dbCredentials: {
    url: process.env.SQLITE_URL!,
  },
});
  1. 创建数据表

在终端运行以下命令:

Terminal
pnpm db:generate
pnpm db:migrate

根据 ./drizzle/sqlite/schema.ts 文件里定义的数据表结构,生成迁移文件。 自动执行迁移文件中的 SQL 语句,创建数据表。

  1. 自定义

数据表结构定义在 ./drizzle/sqlite/schema.ts 文件里,你可以按需修改。

数据库连接客户端定义在 ./drizzle/sqlite/db.ts 文件里,你可以按需修改。

数据读写等操作跟 PostgreSQL 写法一致。

数据可视化管理

终端执行命令:

Terminal
pnpm db:studio

打开浏览器,访问 https://local.drizzle.studio/,可视化管理数据库。

orm-studio

参考