はじめに
Cloudflare D1 は、Cloudflare が提供する、Cloudflare Workers 用に設計されたデータベースです。
その特徴については、下記記事が大変よくまとまっています。
D1 ってなに?って方はまずこちらを読んでみることをおすすめします。
今回は、Cloudflare D1 と SvelteKit を組み合わせて、簡単にハンズオンを行ってみたいと思います。
検証環境
- WSL2 Ubuntu 22.04
- bun 1.0.0
package.json
は最終的に次のようになりました。
"devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-cloudflare-workers": "^1.1.4",
"@sveltejs/kit": "^1.20.4",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"drizzle-kit": "^0.19.13",
"drizzle-orm": "^0.28.6",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.30.0",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1",
"svelte": "^4.0.5",
"svelte-check": "^3.4.3",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.4.2",
"wrangler": "^3.9.0"
},
ORM については必須ではありませんが、今回は Drizzle ORM を利用しました。
プロジェクトの作成
今回は svelte-kit のテンプレートを利用してプロジェクトを作成していきます。
bun create svelte@latest d1-playground
cd d1-playground
bun install
bun install v1.0.0 (822a00c4)
+ @sveltejs/adapter-auto@2.1.0
+ @sveltejs/kit@1.25.0
+ @typescript-eslint/eslint-plugin@6.7.2
+ @typescript-eslint/parser@6.7.2
+ eslint@8.50.0
+ eslint-config-prettier@8.10.0
+ eslint-plugin-svelte@2.33.2
+ prettier@2.8.8
+ prettier-plugin-svelte@2.10.1
+ svelte@4.2.1
+ svelte-check@3.5.2
+ tslib@2.6.2
+ typescript@5.2.2
+ vite@4.4.9
218 packages installed [3.60s]
次に wrangler
をインストールします。
bun add -D wrangler
bun add v1.0.0 (822a00c4)
installed wrangler@3.9.0 with binaries:
- wrangler
- wrangler2
38 packages installed [2.84s]
同時に wrangler.toml
も作成します。
name = "d1-playground"
main = "./.cloudflare/worker.js"
site.bucket = "./.cloudflare/public"
build.command = "npm run build"
compatibility_date = "2023-01-01"
workers_dev = true
これで準備は完了です。
データベースの作成
D1 にデータベースを作成する場合は、wrangler d1
コマンドを利用します。
bun wrangler d1 create d1-playground
--------------------
🚧 D1 is currently in open alpha and is not recommended for production data and traffic
🚧 Please report any bugs to https://github.com/cloudflare/workers-sdk/issues/new/choose
🚧 To request features, visit https://community.cloudflare.com/c/developers/d1
🚧 To give feedback, visit https://discord.gg/cloudflaredev
--------------------
✅ Successfully created DB 'd1-playground' in region APAC
Created your database using D1's new storage backend. The new storage backend is not yet recommended for
production workloads, but backs up your data via point-in-time restore.
[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "d1-playground"
database_id = "YOUR_DATABASE_ID"
wrangler.toml
にも追記しましょう。
[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "d1-playground"
database_id = "YOUR_DATABASE_ID"
drizzle cli と drizzle-orm を追加
D1 にテーブルを作成する際に ORM は必須ではありません。
そのため、この章は読み飛ばしていただいて構いません。
今回は Drizzle ORM を利用して、簡単にデータベースを操作できるようにしていきます。
詳しくは下記記事が参考になります。
bun add drizzle-kit drizzle-orm -D
bun add v1.0.0 (822a00c4)
installed drizzle-kit@0.19.13 with binaries:
- drizzle-kit
installed drizzle-orm@0.28.6
44 packages installed [3.27s]
スキーマの定義は参考記事よりお借りしています。
/*
DO NOT RENAME THIS FILE FOR DRIZZLE-ORM TO WORK
*/
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const users = sqliteTable("users", {
id: integer("id").primaryKey().notNull(),
name: text("name").notNull(),
});
マイグレーションの実行
参考記事ではbetter-sqlite3
がマイグレーションに必要とされていますが、私の環境ではインストールせずに実行できました。
もし理由をご存じのかたがいらっしゃいましたら、ご教授いただけると幸いです。
bun drizzle-kit generate:sqlite --out migrations --schema src/schema.ts
drizzle-kit: v0.19.13
drizzle-orm: v0.28.6
1 tables
users 2 columns 0 indexes 0 fks
[✓] Your SQL migration file ➜ migrations/0000_fast_patch.sql 🚀
マイグレーションの実行
bun wrangler d1 migrations apply d1-playground --local
▲ [WARNING] Processing wrangler.toml configuration:
- D1 Bindings are currently in alpha to allow the API to evolve before general availability.
Please report any issues to https://github.com/cloudflare/workers-sdk/issues/new/choose
Note: Run this command with the environment variable NO_D1_WARNING=true to hide this message
For example: `export NO_D1_WARNING=true && wrangler <YOUR COMMAND HERE>`
--------------------
🚧 D1 is currently in open alpha and is not recommended for production data and traffic
🚧 Please report any bugs to https://github.com/cloudflare/workers-sdk/issues/new/choose
🚧 To request features, visit https://community.cloudflare.com/c/developers/d1
🚧 To give feedback, visit https://discord.gg/cloudflaredev
--------------------
Migrations to be applied:
┌─────────────────────┐
│ name │
├─────────────────────┤
│ 0000_fast_patch.sql │
└─────────────────────┘
✔ About to apply 1 migration(s)
Your database may not be available to serve requests during the migration, continue? … yes
🌀 Mapping SQL input into an array of statements
🌀 Loading YOUR_DATABASE_ID from .wrangler/state/v3/d1
┌─────────────────────┬────────┐
│ name │ status │
├─────────────────────┼────────┤
│ 0000_fast_patch.sql │ ✅ │
└─────────────────────┴────────┘
これで ローカルにテーブルが作成されました。
ここで動作確認をしたい方は、次のコマンドで任意の SQL を実行することが可能です。
wrangler d1 execute d1-playground --local --command='insert into users (name) values ("test")'
wrangler d1 execute d1-playground --local --command='select * from users'
SvelteKit の構築
まずは Cloudflare Workers 上で実行するための設定を行います。
※ wrangler を利用してローカルで動作確認する場合も必要です。
アダプターの追加と設定
bun add -D @sveltejs/adapter-cloudflare-workers
import adapter from "@sveltejs/adapter-cloudflare-workers";
export default {
kit: {
adapter: adapter(),
},
};
さて、ここからは SvelteKit の実装を行っていきます。
D1 クライアントの作成
drizzle-orm/d1
を利用して D1 クライアントを作成します。
client を取得するためには 次のようなコードを記述します。
import { drizzle } from 'drizzle-orm/d1';
export interface Env {
<BINDING_NAME>: D1Database;
}
export default {
async fetch(request: Request, env: Env) {
const db = drizzle(env.<BINDING_NAME>);
const result = await db.select().from(users).all()
return Response.json(results);
},
};
今回は SvelteKit を利用しているため、環境変数を取得する場合は、platform
プロパティを経由する必要があります。
export async function POST({ platform }) {
const DB = platform?.env?.DB;
}
また、platform
から取得する変数の型についても、定義する必要があります。
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
interface Platform {
env?: {
DB: D1Database;
};
}
}
}
export {};
D1 クライアントを作成するためには、環境変数から取得した platform?.env?.DB
を drizzle
関数の引数に渡します。
今回は createD1Client
という関数を作成して、d1client.ts
にまとめておきます。
import { drizzle } from "drizzle-orm/d1";
export const createD1Client = (d1: D1Database | undefined) => {
if (!d1) {
throw new Error(`Database not found`);
}
return drizzle(d1);
};
では、実際にデータ取得、データ更新を実装してみましょう。
データ取得
データ取得は下記のように実装できます。
import { createD1Client } from "$lib/server/d1client";
import { users } from "../schema";
const d1Client = createD1Client(platform?.env?.DB);
const result = await d1Client.select().from(users).all();
データ更新
データ更新は下記のように実装できます。
import { createD1Client } from "$lib/server/d1client";
import { users } from "../schema";
const d1Client = createD1Client(platform?.env?.DB);
const result = await d1Client.insert(users).values({ name: "John" }).execute();
メソッドチェーンを利用してクエリを記述できる点は、 ORM を利用する場合のメリットですね。
全体の実装
最後に実装全体をまとめておきます。
初期ロード時にデータを取得し、フォームからデータを更新することができるようになりました。
注意としては、bun dev
ではなく wrangler dev
を実行する必要があります。
お好みでScript
を編集するとよいでしょう。
+page.svelte
<script lang="ts">
import type { PageData, ActionData } from "./$types";
export let data: PageData;
export let form: ActionData;
</script>
<h1>Regist User</h1>
{#if form && !form.success}
<p>{form.error}</p>
{/if}
<form method="POST">
<label>Name<input name="name" type="text" /></label>
<button type="submit">Submit</button>
</form>
{#each data.users as user}
<p>{user.id}:{user.name}</p>
{/each}
+page.server.ts
import type { PageServerLoad, Actions } from "./$types";
import { createD1Client } from "$lib/server/d1client";
import { users } from "../schema";
export const load = (async ({ platform }) => {
const d1Client = createD1Client(platform?.env?.DB);
const result = await d1Client.select().from(users).all();
return { users: result };
}) satisfies PageServerLoad;
export const actions = {
default: async ({ platform, request }) => {
const data = await request.formData();
const name = data.get("name");
if (!name) {
return { success: false, error: "Name is required" };
}
const d1Client = createD1Client(platform?.env?.DB);
await d1Client.insert(users).values({ name: name.toString() }).execute();
return { success: true };
},
} satisfies Actions;
d1client.ts
import { drizzle } from "drizzle-orm/d1";
export const createD1Client = (d1: D1Database | undefined) => {
if (!d1) {
throw new Error(`Database not found`);
}
return drizzle(d1);
};
まとめ
今回は Cloudflare D1 と SvelteKit を組み合わせて、簡単な CRUD を実装してみました。
D1 はまだアルファ版ということもあり、プロダクト運用には向いていないかもしれませんが、趣味であれば関係ありません。
是非皆さんも試してみてはいかがでしょうか!