跳转到内容

Astro DB

Astro DB 是专为 Astro 生态系统设计的完全托管的 SQL 数据库。在 Astro 中进行本地开发,并部署到任何与 libSQL 兼容的数据库。

Astro DB 是一个用于配置、开发和查询数据的完整解决方案。每当你运行 astro dev 时,都会在 .astro/content.db 中创建一个本地数据库,以便你无需 Docker 或网络连接即可管理数据。

使用内置的 astro add 命令安装 @astrojs/db 集成

终端窗口
npx astro add db

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

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

Astro DB 中的数据使用 SQL 数据表进行存储。数据表将你的数据组织成行和列,其中列强制规定每行值的类型。

通过在你现有的 libSQL 数据库中提供数据结构,或在你将要收集数据的新数据库中提供数据结构,在 db/config.ts 文件中定义你的数据表。这将允许 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 },
})
有关表选项的完整参考,请参阅数据表配置参考

Astro DB 支持以下列类型

db/config.ts
import { defineTable, column } from 'astro:db';
const Comment = defineTable({
columns: {
// A string of text.
author: column.text(),
// A whole integer value.
likes: column.number(),
// A true or false value.
flagged: column.boolean(),
// Date/time values queried as JavaScript Date objects.
published: column.date(),
// An untyped JSON object.
metadata: column.json(),
}
});
有关更多详细信息,请参阅数据表列参考

数据表之间的关系是数据库设计中的一种常见模式。例如,一个 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 }),
body: column.text(),
}
});

在开发模式下,Astro 将使用你的 DB 配置根据你的模式生成本地类型。每次启动开发服务器时,这些类型都会从你的种子文件中重新生成,并允许你以类型安全和自动补全的方式查询和处理数据结构。

除非你在开发期间连接到远程数据库,否则你将无法访问生产数据。这可以保护你的数据,同时允许你使用具有类型安全性的工作数据库进行测试和开发。

要将用于测试和调试的开发数据填充到你的 Astro 项目中,请创建一个 db/seed.ts 文件。从 astro:db 导入 db 对象和你定义的数据表。insert 向每个表中插入一些初始数据。此开发数据应与你的数据库模式和生产数据的形式相匹配。

以下示例为一个 Comment 表和一个 Author 表定义了两行开发数据。

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

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

Astro DB 可以连接到任何本地 libSQL 数据库,或任何暴露 libSQL 远程协议的服务器,无论是托管的还是自托管的。

要将 Astro DB 连接到 libSQL 数据库,请设置从你的数据库提供商处获得的以下环境变量:

  • ASTRO_DB_REMOTE_URL:到你的本地或远程 libSQL 数据库位置的连接 URL。这可能包括 URL 配置选项,如同步和加密作为参数。
  • ASTRO_DB_APP_TOKEN:到你的 libSQL 服务器的认证令牌。远程数据库需要此令牌,而像文件或内存数据库这样的本地数据库则不需要

根据你使用的服务,你可能可以通过 CLI 或 Web UI 来获取这些值。以下部分将演示连接到 Turso 并设置这些值作为示例,但你可以自由使用任何提供商。

Turso 是 libSQL 背后的公司,libSQL 是驱动 Astro DB 的 SQLite 的开源分支。他们提供了一个完全托管的 libSQL 数据库平台,并且与 Astro 完全兼容。

