コンテンツにスキップ

Astro DB

Astro DBは、Astro専用に設計されたフルマネージドSQLデータベースです。ローカルでの開発も、Astro Studioプラットフォームで管理されているホストされたデータベースへの接続も可能です。

v0.8.1以降の)@astrojs/dbインテグレーションを使用して、新しいAstroプロジェクトまたは既存のプロジェクト(astro@4.5以降が必要です)にAstro DBを追加します。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テーブルを使用して保存されます。テーブルはデータを行と列に構造化し、列は各行の値の型を強制します。

テーブルを定義すると、プロジェクトからそのテーブルにクエリするためのTypeScriptのインターフェースが、Astroによって生成されます。これにより、データにアクセスする際に、プロパティの自動補完や型チェックなどの完全なTypeScriptのサポートが得られます。

データベーステーブルを設定するには、astro:dbからdefineTable()columnユーティリティをインポートして使用します。

以下の例では、必須のテキストカラムauthorbodyをもつCommentテーブルを設定しています。そして、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: {
// テキストの文字列。
author: column.text(),
// 整数値。
likes: column.number(),
// 真偽値。
flagged: column.boolean(),
// JavaScriptのDateオブジェクトとしてクエリされる、日付/時刻の値。
published: column.date(),
// 型のないJSONオブジェクト。
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 }),
content: column.text(),
}
});

開発中、AstroはDBの設定を使用して、スキーマに従ってローカルに型を生成します。これらの型は開発サーバーが起動するたびに新たに生成されるため、型安全性と自動補完のもとで、データの形状をクエリしたり操作したりできます。

テストやデバッグのために開発データをAstroプロジェクトにシードするには、db/seed.tsファイルを作成します。astro:dbからdbオブジェクトと設定済みのテーブルをインポートし、db.insert()関数を使用してテーブルの行データオブジェクトの配列を与えます。

以下の例では、Commentテーブルに2行の開発用データを定義しています。

db/seed.ts
import { db, Comment } from 'astro:db';
export default async function() {
await db.insert(Comment).values([
{ authorId: 1, body: 'Astro DBを気に入ってもらえると嬉しいです!' },
{ authorId: 2, body: '楽しんでください!'},
])
}

このファイルが変更されるたびに、開発サーバーは自動的にデータベースを再起動し、型を再生成してseed.tsから開発データをシードします。

プロジェクト内の任意のAstroページエンドポイントから、提供されるdb ORMとクエリビルダーを使用してデータベースへのクエリができます。

import { db } from 'astro:db';

Astro DBには、組み込みのDrizzle ORMクライアントが含まれています。このクライアントを使用するためのセットアップや手動の設定は必要ありません。Astro DBのdbクライアントは、Astroを実行すると(ローカルまたはリモートの)データベースと通信するように自動的に設定されます。また、データベーススキーマの定義を正確に使用して型安全なSQLクエリが可能になるため、存在しないカラムやテーブルを参照するとTypeScriptエラーが発生します。

以下の例では、Commentテーブルのすべての行を選択しています。これにより、db/seed.tsからシードされた開発データの配列全体が返され、ページのテンプレートで使用できるようになります。

src/pages/index.astro
---
import { db, Comment } from 'astro:db';
const comments = await db.select().from(Comment);
---
<h2>コメント</h2>
{
comments.map(({ author, body }) => (
<article>
<p>著者: {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">著者</label>
<input id="author" name="author" />
<label for="content">コンテンツ</label>
<textarea id="content" name="content"></textarea>
<button type="submit">送信</button>
</form>
<!--`comments`をレンダリング-->

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 insert()のAPIリファレンスを参照してください。

特定のプロパティによってテーブルの結果をクエリするには、部分選択(partial select)のためのDrizzleオプションを使用します。たとえば、select()クエリに.where()呼び出しを追加し、必要な比較条件を渡します。

以下の例では、「Astro DB」というフレーズを含むCommentテーブルのすべての行をクエリしています。body内にフレーズが存在するかどうかをチェックするためには、like()オペレータを使用します。

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()文を結合演算子で拡張します。各関数は、結合するテーブルと、2つのテーブル間の行を一致させる条件を受け付けます。

この例では、innerJoin()関数を使用して、Commentの著者をauthorIdカラムに基づいて関連する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>コメント</h2>
{
comments.map(({ Author, Comment }) => (
<article>
<p>著者: {Author.name}</p>
<p>{Comment.body}</p>
</article>
))
}
利用可能なすべての結合演算子と設定オプションについては、Drizzleの結合リファレンスを参照してください。

リモートデータベースへのクエリはすべてネットワークリクエストとして実行されます。大量のクエリを実行する場合や、クエリが失敗した場合に自動的にロールバックするために、複数のクエリを1つのトランザクションへとまとめる必要がある場合があります。

この例では、db.batch()メソッドを使用して、1つのリクエストで複数の行をシードしています。

db/seed.ts
import { db, Author, Comment } from 'astro:db';
export default async function () {
const queries = [];
// 100件のサンプルコメントを1回のネットワークリクエストで
// リモートデータベースにシードします。
for (let i = 0; i < 100; i++) {
queries.push(db.insert(Comment).values({ body: `テストコメント ${i}` }));
}
await db.batch(queries);
}
詳細については、Drizzle db.batch()のドキュメントを参照してください。
Studioの機能

Astro DBは、Astro Studioプラットフォームに接続して、ホストされたデータベースをプロジェクトに迅速に追加できます。Astro Studioのダッシュボードから、新しくホストされたデータベースを表示、管理、デプロイできます。

新しいプロジェクトを作成するには、既成のテンプレートを使用するか、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データベースに対して実行します。

以下のCommentsは、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: 'Astro DBを気に入ってもらえると嬉しいです!' },
{ authorId: 2, body: '楽しんでください!' },
])
}
コマンドの完全なリストについては、CLIリファレンスを参照してください。
Studioの機能

デフォルトでは、devまたはbuildコマンドを実行すると、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の型生成が実行できないような場合でも利用できます。データベースへのクエリや挿入が必要なインテグレーションコードで使用できます。