数据库 ORM
ShipAny 默认使用 Supabase 作为数据库依赖,你可以参考:数据库 一章配置数据库连接。
如果你需要使用其他数据库,比如 PostgreSQL / MySQL / SQLite 等,可以参考本章,配置 ORM 连接。
新项目初始化
ShipAny 在 drizzle
分支下,提供了支持 drizzle ORM 的代码。在开发新项目时,你可以选择使用此分支初始化项目。
此分支不支持 edge-runtime,不能部署到 cloudflare pages,适合部署到 Vercel 或自建服务器。
此分支代码改动较大,如果是旧项目,不建议同步此分支进行升级。
- 拉取代码
git clone -b drizzle [email protected]:shipanyai/shipany-template-one.git my-shipany-project
- 创建配置文件
cp .env.example .env.development
- 配置数据库连接
ShipAny 默认支持 SQLite / MySQL / PostgreSQL 三种数据库,根据你的项目需求,填写其中一个数据库的连接信息即可。
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 连接。
- 定义数据结构
你可以按需修改默认的数据结构定义:
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)]
);
- 导出数据表结构
数据读写操作时,默认导入 ./drizzle/schema.ts
文件里的数据表结构,你需要根据你的需求,导出数据表结构。
export * from "./postgres/schema";
- 修改数据操作客户端
默认的 PostgreSQL 数据操作客户端定义在 ./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;
}
- 导出数据操作客户端
你需要导出实际使用的数据操作客户端,给到模型操作使用。
export * from "./postgres/db";
- 导出 ORM 配置
按需修改 ORM 的配置,根据你选择的数据库,做对应的调整。
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!,
},
});
- 创建数据表
在终端运行以下命令:
pnpm db:generate
pnpm db:migrate
根据 ./drizzle/postgres/schema.ts
文件里定义的数据表结构,生成迁移文件。
自动执行迁移文件中的 SQL 语句,创建数据表。
- 数据操作示例
使用 const db = await getDb()
获取数据操作客户端,然后根据你的需求,进行数据操作。
数据表类型从 ./drizzle/postgres/schema.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 配置。
- 配置 MySQL 数据库连接信息
MYSQL_URL = "mysql://aigc:[email protected]:3306/shipany"
- 导出 MySQL 数据表结构
export * from "./mysql/schema";
- 导出 MySQL 数据操作客户端
export * from "./mysql/db";
- 导出 MySQL ORM 配置
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!,
},
});
- 创建数据表
在终端运行以下命令:
pnpm db:generate
pnpm db:migrate
根据 ./drizzle/mysql/schema.ts
文件里定义的数据表结构,生成迁移文件。
自动执行迁移文件中的 SQL 语句,创建数据表。
- 自定义
数据表结构定义在 ./drizzle/mysql/schema.ts
文件里,你可以按需修改。
数据库连接客户端定义在 ./drizzle/mysql/db.ts
文件里,你可以按需修改。
数据读写等操作跟 PostgreSQL 写法一致。
使用 SQLite
如果你使用 SQLite 数据库,可以按以下流程修改 ORM 配置。
- 配置 SQLite 数据库连接信息
SQLITE_URL = "file:data/shipany.db"
data/shipany.db
是你的本地文件,可以使用相对路径或绝对路径。
- 导出 SQLite 数据表结构
export * from "./sqlite/schema";
- 导出 SQLite 数据操作客户端
export * from "./sqlite/db";
- 导出 SQLite ORM 配置
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!,
},
});
- 创建数据表
在终端运行以下命令:
pnpm db:generate
pnpm db:migrate
根据 ./drizzle/sqlite/schema.ts
文件里定义的数据表结构,生成迁移文件。
自动执行迁移文件中的 SQL 语句,创建数据表。
- 自定义
数据表结构定义在 ./drizzle/sqlite/schema.ts
文件里,你可以按需修改。
数据库连接客户端定义在 ./drizzle/sqlite/db.ts
文件里,你可以按需修改。
数据读写等操作跟 PostgreSQL 写法一致。
数据可视化管理
终端执行命令:
pnpm db:studio
打开浏览器,访问 https://local.drizzle.studio/
,可视化管理数据库。