以下步骤将指导你完成安装 Turso CLI、登录(或注册)、创建新数据库、获取所需的环境变量以及将模式推送到远程数据库的过程。

  1. 安装 Turso CLI

  2. 登录或注册 Turso。

  3. 创建一个新数据库。在此示例中,数据库名称为 andromeda

    终端窗口
    turso db create andromeda
  4. 运行 show 命令以查看有关新创建的数据库的信息。

    终端窗口
    turso db show andromeda

    复制 URL 值并将其设置为 ASTRO_DB_REMOTE_URL 的值。

    .env
    ASTRO_DB_REMOTE_URL=libsql://andromeda-houston.turso.io
  5. 创建一个新令牌以验证对数据库的请求。

    终端窗口
    turso db tokens create andromeda

    复制命令的输出并将其设置为 ASTRO_DB_APP_TOKEN 的值。

    .env
    ASTRO_DB_REMOTE_URL=libsql://andromeda-houston.turso.io
    ASTRO_DB_APP_TOKEN=eyJhbGciOiJF...3ahJpTkKDw
  6. 将你的数据库模式和元数据推送到新的 Turso 数据库。

    终端窗口
    astro db push --remote
  7. 恭喜,你现在已经连接好数据库了!休息一下吧。 👾

    终端窗口
    turso relax

要探索 Turso 的更多功能,请查看 Turso 文档

Astro DB 允许你连接到本地和远程数据库。默认情况下,Astro 对 devbuild 命令使用本地数据库文件,每次都会重新创建表并插入开发种子数据。

要连接到托管的远程数据库,请使用 --remote 标志。此标志启用对远程数据库的可读和可写访问,允许你在生产环境中接受和持久化用户数据

配置你的构建命令以使用 --remote 标志。

package.json
{
"scripts": {
"build": "astro build --remote"
}
}

你也可以直接在命令行中使用该标志。

终端窗口
# Build with a remote connection
astro build --remote
# Develop with a remote connection
astro dev --remote

The --remote flag uses the connection to the remote DB both locally during the build and on the server. Ensure you set the necessary environment variables in both your local development environment and your deployment platform.

部署你的 Astro DB 项目时,请确保你的部署平台的构建命令设置为 npm run build(或你的包管理器的等效命令),以利用你在 package.json 中配置的 --remote 标志。

ASTRO_DB_REMOTE_URL 环境变量配置你的数据库位置以及其他选项,如同步和加密。

