Open3

Cloudflareとか

Mt.SouthernMt.Southern

Honoを試す、で炎上するw

hono環境を作る。bunでやってみる。
前提としては、bun入りdevcontainer環境を事前に作成しているのでそちらから実行。

ターミナルコマンド
$ bun create hono
create-hono version 0.13.0
? Target directory hono-example
? Which template do you want to use? cloudflare-workers
? Do you want to install project dependencies? yes
? Which package manager do you want to use? bun
✔ Cloning the template
✔ Installing project dependencies
🎉 Copied project files
Get started with: cd hono-example

んで、動かしてみる。

ターミナル
$ cd hono-example
$ bun run dev

$ wrangler dev src/index.ts

 ⛅️ wrangler 3.75.0
-------------------

? Would you like to help improve Wrangler by sending usage metrics to Cloudflare? › (Y/n)╭─────────────────────────────────
⎔ Starting local server...
[wrangler:inf] Ready on http://localhost:8787
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ [b] open a browser, [d] open Devtools, [l] turn off local mode, [c] clear console, [x] to exit                          │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

こうなるんでブラウザから http://localhost:8787 へアクセスするもうまくいかず。。。

困ったらchat GPT大先生にヘルプ。

そしたら、wrangler.tomlファイルにdevの設定をしたらええそうで提案して貰った内容の

wrangler.toml
# ローカル開発時の設定
[dev]
port = 8787                 # ローカルで使用するポート
ip = "127.0.0.1"            # 開発用のIPアドレス (通常は127.0.0.1)

設定して保存したら、ターミナル側で勝手にリロードしてくれて表示が変わった。

ターミナル
wrangler.toml changed...
⎔ Reloading local server...
⎔ Reloading local server...
[wrangler:inf] Updated and ready on http://127.0.0.1:8787
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ [b] open a browser, [d] open Devtools, [l] turn off local mode, [c] clear console, [x] to exit                          │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

ほな、もっかい!ということでブラウザで再度、 http://localhost:8787 へアクセスしたら。。。

あかんやん!

Chat GPT大先生の嘘つき~。
ちゃんとdevcontainer環境で、ってプロンプトにも書いたのにぃ。

うーん、なんでや?
ちょっと悩んだ末、127.0.0.1 やとコンテナのローカル環境しかアクセスでけへん事に気づく。

ああ、そしたら全部受けたらええやん。

と言うことで設定を変更

wrangler.toml
# ローカル開発時の設定
[dev]
port = 8787                 # ローカルで使用するポート
ip = "0.0.0.0"            # 開発用のIPアドレス (コンテナへのアクセスは全部通すので0.0.0.0)

としたらまたまたターミナルに変化があった。

ターミナル
wrangler.toml changed...
⎔ Reloading local server...
⎔ Reloading local server...
[wrangler:inf] Updated and ready on http://0.0.0.0:8787

再々度、ブラウザから http://localhost:8787 にアクセスしてみると今度は
Hello Hono!
と表示された。

めでたしめでたし。

Mt.SouthernMt.Southern

Cloudflare D1 devcontainer環境で試し、燃え上がるw

CloudflareにD1と言う素敵なsqlite環境があるという事で試してみたら、炎上したw

環境としてはdevcontainerにbun実行環境(nodejs)なんやけど、チュートリアルとかそのまんま動かしてもうまくいかへんかった。
前提としてはHonoの時と一緒なので、

wrangler.toml
# ローカル開発時の設定
[dev]# ローカル開発時の設定
[dev]
port = 8787                 # ローカルで使用するポート
ip = "0.0.0.0"            # 開発用のIPアドレス (コンテナへのアクセスは全部通すので0.0.0.0)

も前提として入れとくこと。

ターミナル
$ bunx wrangler d1 create sample-db
 ⛅️ wrangler 3.78.2
-------------------

Attempting to login via OAuth...
Opening a link in your default browser: https://dash.cloudflare.com/oauth2/auth?response_type=code&client_id=...中略...
▲ [WARNING] Failed to open[ERROR] Timed out waiting for authorization code, please try again.



✘ [ERROR] Did not login, quitting...


🪵  Logs were written to "/home/app/.config/.wrangler/logs/wrangler-2024-09-14_01-02-56_935.log"
error: "wrangler" exited with code 1

