跳转到内容

Astro DB

Astro DB 是一款专为 Astro 设计的全托管 SQL 数据库。你可以在本地开发或者连接到我们 Astro Studio 平台上托管的数据库。

使用 @astrojs/db 集成v0.8.1 或更高版本)来将 Astro DB 添加到新的或现有的 Astro 项目中(需要 astro@4.5 或更高版本)。Astro 包含一个内置的 astro add 命令能帮你自动化设置此过程。

Terminal window
npx astro add db

如果你愿意,也可以手动安装 @astrojs/db

Astro DB 是一个用于配置、开发和查询你的数据的完整解决方案。每当你运行 astro dev 时,都会创建一个本地数据库,并使用 LibSQL 来管理你的数据,而无需 Docker 或网络连接。

使用 astro add 命令安装 @astrojs/db 将在你的项目中创建一个 db/config.ts 文件,你将在其中定义你的数据库表:

db/config.ts
import { defineDb } from 'astro:db';
export default defineDb({
tables: { },
})

Astro DB 中的数据存储在 SQL 表中。表将你的数据结构化为行和列,其中每列决定每行对应值的类型。Astro DB 支持以下列类型:

db/config.ts
import { defineTable, column } from 'astro:db';
const Comment = defineTable({
columns: {
// 一段文本字符串。
author: column.text(),
// 一个整数值。
likes: column.number(),
// 一个真或假的值。
flagged: column.boolean(),
// 作为 JavaScript Date 对象查询的日期/时间值。
published: column.date(),
// 一个未类型化的 JSON 对象。
metadata: column.json(),
}
});

表驱动 Astro 的自动 TypeScript 类型并为你的内容提供支持。当你定义一个表时,Astro 会为其自动生成并应用一个 TypeScript 接口。最终当你查询你的数据时,你将得到完整的 TypeScript 支持,包括属性自动补全和类型检查。

要配置一个独立的数据库表,应从 astro:db 导入并使用 defineTable()column 工具。

这个例子配置了一个 Comment 表,它有 authorbody 的必需文本列,然后通过 defineDb() 导出使其在你的项目中可用。

db/config.ts
import { defineDb, defineTable, column } from 'astro:db';
const Comment = defineTable({
columns: {
author: column.text(),
body: column.text(),
}
})
export default defineDb({
tables: { Comment },
})
查看 表配置参考 以获取完整的表选项参考。

表之间的关系是数据库设计中的常见模式。例如,一个 Blog 表可能与 CommentAuthorCategory 等其他几个表密切相关。

你可以使用引用列定义来这些表之间的关系并将它们保存到你的数据库模式中。要建立关系,你需要:

  • 在被引用的表上有一个标识符列。这通常是带有 primaryKey 属性的 id 列。
  • 在基础表上有一个用于存储被引用 id 的列。这将使用 references 属性建立关系。

这个例子显示了一个 Comment 表的 authorId 列引用了 Author 表的 id 列。

db/config.ts
const Author = defineTable({
columns: {
id: column.number({ primaryKey: true }),
name: column.text(),
}
});
const Comment = defineTable({
columns: {
authorId: column.number({ references: () => Author.columns.id }),
content: column.text(),
}
});

为你的数据库填充数据

段落标题 为你的数据库填充数据

在开发过程中,Astro 将使用你的 DB 配置并根据你的模式生成本地类型。这些内容将在每次启动开发服务器时重新生成,并允许你使用类型安全和自动补全来查询和处理你的数据样例。

要为你的 Astro 项目填充开发数据进行测试和调试,你需要创建一个 db/seed.ts 文件。从 astro:db 导入 db 对象和任何配置的表,然后使用 db.insert() 函数提供一个表行数据对象的数组。

以下例子为 Comment 表定义了两行开发数据:

db/seed.ts
import { db, Comment } from 'astro:db';
export default async function() {
await db.insert(Comment).values([
{ authorId: 1, body: 'Hope you like Astro DB!' },
{ authorId: 2, body: 'Enjoy!'},
])
}

你的开发服务器将在此文件更改时自动重启你的数据库,然后重新生成你的类型并从 seed.ts 填充你的开发数据。

你可以从项目中的任何 Astro 页面端点 使用提供的 db ORM 和查询构建器来查询你的数据库。

import { db } from 'astro:db';

Astro DB 包含一个内置的 Drizzle ORM 客户端。使用该客户端无需设置或手动配置。当你运行 Astro 时,Astro DB 的 db 客户端会自动配置以与你的数据库(本地或远程)进行通信。它使用你明确的数据库模式定义进行类型安全的 SQL 查询,当你引用不存在的列或表时,会出现 TypeScript 错误。

