🔥
Bun+HonoX+D1+Prisma を動かしてみた
個人開発をやりたいなと思い面白そうな技術スタックをかき集めて動かしてみました
honoxプロジェクト作成
bunx create-hono@latest
- Target directory は
bun-honox-d1-prisma
- templateは
x-basic
を選択 - package managerに
bun
を選択
D1にDatabaseを作成
bunx wrangler d1 create blog
D1への接続確認
bunx wrangler d1 execute blog --command "SELECT name FROM sqlite_schema WHERE type ='table'"
wrangler.tomlファイルを作成
プロジェクト直下にwrangler.tomlファイルを作成してd1 createコマンド実行後の接続情報を張り付けておく
また、Cloudflare PagesにDeployする場合、wrangler.tomlにpages_build_output_dir
が必要なので追加しておく
wrangler.toml
name = "bun-honox-d1-prisma"
pages_build_output_dir = "./dist"
[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "blog"
database_id = "xxxxxxx"
Prismaの設定
インストール
bun add -d prisma
bun add @prisma/client @prisma/adapter-d1
初期化
bunx prisma init --datasource-provider sqlite
.gitignoreに.env
を入れろと言われるので入れておく
schema.prismaの設定とスキーマ定義
D1サポートを使うのにpreviewFeatures = ["driverAdapters"]
を追加する。
また、テーブルのスキーマ定義も追加する。
prisma/schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Post {
id String @id @default(cuid())
title String
content String
}
マイグレーションファイル作成
bunx wrangler d1 migrations create blog create_post_table
migrate実行
bunx prisma migrate diff --from-empty --to-schema-datamodel ./prisma/schema.prisma --script --output migrations/0001_create_post_table.sql
localに対してTable作成
bunx wrangler d1 migrations apply blog --local
remoteに対してTable作成
bunx wrangler d1 migrations apply blog --remote
Prisma Client作成
bunx prisma generate
vite.config.tsの変更
Viteの設定でpluginsの設定を追加する。
また、ssr.external
に @prisma/client
と@prisma/adapter-d1
を追加する必要がある。
vite.config.ts
import pages from '@hono/vite-cloudflare-pages'
import honox from 'honox/vite'
import adapter from '@hono/vite-dev-server/cloudflare'
import client from 'honox/vite/client'
import { defineConfig } from 'vite'
const baseConfig = {
ssr: {
external: [
'@prisma/client',
'@prisma/adapter-d1',
]
},
}
export default defineConfig(({ mode }) => {
if (mode === 'client') {
return {
...baseConfig,
plugins: [client()]
}
} else {
return {
...baseConfig,
plugins: [
honox({
devServer: { adapter }
}),
pages()
],
}
}
})
global.d.tsにD1DatabaseのBindings追加
app/global.d.ts
declare module 'hono' {
interface Env {
Variables: {}
Bindings: {
DB: D1Database
}
}
interface ContextRenderer {
(content: string | Promise<string>, head?: Head): Response | Promise<Response>
}
}
必要最小限のコードを追加する
routes/posts配下に追加
一覧画面
routes/posts/index.tsx
import { createRoute } from "honox/factory";
import { PrismaClient } from "@prisma/client";
import { PrismaD1 } from "@prisma/adapter-d1";
export default createRoute(async (c) => {
const adapter = new PrismaD1(c.env.DB);
const prisma = new PrismaClient({ adapter });
const posts = await prisma.post.findMany();
return c.render(
<div>
<h1>Posts</h1>
<ul>
{posts.map((post) => (
<li>
<a href={`/posts/${post.id}`}>{post.title}</a>
</li>
))}
</ul>
<a href={"/posts/create"}>create new post</a>
</div>
);
});
作成画面
routes/posts/create.tsx
import { createRoute } from "honox/factory";
import { PrismaClient } from "@prisma/client";
import { PrismaD1 } from "@prisma/adapter-d1";
export default createRoute((c) => {
return c.render(
<div>
<h1>Create Post</h1>
<form method="post">
<label>
Title
<input name="title" type="text" />
</label>
<label>
Content
<textarea name="content" />
</label>
<button type="submit">Create</button>
</form>
</div>,
{
title: "Create Post",
}
);
});
export const POST = createRoute(async (c) => {
const adapter = new PrismaD1(c.env.DB);
const prisma = new PrismaClient({ adapter });
const body = await c.req.formData();
const user = await prisma.post.create({
data: {
title: body.get("title")?.toString() ?? "",
content: body.get("content")?.toString() ?? ""
},
});
return c.redirect("/posts");
});
詳細画面
routes/posts/[id].tsx
import { createRoute } from "honox/factory";
import { PrismaClient } from "@prisma/client";
import { PrismaD1 } from "@prisma/adapter-d1";
export default createRoute(async (c) => {
const adapter = new PrismaD1(c.env.DB);
const prisma = new PrismaClient({ adapter });
const { id } = c.req.param();
const post = await prisma.post.findUnique({
where: {
id: id,
},
});
return c.render(
<div>
<h1>{post?.title}</h1>
<p>{post?.content}</p>
<a href={"/posts"}>posts</a>
</div>
);
});
動かしてみる
bun run dev
デプロイしてみる
bun run deploy
GitHub
参考情報
ssr.externalの設定でハマっていた時に見つけて助かりました。
次はsqlcも試してみたいな
Discussion