Open5

remixとD1でTODOアプリを作る

おたきおたき

プロジェクト作成

公式テンプレートから作成

npx create-remix@latest --template remix-run/remix/templates/cloudflare

インストールしたパッケージは以下

package.json
{
  "name": "remix-d1-todo-app",
  "private": true,
  "sideEffects": false,
  "type": "module",
  "scripts": {
    "build": "remix vite:build",
    "deploy": "wrangler pages deploy ./build/client",
    "dev": "remix vite:dev",
    "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
    "start": "wrangler pages dev ./build/client",
    "tsc": "tsc",
    "typegen": "wrangler types"
  },
  "dependencies": {
    "@conform-to/react": "^1.0.6",
    "@conform-to/zod": "^1.0.6",
    "@radix-ui/react-slot": "^1.0.2",
    "@remix-run/cloudflare": "^2.8.1",
    "@remix-run/cloudflare-pages": "^2.8.1",
    "@remix-run/react": "^2.8.1",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.0",
    "isbot": "^4.1.0",
    "lucide-react": "^0.365.0",
    "miniflare": "^3.20231030.4",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "tailwind-merge": "^2.2.2",
    "tailwindcss-animate": "^1.0.7",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@cloudflare/workers-types": "^4.20230518.0",
    "@remix-run/dev": "^2.8.1",
    "@types/react": "^18.2.20",
    "@types/react-dom": "^18.2.7",
    "@typescript-eslint/eslint-plugin": "^6.7.4",
    "@typescript-eslint/parser": "^6.7.4",
    "autoprefixer": "^10.4.19",
    "eslint": "^8.38.0",
    "eslint-import-resolver-typescript": "^3.6.1",
    "eslint-plugin-import": "^2.28.1",
    "eslint-plugin-jsx-a11y": "^6.7.1",
    "eslint-plugin-react": "^7.33.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "node-fetch": "^3.3.2",
    "postcss": "^8.4.38",
    "prettier": "^3.2.5",
    "prettier-plugin-tailwindcss": "^0.5.13",
    "remix-development-tools": "^4.1.4",
    "tailwindcss": "^3.4.3",
    "typescript": "^5.1.6",
    "vite": "^5.1.0",
    "vite-tsconfig-paths": "^4.2.1",
    "wrangler": "^3.24.0"
  },
  "engines": {
    "node": ">=18.0.0"
  }
}

デプロイする(先にビルドすること忘れず)

npm run deploy

> deploy
> wrangler pages deploy ./build/client

▲ [WARNING] Pages now has wrangler.toml support.

  We detected a configuration file at
  /Users/takidakeiichiro/ghq/github.com/otaki0413/remix-d1-todo-app/wrangler.toml
  but it is missing the "pages_build_output_dir" field, required by Pages.
  If you would like to use this configuration file to deploy your project,
  please use "pages_build_output_dir" to specify the directory of static
  files to upload.
  Ignoring configuration file for now, and proceeding with project deploy.


No project specified. Would you like to create one or use an existing project?
❯ Create a new project
  Use an existing project
✔ Enter the name of your new project: … remix-d1-todo-app
✔ Enter the production branch name: … main
✨ Successfully created the 'remix-d1-todo-app' project.
✨ Compiled Worker successfully
🌍  Uploading... (8/8)

✨ Success! Uploaded 8 files (3.60 sec)

✨ Uploading Functions bundle
✨ Deployment complete! Take a peek over at https://faa34ce4.remix-d1-todo-app.pages.dev
おたきおたき

D1の設定

  • wranglerコマンドでtodo-appという名前でDB作成
❯ npx wrangler d1 create todo-app
 ⛅️ wrangler 3.48.0
-------------------
✅ Successfully created DB 'todo-app' in region APAC
Created your new D1 database.