以下例子选择了 Comment 表的所有行。这将返回来自 db/seed.ts 全量的填充的开发数据数组,然后可以在你的页面模板中使用它们:

src/pages/index.astro
---
import { db, Comment } from 'astro:db';
const comments = await db.select().from(Comment);
---
<h2>Comments</h2>
{
comments.map(({ author, body }) => (
<article>
<p>Author: {author}</p>
<p>{body}</p>
</article>
))
}
查看 Drizzle select() API 参考 以获取完整概述。

要接受用户输入,如处理表单请求并将数据插入到你的远程托管数据库,需要为你的 Astro 项目配置 按需渲染 并为你的部署环境 添加一个 SSR 适配器

这个例子基于解析后表单的 POST 请求向 Comment 表插入一行:

src/pages/index.astro
---
import { db, Comment } from 'astro:db';
if (Astro.request.method === 'POST') {
// 解析表单数据
const formData = await Astro.request.formData();
const author = formData.get('author');
const content = formData.get('content');
if (typeof author === 'string' && typeof content === 'string') {
// 将表单数据插入到 Comment 表中
await db.insert(Comment).values({ author, content });
}
}
// 在每次请求上渲染新的评论列表
const comments = await db.select().from(Comment);
---
<form method="POST" style="display: grid">
<label for="author">Author</label>
<input id="author" name="author" />
<label for="content">Content</label>
<textarea id="content" name="content"></textarea>
<button type="submit">Submit</button>
</form>
<!--渲染 `comments`-->

你也可以从 API 端点查询你的数据库。这个例子通过 id 参数从 Comment 表中删除一行:

src/pages/api/comments/[id].ts
import type { APIRoute } from "astro";
import { db, Comment } from 'astro:db';
export const DELETE: APIRoute = async (ctx) => {
await db.delete(Comment).where({ id: ctx.params.id });
return new Response(null, { status: 204 });
}

查看 Drizzle insert() API 参考 以获取完整概述。

要通过特定属性查询表结果,请使用 Drizzle 的部分查询选项。例如,向你的 select() 查询添加 一个 .where() 调用,并传递你想做的比较操作。

以下例子查询了 Comment 表中包含 “Astro DB” 短语的所有行。使用 like() 操作符 检查 body 中是否存在短语:

src/pages/index.astro
---
import { db, Comment, like } from 'astro:db';
const comments = await db.select().from(Comment).where(
like(Comment.body, '%Astro DB%')
);
---

所有用于构建查询的 Drizzle 辅助工具都从 astro:db 模块中暴露出来。这包括:

import { eq, gt, count, sql } from 'astro:db';

你可以使用 SQL 连接从多个表查询关联的数据。要创建一个连接查询,请使用连接操作符扩展你的 db.select() 语句。每个函数都接受一个要连接的表和一个条件来匹配两个表之间的行。

这个例子使用了 innerJoin() 函数将 Comment 的作者与他们相关的 Author 信息连接起来,基于 authorId 列。这将返回一个对象数组,每个 AuthorComment 行作为顶级属性:

src/pages/index.astro
---
import { db, eq, Comment, Author } from 'astro:db';
const comments = await db.select()
.from(Comment)
.innerJoin(Author, eq(Comment.authorId, Author.id));
---
<h2>Comments</h2>
{
comments.map(({ Author, Comment }) => (
<article>
<p>Author: {Author.name}</p>
<p>{Comment.body}</p>
</article>
))
}

查看 Drizzle 连接参考 以获取所有可用的连接操作符和配置选项。

所有远程数据库查询都作为网络请求进行。当进行大量查询,或者如果任何查询失败需要自动回滚时,你可能需要将查询批量处理为单个事务。

这个例子使用 db.batch() 方法在单个请求中填充多行:

db/seed.ts
import { db, Author, Comment } from 'astro:db';
export default async function () {
const queries = [];
// 使用单个网络请求将 100 个样本数据填充到你的远程数据库中。
for (let i = 0; i < 100; i++) {
queries.push(db.insert(Comment).values({ body: `Test comment ${i}` }));
}
await db.batch(queries);
}

查看 Drizzle db.batch() 文档以获取更多详细信息。

Studio 功能

Astro DB 可以连接到 Astro Studio 平台,快速为你的项目添加一个托管数据库。你可以从 Astro Studio 站点上查看、管理和部署新的托管数据库。