libSQL 支持 HTTP 和 WebSockets 作为远程服务器的传输协议。它还支持使用本地文件或内存数据库。这些可以通过在连接 URL 中使用以下 URL 方案进行配置:

  • memory: 将使用内存数据库。在这种情况下,主机必须为空。
  • file: 将使用本地文件。主机是文件的路径 (file:path/to/file.db)。
  • libsql: 将通过库首选的协议使用远程服务器(这在不同版本中可能有所不同)。主机是服务器的地址 (libsql://your.server.io)。
  • http: 将通过 HTTP 使用远程服务器。https: 可用于启用安全连接。主机与 libsql: 相同。
  • ws: 将通过 WebSockets 使用远程服务器。wss: 可用于启用安全连接。主机与 libsql: 相同。

libSQL 连接的详细信息(例如加密密钥、复制、同步间隔)可以在远程连接 URL 中配置为查询参数。

例如,要让一个加密的本地文件作为 libSQL 服务器的嵌入式副本工作,你可以设置以下环境变量:

.env
ASTRO_DB_REMOTE_URL=file://local-copy.db?encryptionKey=your-encryption-key&syncInterval=60&syncUrl=libsql%3A%2F%2Fyour.server.io
ASTRO_DB_APP_TOKEN=token-to-your-remote-url

libSQL 原生支持加密数据库。传递此搜索参数将使用给定的密钥启用加密:

.env
ASTRO_DB_REMOTE_URL=file:path/to/file.db?encryptionKey=your-encryption-key

嵌入式副本是 libSQL 客户端的一项功能,它可以在本地文件或内存中创建数据库的完全同步副本,以实现超快读取。写入操作被发送到 syncUrl 上定义的远程数据库,并与本地副本同步。

使用此属性传递一个单独的连接 URL,将数据库转变为另一个数据库的嵌入式副本。这应该只与 file:memory: 方案一起使用。该参数必须进行 URL 编码。

例如,要拥有一个位于 libsql://your.server.io 的数据库的内存嵌入式副本,你可以这样设置连接 URL:

.env
ASTRO_DB_REMOTE_URL=memory:?syncUrl=libsql%3A%2F%2Fyour.server.io

嵌入式副本同步之间的时间间隔(以秒为单位)。默认情况下,它仅在启动时和写入后同步。

此属性仅在 syncUrl 也被设置时使用。例如,要设置一个内存嵌入式副本每分钟同步一次,请设置以下环境变量:

.env
ASTRO_DB_REMOTE_URL=memory:?syncUrl=libsql%3A%2F%2Fyour.server.io&syncInterval=60

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

import { db } from 'astro:db';

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

以下示例选择了 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 项目配置按需渲染并为你的部署环境添加一个适配器

此示例根据解析的表单 POST 请求将一行插入到 Comment 表中。

src/pages/index.astro
---
import { db, Comment } from 'astro:db';
if (Astro.request.method === 'POST') {
// Parse form data
const formData = await Astro.request.formData();
const author = formData.get('author');
const body = formData.get('body');
if (typeof author === 'string' && typeof body === 'string') {
// Insert form data into the Comment table
await db.insert(Comment).values({ author, body });
}
}
// Render the new list of comments on each request
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="body">Body</label>
<textarea id="body" name="body"></textarea>
<button type="submit">Submit</button>
</form>
<!-- Render `comments` -->

你还可以使用 Astro actions 将数据插入到 Astro DB 表中。以下示例使用一个 action 将一行插入到 Comment 表中。

src/actions/index.ts
import { db, Comment } from 'astro:db';
import { defineAction } from 'astro:actions';
import { z } from 'astro:schema';
export const server = {
addComment: defineAction({
// Actions include type safety with Zod, removing the need
// to check if typeof {value} === 'string' in your pages
input: z.object({
author: z.string(),
body: z.string(),
}),
handler: async (input) => {
const updatedComments = await db
.insert(Comment)
.values(input)
.returning(); // Return the updated comments
return updatedComments;
},
}),
};

有关完整概述,请参阅 Drizzle insert() API 参考

你还可以从 API 端点查询你的数据库。此示例通过 id 参数从 Comment 表中删除一行。

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

有关完整概述,请参阅 Drizzle delete() 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 join 从多个表中查询相关数据。要创建 join 查询,请使用 join 操作符扩展你的 db.select() 语句。每个函数都接受一个要连接的表和一个用于匹配两个表之间行的条件。

此示例使用 innerJoin() 函数,根据 authorId 列将 Comment 的作者与其相关的 Author 信息连接起来。这将返回一个对象数组,其中每个 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>
))
}

有关所有可用的 join 操作符和配置选项,请参阅 Drizzle join 参考

所有远程数据库查询都是作为网络请求进行的。当进行大量查询时,你可能需要将查询“批量”组合成单个事务,或者在任何查询失败时进行自动回滚。

此示例使用 db.batch() 方法在单个请求中填充多行数据。

db/seed.ts
import { db, Author, Comment } from 'astro:db';
export default async function () {
const queries = [];
// Seed 100 sample comments into your remote database
// with a single network request.
for (let i = 0; i < 100; i++) {
queries.push(db.insert(Comment).values({ body: `Test comment ${i}` }));
}
await db.batch(queries);
}

有关更多详细信息,请参阅 Drizzle db.batch() 文档。

你可以将在开发期间所做的更改推送到你的数据库。

随着项目的发展,你的数据表结构可能会发生变化。你可以在本地安全地测试配置更改,并在部署时推送到你的远程数据库。

你可以通过 CLI 使用 astro db push --remote 命令将你的本地模式更改推送到你的远程数据库。

终端窗口
npm run astro db push --remote

此命令将验证你的本地更改可以在不丢失数据的情况下进行,并在必要时建议如何安全地更改你的模式以解决冲突。

