Closed28

簡単なTodoリストAPIのためにD1とDrizzleに触れる

hajimismhajimism

HonoとCloudfrare Workersで簡単なTodoリストAPIを作っていて、いまはローカルのモックデータを使ってAPIのガワだけできたところ。

ローカルのデータを何かしらに永続化したくて、「KVとD1のどっちかかなあ」ってなって(よく知らない)、TodoだけじゃなくてUserも登場させたい・プロパティごとに書き込みをやりたい、あたりの理由でKVじゃきつそうってなって、D1使うならDrizzleがいいらしいってmizchiさんの記事で読んだなあってなって、とりあえずスクラップを開いた。

hajimismhajimism

D1に公式対応しているらしいのでDrizzle側から攻める
https://orm.drizzle.team/docs/overview

hajimismhajimism

Drizzle ORM is a headless TypeScript ORM with a head.

どういうこと?

hajimismhajimism

Drizzle lets you define and manage database schema in typescript, lets you access your data in SQL-like or relational way, has a massive variety of opt-in tools which makes Drizzle an ultimate tool for managing and accessing your SQL data.

"SQL-like or relational way"この2つで何が対比されているのかもよくわかってない

hajimismhajimism

あ、すぐ下に書いてあった。これがSQL-likeということらしい。

// Access your data
await db
	.select()
	.from(countries)
	.leftJoin(cities, eq(cities.countryId, countries.id))
	.where(eq(countries.id, 10))
hajimismhajimism

あー、で、SQLっぽくない抽象化を施したやつがrelational wayってことなのかな?

const result = await db.query.users.findMany({
	with: {
		posts: true			
	},
});
hajimismhajimism

あんまりよくわかってないけどとりあえずDrizzle for D1のページ
https://orm.drizzle.team/docs/quick-sqlite/d1

hajimismhajimism

手元にWorkersプロジェクト自体はすでにあるので、wrangler d1 create todo-listから

hajimismhajimism

これでいいのかなあ

import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";

export const users = sqliteTable("users", {
  userId: integer("userId").primaryKey().notNull(),
  name: text("name").notNull(),
  iconUrl: text("iconUrl"),
  createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
});

export const todos = sqliteTable("todos", {
  id: integer("id").primaryKey().notNull(),
  title: text("title").notNull(),
  status: text("status", { enum: ["todo", "doing", "done"] }).notNull(),
  assigneeId: integer("assigneeId"),
  createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
});

hajimismhajimism

ni drizzle-kit drizzle-orm -Dしてpnpm drizzle-kit generate:sqlite --out migrations --schema src/schema.tsしたらこれが出てきた

CREATE TABLE `todos` (
	`id` integer PRIMARY KEY NOT NULL,
	`title` text NOT NULL,
	`status` text NOT NULL,
	`assigneeId` integer,
	`createdAt` integer NOT NULL
);
--> statement-breakpoint
CREATE TABLE `users` (
	`userId` integer PRIMARY KEY NOT NULL,
	`name` text NOT NULL,
	`iconUrl` text,
	`createdAt` integer NOT NULL
);

hajimismhajimism

mizchiさんの記事のとおりにやって.wrangler/state/v3/d1/206039d5-f4d4-4648-bf12-49fe93704edf/db.sqliteが生成された

hajimismhajimism

Honoだとnew Hono()のときにbindingsをかませることによってcontextからD1にアクセスできるらしい。
https://developers.cloudflare.com/d1/examples/d1-and-hono/

hajimismhajimism
const todoApi = new Hono<{ Bindings: Bindings }>();

todoApi.get("/", async (c) => {
  const db = drizzle(c.env.DB);
  const result = await db.select().from(todos).all();
  return c.json(result);
});

これでresultに型がついてはいた

hajimismhajimism

https://orm.drizzle.team/drizzle-studio/overview

drizzle.config.ts
import type { Config } from "drizzle-kit";

export default {
  schema: "./src/schema.ts",
  driver: "better-sqlite",
  dbCredentials: {
    url: ".wrangler/state/v3/d1/206039d5-f4d4-4648-bf12-49fe93704edf/db.sqlite",
  },
} satisfies Config;

これで立ち上げることができた。絶対shadcn/ui。

hajimismhajimism

あ、Add recordのsave changeのときに型があってないとこうなる

hajimismhajimism

えー、適当な値をinsertしたいときにはどこからやればいいのだろう

hajimismhajimism
export const users = sqliteTable("users", {
  userId: text("userId").primaryKey().notNull().$defaultFn(generateId),
  name: text("name").notNull().default(""),
  iconUrl: text("iconUrl"),
  createdAt: integer("createdAt", { mode: "timestamp" })
    .notNull()
    .$defaultFn(() => new Date()),
});

defaultFnを設定すればstudioからでも自動で値が挿入されるかなと思ったら、datetype mismatchと怒られてできなかった。原因わからず。

hajimismhajimism

ためしにcreatedAtのプロパティを消してもdatetype mismatchになるなあ、なんで?

hajimismhajimism

キャッシュだったっぽい?色々消して新しく作り直したらいけた。

hajimismhajimism

Date問題をイマイチ解決できないまま、一旦いやになったのでClose

このスクラップは2023/09/18にクローズされました