在 Astro Studio 中创建项目有两种方式:

  1. 通过 Astro Studio CLI 使用 astro link
  2. 通过 Astro Studio 网页 UI

要在 Astro Studio 网页 UI 中创建新的数据库,点击头部中的 “create project” 按钮并按照指示操作。Astro Studio 将引导你完成必要的步骤,流程结束时,你将为你的项目配置一个托管数据库。

Studio 功能

随着你项目的增长,你的表模式也会随着时间的推移而改变。你可以在本地安全地测试配置更改,并在部署时推送到你的 Studio 数据库。

从仪表板创建 Studio 项目时,你将有创建 GitHub CI 流水线的选项。这将在与你的仓库的主分支合并时自动迁移模式更改。

你也可以使用 astro db push 命令通过 CLI 推送模式变更:

Terminal window
npm run astro db push

这个命令将验证更改是否可以在无数据丢失的情况下进行,并指导如何推荐模式更改以解决冲突。如果必须进行破坏性的模式更改,添加 --force-reset 标志以重置所有生产数据。

Studio 功能

你可能需要将数据推送到你的 Studio 数据库进行填充或数据迁移。你可以使用 astro:db 模块创建一个 .ts 文件来编写类型安全的查询。然后,使用命令 astro db execute <file-path> --remote 执行该文件对你的 Studio 数据库:

以下评论可以使用命令 astro db execute db/seed.ts --remote 进行填充:

db/seed.ts
import { Comment } from 'astro:db';
export default async function () {
await db.insert(Comment).values([
{ authorId: 1, body: 'Hope you like Astro DB!' },
{ authorId: 2, body: 'Enjoy!' },
])
}

查看 CLI 参考 获取完整的命令列表。

Studio 功能

默认情况下,当你运行 devbuild 命令时,Astro 将使用本地数据库文件。每次运行命令时,表都会从头开始重新创建,并插入开发的样例数据。

要连接到你的托管 Studio 数据库,你可以添加 --remote 参数。使用此参数进行生产部署,以便对你的 Studio 数据库拥有可读写权限。这将允许你 接受并持久化用户数据

Terminal window
# 使用远程连接进行构建
astro build --remote
# 使用远程连接进行开发
astro dev --remote

要使用远程连接,你需要一个应用令牌来进行 Studio 认证。请访问 Studio 控制面板获取令牌创建和设置指南。

当你准备好部署时,参见我们的 使用 Studio 连接进行部署指南

Astro 集成 可以通过额外的 Astro DB 表和填充数据来扩展用户项目。

astro:db:setup 钩子中使用 extendDb() 方法注册额外的 Astro DB 配置和填充文件。 defineDbIntegration() 辅助函数为 astro:db:setup 钩子提供 TypeScript 支持和自动补全。

my-integration/index.ts
import { defineDbIntegration } from '@astrojs/db/utils';
export default function MyIntegration() {
return defineDbIntegration({
name: 'my-astro-db-powered-integration',
hooks: {
'astro:db:setup': ({ extendDb }) => {
extendDb({
configEntrypoint: '@astronaut/my-package/config',
seedEntrypoint: '@astronaut/my-package/seed',
});
},
// 其他集成钩子...
},
});
}

集成的 配置填充 文件遵循与其用户定义的等效项相同的格式。

在集成中进行类型安全操作

段落标题 在集成中进行类型安全操作

在进行集成工作时,你可能无法从 astro:db 导出的 Astro 生成的表类型中获益。 为了完全的类型安全,使用 asDrizzleTable() 辅助工具创建一个可以用于数据库操作的表引用对象。

例如,给定一个集成设置以下 Pets 数据库表:

my-integration/config.ts
import { defineDb, defineTable, column } from 'astro:db';
export const Pets = defineTable({
columns: {
name: column.text(),
species: column.text(),
},
});
export default defineDb({ tables: { Pets } });

填充文件可以导入 Pets 并使用 asDrizzleTable() 向你的表插入具有类型检查的行:

my-integration/seed.ts
import { asDrizzleTable } from '@astrojs/db/utils';
import { db } from 'astro:db';
import { Pets } from './config';
export default async function() {
const typeSafePets = asDrizzleTable('Pets', Pets);
await db.insert(typeSafePets).values([
{ name: 'Palomita', species: 'cat' },
{ name: 'Pan', species: 'dog' },
]);
}

asDrizzleTable('Pets', Pets) 返回的值等同于 import { Pets } from 'astro:db',但即使 Astro 的类型生成无法运行时也可用。 你可以在任何需要查询或插入数据库的集成代码中使用它。