ってなるんで、リンクをコピーしてブラウザに貼り付けてログインして。。。ってやってもうまくいかへんし。。。
原因としては、OAuthの流れで行くと、リンク先はブラウザで開いて許可出したらコールバックでローカルのサーバで受けて、ってやるけど、devcontainer環境なんで受けられへん訳やね。。。

しゃーないから、再びChatGPT大先生にお出まし頂く。

ご宣託としては、APIトークンであんじょうやったったらええねんで、とのお達し。

つーことで、cloudflareのコンパネにGo!

  • 右上のヒト型アイコンからマイプロフィールを選ぶ。
  • 左のメニューからAPIトークンを選び、 トークンを作成するボタンでクリック。
  • 権限のテンプレートがあるので Cloudflare Workersを編集するテンプレートを使用する ボタンをクリック。
  • 次の画面でアクセス許可にD1がないので +更に追加する として アカウントD1 編集をドロップダウンから選ぶ
  • アカウントリソースで 含む 自分のアカウント名 をドロップダウンから選択
  • ゾーンリソースで 含む アカウントにある全てのゾーン 自分のアカウント名 をドロップダウンから選択
  • 概要に進むをクリックすると、確認画面へ遷移する

すると

確認画面
Cloudflare Workers を編集する API トークンの概要
この API トークンは、それぞれの権限とともに、以下のアカウントとゾーンに影響します


アカウント名's Account - Workers KV Storage:編集, Workers スクリプト:編集, アカウント設定:読み取り, Workers Tail:読み取り, Workers R2 Storage:編集, Cloudflare Pages:編集, D1:編集
すべてのゾーン - Workers ルート:編集
すべてのユーザー - ユーザーの詳細:読み取り, メンバーシップ:読み取り

と設定した内容の最終確認があるのでトークンを作成する をクリックするとトークンが作成される。

なお、トークン作成時の

  • クライアント IP アドレスフィルタリング
  • TTL

については本来設定すべきやけど、今回はテストなのでパス。

で、次の画面では作成されたAPIトークンが表示されるのできちんとコピーしてどっかに保存しとく。再度表示はでけへんからきちんとどっかにメモっとけ!って感じやなぁ。

で、次はそのAPIキーをdevcontainer環境上に環境変数として反映させる。

ターミナル
$ export CLOUDFLARE_API_TOKEN=[取得したAPIトークン文字列]

でも、このやり方、シェル起動毎に書くのもいややし、.bashrcとかに書くっていうのもプロジェクト異なる毎に書き換えになるんでいややなぁ。

てことで、プロジェクトフォルダ内の .envに書くことで解決することにした。

.env
CLOUDFLARE_API_TOKEN = "[取得したAPIトークン文字列]"

としてから、先ほど失敗したd1データベース作成のコマンドを再実行。

$ bun run wrangler d1 create sample-db
 ⛅️ wrangler 3.78.2
-------------------

✅ Successfully created DB 'sample-db' in region APAC
Created your new D1 database.