如果你必须以与远程数据库上现有数据不兼容的方式更改表模式,则需要重置生产数据库。

要推送包含破坏性更改的表模式更新,请添加 --force-reset 标志以重置所有生产数据。

终端窗口
npm run astro db push --remote --force-reset

在将模式推送到远程数据库后,可以重命名表。

如果你没有任何重要的生产数据,那么你可以使用 --force-reset 标志重置你的数据库。此标志将删除数据库中的所有表并创建新表,使其与你当前的模式完全匹配。

要在保留生产数据的同时重命名表,你必须执行一系列非破坏性更改,以安全地将本地模式推送到远程数据库。

以下示例将表从 Comment 重命名为 Feedback

  1. 在你的数据库配置文件中,将 deprecated: true 属性添加到你要重命名的表中。

    db/config.ts
    const Comment = defineTable({
    deprecated: true,
    columns: {
    author: column.text(),
    body: column.text(),
    }
    });
  2. 添加一个具有新名称的新表模式(与现有表的属性完全匹配)。

    db/config.ts
    const Comment = defineTable({
    deprecated: true,
    columns: {
    author: column.text(),
    body: column.text(),
    }
    });
    const Feedback = defineTable({
    columns: {
    author: column.text(),
    body: column.text(),
    }
    });
  3. 使用 astro db push --remote 推送到你的远程数据库。这将添加新表并将旧表标记为已弃用。

  4. 更新你的任何本地项目代码以使用新表而不是旧表。你可能还需要将数据迁移到新表中。

  5. 一旦你确信旧表在你的项目中不再使用,你可以从你的 config.ts 中删除该模式。

    db/config.ts
    const Comment = defineTable({
    deprecated: true,
    columns: {
    author: column.text(),
    body: column.text(),
    }
    });
    const Feedback = defineTable({
    columns: {
    author: column.text(),
    body: column.text(),
    }
    });
  6. 再次使用 astro db push --remote 推送到你的远程数据库。旧表将被删除,只留下新的、重命名后的表。

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

可以使用命令 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 参考

Astro 集成可以通过额外添加的 Astro DB 表和种子数据来扩展用户项目。

astro:db:setup 钩子中使用 extendDb() 方法来注册额外的 Astro DB 配置和种子文件。The defineDbIntegration() helper provides TypeScript support and auto-complete for the astro:db:setup hook.

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',
});
},
// Other integration hooks...
},
});
}

集成的配置文件种子文件遵循与用户定义的等效文件相同的格式。

在处理集成时,你可能无法从 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 的类型生成无法运行时也可用。你可以在任何需要查询或插入数据库的集成代码中使用它。

  1. Studio 仪表板中,导航到你希望迁移的项目。在“设置”选项卡中,使用“导出数据库”按钮下载数据库的转储文件。
  2. 按照官方说明安装 Turso CLI注册或登录你的 Turso 帐户。
  3. 使用 turso db create 命令在 Turso 上创建一个新数据库。
    终端窗口
    turso db create [database-name]
  4. 使用 Turso CLI 获取数据库 URL,并将其用作环境变量 ASTRO_DB_REMOTE_URL
    终端窗口
    turso db show [database-name]
    ASTRO_DB_REMOTE_URL=[your-database-url]
  5. 创建一个用于访问数据库的令牌,并将其用作环境变量 ASTRO_DB_APP_TOKEN
    终端窗口
    turso db tokens create [database-name]
    ASTRO_DB_APP_TOKEN=[your-app-token]
  6. 将你的数据库模式和元数据推送到新的 Turso 数据库。
    终端窗口
    astro db push --remote
  7. 将第 1 步中的数据库转储文件导入到你的新 Turso 数据库中。
    终端窗口
    turso db shell [database-name] < ./path/to/dump.sql
  8. 一旦你确认你的项目连接到新数据库,就可以安全地从 Astro Studio 中删除该项目。
贡献 社区 赞助