[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "todo-app"
database_id = "<データベースID>"

wrangler.tomlにコピペ

wrangler.toml
[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "todo-app"
database_id = "<データベースID>"

npmスクリプトよりwrangler typesを実行して、型ファイル作成
これはwrangler.tomlが元になっている

worker-configuration.d.ts
// Generated by Wrangler on Sun Apr 07 2024 16:28:19 GMT+0900 (Japan Standard Time)
// by running `wrangler types`

interface Env {
  DB: D1Database;
}

ダッシュボードよりバインディング設定をする

下記スクラップ参考
https://zenn.dev/link/comments/bef03a031a285d

おたきおたき

Drizzle ORMの導入 + マイグレーション適用

パッケージインストール

npm i drizzle-orm
npm i -D drizzle-kit

スキーマを作成する(SQLiteの型は https://orm.drizzle.team/docs/column-types/sqlite を参考)

schema.server.ts
import { sql } from "drizzle-orm";
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";

export const todos = sqliteTable("todos", {
  id: integer("id").primaryKey(),
  text: text("text").notNull(),
  done: integer("done", { mode: "boolean" }),
  userId: integer("userId").references(() => users.id),
});

export const users = sqliteTable("users", {
  id: integer("id").primaryKey(),
  email: text("email").unique().notNull(),
  name: text("name"),
  createdAt: text("createdAt")
    .notNull()
    .default(sql`CURRENT_TIMESTAMP`),
  updatedAt: text("updatedAt")
    .notNull()
    .default(sql`CURRENT_TIMESTAMP`),
});

プロジェクト直下にDrizzleの設定ファイルdrizzle.config.tsを作成しておく

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

export default {
  schema: "app/drizzle/schema.server.ts",
  out: "app/drizzle/migrations",
} satisfies Config;

npmスクリプトを設定 + 実行

package.json
"scripts": {
    "drizzle:update": "drizzle-kit generate:sqlite" 👈️ これ!
  },
npm run drizzle:update

> drizzle:update
> drizzle-kit generate:sqlite

drizzle-kit: v0.20.14
drizzle-orm: v0.30.7

No config path provided, using default 'drizzle.config.ts'
Reading config file '/Users/takidakeiichiro/ghq/github.com/otaki0413/remix-d1-todo-app/drizzle.config.ts'
2 tables
todos 4 columns 0 indexes 1 fks
users 5 columns 1 indexes 0 fks

ローカルにマイグレーション適用(オプションなし)

❯ npx wrangler d1 migrations apply todo-app
 ⛅️ wrangler 3.48.0
-------------------
Migrations to be applied:
┌──────────────────────────────┐
│ name                         │
├──────────────────────────────┤
│ 0000_silent_black_knight.sql │
└──────────────────────────────┘
? About to apply 1 migration(s)
✔ 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
🌀 Executing on local database todo-app (361dd76e-3885-4552-ac43-d725014ad9b4) from .wrangler/state/v3/d1:
🌀 To execute on your remote database, add a --remote flag to your wrangler command.
┌──────────────────────────────┬────────┐
│ name                         │ status │
├──────────────────────────────┼────────┤
│ 0000_silent_black_knight.sql │ ✅       │
└──────────────────────────────┴────────┘

リモートに適用(--remoteオプションつける)

❯ npx wrangler d1 migrations apply todo-app --remote
 ⛅️ wrangler 3.48.0
-------------------
Migrations to be applied:
┌──────────────────────────────┐
│ name                         │
├──────────────────────────────┤
│ 0000_silent_black_knight.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
🌀 Parsing 4 statements
🌀 Executing on remote database todo-app (361dd76e-3885-4552-ac43-d725014ad9b4):
🌀 To execute on your local development database, remove the --remote flag from your wrangler command.
🚣 Executed 4 commands in 0.7379ms
┌──────────────────────────────┬────────┐
│ name                         │ status │
├──────────────────────────────┼────────┤
│ 0000_silent_black_knight.sql │ ✅       │
└──────────────────────────────┴────────┘

テーブルできてるね!

参考
https://github.com/drizzle-team/drizzle-orm/tree/main/examples/cloudflare-d1