[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "sample-db"
database_id = [ここに作成したデータベースのIDが表示される]

となるので、最後に表示されている内容をwrangler.tomlに追記。

wrangler.toml
[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "sample-db"
database_id = [ここに作成したデータベースのIDが表示される]

確認

ターミナル
$ bun run wrangler d1 list

 ⛅️ wrangler 3.78.2
-------------------

┌──────────────────────────────────────┬─────────┬────────────┬──────────────────────────┬───────────┬────────────┐
│ uuid                                 │ name    │ version    │ created_at               │ file_size │ num_tables │
├──────────────────────────────────────┼─────────┼────────────┼──────────────────────────┼───────────┼────────────┤
│ XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX │sample-db│ production │ 2024-09-15T05:03:10.024Z │           │            │
└──────────────────────────────────────┴─────────┴────────────┴──────────────────────────┴───────────┴────────────┘

よしよし、データベースで来てる。

これでテーブルCREATEのSQLを実行したらいけるはず。

と言うことで、sqlファイル

create_table_todos.sql
CREATE TABLE IF NOT EXISTS users (
    user_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    user_name TEXT NOT NULL,
    user_email TEXT NOT NULL UNIQUE,
    user_phone_number TEXT NOT NULL,
    user_address TEXT,
    user_password TEXT NOT NULL,
    created_at  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO users (user_name, user_email, user_phone_number, user_address, user_password)
VALUES
    ('Alice Cooper', 'alice@example.com', '03-1111-2222', 'Chuo-ku, Tokyo, Japan', 'abcdefg'),
    ('Bob Marley', 'bob@example.com', '03-2222-2222', 'Minato-ku, Tokyo, Japan', 'abcdefg'),
    ('Charlie Sheen', 'charlie@example.com', '03-3333-3333', 'Toshima-ku, Tokyo, Japan', 'abcdefg');

で、実行。

ターミナル
$ bun run wrangler d1 execute sample-db --local --file=sql/create_table.sql 

 ⛅️ wrangler 3.78.2
-------------------

🌀 Executing on local database todo-db ([作成したデータベースのID]) from .wrangler/state/v3/d1:
🌀 To execute on your remote database, add a --remote flag to your wrangler command.

つづいて、

ターミナル
$ bun run wrangler d1 execute sample-db --local --command='SELECT * FROM users;'

 ⛅️ wrangler 3.78.2
-------------------

🌀 Executing on local database todo-db (c359313a-4323-449a-98db-c4e931426c1b) from .wrangler/state/v3/d1:
🌀 To execute on your remote database, add a --remote flag to your wrangler command.
┌─────────┬───────────────┬─────────────────────┬───────────────────┬──────────────────────────┬───────────────┬─────────────────────┬─────────────────────┐
│ user_id │ user_name     │ user_email          │ user_phone_number │ user_address             │ user_password │ created_at          │ updated_at          │
├─────────┼───────────────┼─────────────────────┼───────────────────┼──────────────────────────┼───────────────┼─────────────────────┼─────────────────────┤
│ 1       │ Alice Cooper  │ alice@example.com   │ 03-1111-2222      │ Chuo-ku, Tokyo, Japan    │ abcdefg       │ 2024-09-15 16:48:14 │ 2024-09-15 16:48:14 │
├─────────┼───────────────┼─────────────────────┼───────────────────┼──────────────────────────┼───────────────┼─────────────────────┼─────────────────────┤
│ 2       │ Bob Marley    │ bob@example.com     │ 03-2222-2222      │ Minato-ku, Tokyo, Japan  │ abcdefg       │ 2024-09-15 16:48:14 │ 2024-09-15 16:48:14 │
├─────────┼───────────────┼─────────────────────┼───────────────────┼──────────────────────────┼───────────────┼─────────────────────┼─────────────────────┤
│ 3       │ Charlie Sheen │ charlie@example.com │ 03-3333-3333      │ Toshima-ku, Tokyo, Japan │ abcdefg       │ 2024-09-15 16:48:14 │ 2024-09-15 16:48:14 │
└─────────┴───────────────┴─────────────────────┴───────────────────┴──────────────────────────┴───────────────┴─────────────────────┴─────────────────────┘

よっしゃ!いけた!

Mt.SouthernMt.Southern

prismaとclouflare d1 を連携してみる

基本的には公式のGet Startedとかをなぞってみるんやけど、既存テーブルとかあるのでその辺考えながら進めることにする。
あとは、cloudflare d1 ローカル(あとでリモート)環境なので
https://www.prisma.io/docs/orm/overview/databases/cloudflare-d1
も確認。

Prisma ORM support for Cloudflare D1 is currently in Preview

おおぅ、現在プレビューってことか。大きく変わるかも知らんけど、とりあえずここを元にして進めることにする。

ちょっとprismaってなんやろな

  • nodeJS/Typescript ORM
  • 主要コンポーネント
    • prisma Client
    • prisma Migrate
    • prisma Studio
  • 基本的な流れ
    1. 環境構築(初回のみ)
    2. prismaの初期化(初回のみ)
    3. スキーマの定義
    4. マイグレーションの作成と実行
    5. prisma Clientの生成
  • 以降はコード内で使用

基本この流れやけど、公式に書いてあるd1の注意点をまとめてみる。

cloudflare d1とprisma使う時の注意点

  • 通常のdbの場合はprisma migrateとかつかって全部prismaで完結するが、d1の自前マイグレーション機能があるのでそちらを使う
    • wrangler d1 migrateions コマンド
  • sqliteデータベースプロバイダとともに @prisma/adpter-d1 ドライバアダプタも必要

環境構築

で、基本的にbun環境なのでTypescript関連の手順は飛ばして、prismaインストールあたりから始める。

ターミナル
$ bun add -d prisma
[0.07ms] ".env"
bun add v1.1.27 (267afa29)

installed prisma@5.19.1 with binaries:
 - prisma

6 packages installed [470.00ms]

$ bun add @prisma/client @prisma/adapter-d1
[0.07ms] ".env"
bun add v1.1.27 (267afa29)

installed @prisma/client@5.19.1
installed @prisma/adapter-d1@5.19.1

4 packages installed [1.65s]

prisma本体のインストールとクライアント、d1アダプタをインストールする。prisama本体はdevDependenciesでインストール。

prismaの初期設定

次にデータソースのプロバイダを sqlite としてinitする。

ターミナル
$ bun run prisma init --datasource-provider sqlite

✔ Your Prisma schema was created at prisma/schema.prisma
  You can now open it in your favorite editor.

warn You already have a .gitignore file. Don't forget to add `.env` in it to not commit any private information.

Next steps:
1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started
2. Run prisma db pull to turn your database schema into a Prisma schema.
3. Run prisma generate to generate the Prisma Client. You can then start querying your database.
4. Tip: Explore how you can extend the ORM with scalable connection pooling, global caching, and real-time database events. Read: https://pris.ly/cli/beyond-orm

More information in our documentation:
https://pris.ly/d/getting-started

プロジェクトディレクトリに prisamaが作成されて、prisma/schema.prisma ファイルが生成される。

中身を確認すると

schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

となってた。

d1ドライバアダプタを利用するため修正が必要

schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
+  previewFeatures = ["driverAdapters"]
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

スキーマの定義

次はマイグレーション用のディレクトリとファイルを生成する。

ターミナル
$ bun run wrangler d1 migrations create example-db drop_user_table

 ⛅️ wrangler 3.78.2
-------------------

✔ No migrations folder found. Set `migrations_dir` in wrangler.toml to choose a different path.
Ok to create /app/example-todo-api/migrations? … yes
✅ Successfully created Migration '0001_drop_user_table.sql'!

The migration is available for editing here
/app/example-todo-api/migrations/0001_drop_user_table.sql

コメントだけの空のsqlファイルができる

migrations/0001_drop_user_table.sql
-- Migration number: 0001 	 2024-09-15T17:03:39.563Z

さて、前回作ったテーブルは一旦消したい。
まだモデルを作っていないので schema.prismaには存在しない。

この状態で prisma migrate difff --from-local-d1 ... とするとローカルDBにはテーブルが存在し、モデルが存在しないのでその差分でテーブルドロップとなる。

ターミナル
$ bun prisma migrate diff --from-local-d1 --to-schema-datamodel ./prisma/schema.prisma --script > migrations/0001_drop_user_table.sql

としてテーブルを削除するSQLを migrations/0001_drop_user_table.sqlに再生成。

migrations/0001_create_user_table.sql
-- DropTable
PRAGMA foreign_keys=off;
DROP TABLE "users";
PRAGMA foreign_keys=on;

できあがったマイグレーションsqlを実行

ターミナル
$ bun run wrangler d1 migrations apply example-db --local

 ⛅️ wrangler 3.78.2
-------------------

Migrations to be applied:
┌──────────────────────────┐
│ name                     │
├──────────────────────────┤
│ 0001_drop_user_table.sql │
└──────────────────────────┘
✔ About to apply 1 migration(s)
Your database may not be available to serve requests during the migration, continue? … yes
🌀 Executing on local database example-db ([生成されたデータベースID]) from .wrangler/state/v3/d1:
🌀 To execute on your remote database, add a --remote flag to your wrangler command.
┌──────────────────────────┬────────┐
│ name                     │ status │
├──────────────────────────┼────────┤
│ 0001_drop_user_table.sql │ ✅     │
└──────────────────────────┴────────┘

その後、 prisma/schema.prismaにモデルを追記。

schema.prisma
model User {
  userId Int @id @default(autoincrement()) @map("user_id")
  userName String @map("user_name")
  userEmail String @unique @map("user_email")
  userPhoneNumber String @unique @map("user_phone_number")
  userAddress String? @map("user_address")
  userPassword String @map("user_password")
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @map("updated_at")
  @@map("users")
}

テーブル名/カラム名をスネークケースにしたいのでモデル名/フィールド名に@@map/@mapで関連付けしておく。
また、emailとphoneNumberはユニーク制約を付けるため@uniqueをフィールドに付与。

マイグレーションの作成と実行

このモデルを元にマイグレーションファイルを生成する。

ターミナル
$ bun prisma migrate diff --from-local-d1 --to-schema-datamodel ./prisma/schema.prisma --script > migrations/0002_create_user_table.sql

できあがったマイグレーションファイル

migrations/0002_creat_user_table.sql
-- CreateTable
CREATE TABLE "users" (
    "user_id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "user_name" TEXT NOT NULL,
    "user_email" TEXT NOT NULL,
    "user_phone_number" TEXT NOT NULL,
    "user_address" TEXT,
    "user_password" TEXT NOT NULL,
    "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- CreateIndex
CREATE UNIQUE INDEX "users_user_email_key" ON "users"("user_email");

-- CreateIndex
CREATE UNIQUE INDEX "users_user_phone_number_key" ON "users"("user_phone_number");

よしよし、きちんと想定通りできてるやん!

で、これをapplyするので再度実行

ターミナル
$ bun run wrangler d1 migrations apply example-db --local
 ⛅️ wrangler 3.78.2
-------------------

Migrations to be applied:
┌────────────────────────────┐
│ name                       │
├────────────────────────────┤
│ 0002_create_user_table.sql │
└────────────────────────────┘
✔ About to apply 1 migration(s)
Your database may not be available to serve requests during the migration, continue? … yes
🌀 Executing on local database example-db ([生成したデータベースID]) from .wrangler/state/v3/d1:
🌀 To execute on your remote database, add a --remote flag to your wrangler command.
┌────────────────────────────┬────────┐
│ name                       │ status │
├────────────────────────────┼────────┤
│ 0002_create_user_table.sql │ ✅       │
└────────────────────────────┴────────┘

後はINSERT文を作成してこれを実行

sql/insert_user.sql
INSERT INTO users (user_name, user_email, user_phone_number, user_address, user_password)
VALUES
    ('Alice Cooper', 'alice@example.com', '03-1111-2222', 'Chuo-ku, Tokyo, Japan', 'abcdefg'),
    ('Bob Marley', 'bob@example.com', '03-2222-2222', 'Minato-ku, Tokyo, Japan', 'abcdefg'),
    ('Charlie Sheen', 'charlie@example.com', '03-3333-3333', 'Toshima-ku, Tokyo, Japan', 'abcdefg');
ターミナル
    $ bun run wrangler d1 execute sample-db --local --file=sql/insert_user.sql 
 ⛅️ wrangler 3.78.2
-------------------

🌀 Executing on local database example-db ([生成したデータベースID]) from .wrangler/state/v3/d1:
🌀 To execute on your remote database, add a --remote flag to your wrangler command.
ターミナル
$ bun run wrangler d1 execute example-db --local --command="SELECT * FROM users;"

 ⛅️ wrangler 3.78.2
-------------------

🌀 Executing on local database example-db ([生成したデータベースID]) from .wrangler/state/v3/d1:
🌀 To execute on your remote database, add a --remote flag to your wrangler command.
┌─────────┬───────────────┬─────────────────────┬───────────────────┬──────────────────────────┬───────────────┬─────────────────────┬─────────────────────┐
│ user_id │ user_name     │ user_email          │ user_phone_number │ user_address             │ user_password │ created_at          │ updated_at          │
├─────────┼───────────────┼─────────────────────┼───────────────────┼──────────────────────────┼───────────────┼─────────────────────┼─────────────────────┤
│ 1       │ Alice Cooper  │ alice@example.com   │ 03-1111-2222      │ Chuo-ku, Tokyo, Japan    │ abcdefg       │ 2024-09-15 17:51:01 │ 2024-09-15 17:51:01 │
├─────────┼───────────────┼─────────────────────┼───────────────────┼──────────────────────────┼───────────────┼─────────────────────┼─────────────────────┤
│ 2       │ Bob Marley    │ bob@example.com     │ 03-2222-2222      │ Minato-ku, Tokyo, Japan  │ abcdefg       │ 2024-09-15 17:51:01 │ 2024-09-15 17:51:01 │
├─────────┼───────────────┼─────────────────────┼───────────────────┼──────────────────────────┼───────────────┼─────────────────────┼─────────────────────┤
│ 3       │ Charlie Sheen │ charlie@example.com │ 03-3333-3333      │ Toshima-ku, Tokyo, Japan │ abcdefg       │ 2024-09-15 17:51:01 │ 2024-09-15 17:51:01 │
└─────────┴───────────────┴─────────────────────┴───────────────────┴──────────────────────────┴───────────────┴─────────────────────┴─────────────────────┘