Closed8

Remix+CloudflareでWebサイトを作る 5(Drizzle Timestamp・refetchの方法・remix.env.d.ts・エラーハンドリング・react-hot-toast)

saneatsusaneatsu

【2024-02-11】DrizzleでSQLiteを使う時のTimestampとデフォルト値の書き方

スキーマの定義方法

もともとこんな感じで定義していた。

db/schema.ts
const timestamp = {
  createdAt: integer("createdAt", { mode: "timestamp" })
    .notNull()
    .default(sql`CURRENT_TIMESTAMP`),
};

実際にデータを入れて値を取ってみると createdAt: undefined になってしまっていた。
以下のように修正。

db/schema.ts
const timestamp = {
  createdAt: text("createdAt")
    .notNull()
    .default(sql`datetime(CURRENT_TIMESTAMP, '+9 hours')`),
  updatedAt: text("updatedAt").notNull(),
};

// 共通化しているtimestampをこんな感じで使っている
export const tagSchema = sqliteTable("tags", {
  id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
  name: text("name").notNull().unique(),
  ...timestamp,
});

npx drizzle-kit generate:sqlite を実行してマイグレーションファイルを作成すると、以下のようなsqlファイルが作成されている

migrations/0000_smiling_multiple_man.sql
CREATE TABLE `tags` (
	`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
	`name` text NOT NULL,
	`createdAt` text DEFAULT datetime(CURRENT_TIMESTAMP, '+9 hours') NOT NULL,
	`updatedAt` text NOT NULL
);

これをローカルに反映させようとすると以下のようなエラーが出る

$ npx wrangler d1 migrations apply YOUR_DB_NAME --local

┌───────────────────────────────┬────────┐
│ name                          │ status │
├───────────────────────────────┼────────┤
│ 0000_smiling_multiple_man.sql │ ❌       │
└───────────────────────────────┴────────┘

❌ Migration 0000_smiling_multiple_man.sql failed with the following errors:

✘ [ERROR] near "(": syntax error at offset 159

作成されたsqlファイルを以下のように修正する。
具体的には datetime(CURRENT_TIMESTAMP, '+9 hours') を括弧で囲んだ。

migrations/0000_smiling_multiple_man.sql
CREATE TABLE `tags` (
	`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
	`name` text NOT NULL,
	`createdAt` text DEFAULT (datetime(CURRENT_TIMESTAMP, '+9 hours')) NOT NULL,
	`updatedAt` text NOT NULL
);

最終形

これでエラーが出なくなった。
sqlファイルを作成するたびに修正するわけにも以下ないのでスキーマ自体を括弧で囲むように修正。

db/schema.ts
const timestamp = {
  createdAt: text("createdAt")
    .notNull()
    .default(sql`(datetime(CURRENT_TIMESTAMP, '+9 hours'))`), // datetimeを括弧で囲む
  updatedAt: text("updatedAt").notNull(),
};

取得結果

すると以下のような形式でデータが取れるようになる。

{
  id: 1,
  name: 'テクノロジー',
  createdAt: '2024-02-11 05:06:35',
  updatedAt: '2024-02-11 05:06:35',
}
saneatsusaneatsu

【2024-02-11】Remixでfetcher.submitでDELETEした後にfetcher.submitでGETしたい

背景

テーブルに何かしらの情報一覧があって、1つのRowを削除した後に再度フェッチをするというごく一般的なユースケース。

雑に書くと以下のようなコード。
削除確認のモーダルを出して「削除」ボタンを押したらfetcher.submit を実行して削除するところまではできた。しかしながらfetcherはawaitできないので再取得を削除前に行ってしまう。

今まではTanStack Query使って脳みそあまり使ってこなかったけどRemixではどうしようか。
こういうケースはあるあるだし、流石にRemixでのベストプラクティスがありそうだから探したい。

export default function Hoge() {
  const fetcher = useFetcher();

  return (<div>
    fetcher.submit(
    { id: tag.id },
    {
      action: "/admin/tags/delete",
      method: "POST",
    }
    );
    fetcher.submit({
      action: "/admin/tags",
      method: "GET",
    });
    </div>
  )
}

削除処理が終わったことをどう知るか

https://gist.github.com/samselikoff/510c020e4c9ec17f1cf76189ce683fa8
https://zenn.dev/yupix/articles/65dca9fe6eb317#コンポーネントからサーバー側にアクセスする

ここらへんを見ると、useEffect を使って特定の状態になった時に何かしらを実行することが推奨されているっぽい。

再取得の方法

fetcher.load() とかで良いのかと思ったけど、以下を見ると2つの方法があった。
2つ目を採用した。

https://github.com/remix-run/react-router/discussions/10381

// 1つ目
const navigate = useNavigate()
navigate('.', { replace: true })

// 2つ目
const revalidator = useRevalidator();
saneatsusaneatsu

【2024-02-12】remix.env.d.ts とはなんなのか

背景

Rmix x CloudflareのExampleコードとか眺めているとこのファイルに定義されたものはexportしていないのに他のファイルから型定義を参照できていたりする。

また、どっかから前に拾ってきたコードだが以下のように書くと「loaderで返される値が JsonifyObject 型なので型定義でてますよ」系のエラーが出なくなる(このコードは使っていない)

このファイルはどのような役割を果たしているのか良くわかっていないので調べる

remix.env.d.ts
declare module "@remix-run/server-runtime" {
  export interface AppLoadContext {}
}

そもそも /// とは

これは、TypeScriptのトリプルスラッシュディレクティブと呼ばれるもので、特定の型定義を参照するために使用される。

/// <reference types="@remix-run/dev" /> と書くことで @remix-run/dev パッケージの型定義を参照することで、パッケージが提供する型を利用できるようになる

因みに、@remix-run/devは、Remixフレームワークの開発サーバーと関連ツールを提供するパッケージで、Remixの開発サーバーのAPIやツールをTypeScriptで型安全に使用することができるようなるらしい(by ChatGPT)

まずは公式ドキュメントを漁る

https://remix.run/docs/en/main/guides/typescript

Remix has TypeScript type definitions built-in as well. The starter templates create a remix.env.d.ts file that is referenced by the tsconfig.json:

Remix には TypeScript の型定義も組み込まれています。 スターター テンプレートは、tsconfig.json によって参照される remix.env.d.ts ファイルを作成します。

Note that the types referenced in remix.env.d.ts will depend on which environment you're running your app in. For example, there are different globals available in Cloudflare.

remix.env.d.ts で参照されるタイプは、アプリを実行している環境によって異なることに注意してください。たとえば、Cloudflare ではさまざまなグローバルが使用できます。

ChatGPTに聞いてみた

remix.env.d.tsはRemixにおいてどのような役割のために利用されるファイルですか?

remix.env.d.tsは、Remixフレームワークで使用される環境変数をTypeScriptに型定義するためのファイルです。.envファイルや環境変数は、アプリケーションの設定や秘密情報(APIキーなど)を管理するためによく使用されます。しかし、これらの変数はJavaScriptでは動的に扱われるため、TypeScriptの型チェックの対象外となります。

remix.env.d.tsファイルを使用することで、環境変数に対する型チェックを有効にすることができます。これにより、環境変数の使用に関するエラーをコンパイル時に検出することができ、バグを早期に発見することが可能になります。

例えば、以下のようにremix.env.d.tsを定義することで、process.env.MY_API_KEYが文字列型であることをTypeScriptに伝えることができます:

declare global {
  namespace NodeJS {
    interface ProcessEnv {
      MY_API_KEY: string;
    }
  }
}

declare module "@remix-run/server-runtime"

declare は既存のJSモジュールや、グローバル変数、関数を型定義をするために使用される。
これによってTypescriptはJSのコード理解し型チェックを行う。

daclare を使って同じ名前のモジュールをやインターフェースを複数回定義すると、それらの定義はマージされる(宣言的マージ:Declaration Merging)。
これによって既存の型を拡張したり、モジュール間で方を共有する。

// @remix-run/server-runtimeモジュールがSomeInterfaceとAnotherInterfaceの両方をエクスポートするという意味になります。
declare module "@remix-run/server-runtime" {
  export interface SomeInterface {
    // ...
  }
}

declare module "@remix-run/server-runtime" {
  export interface AnotherInterface {
    // ...
  }
}

まとめ

remix.env.d.ts とは?

  • remix.env.d.tsは型定義や環境変数などの型定義をするファイル
    • 環境変数に型チェックをすることができるようになることで安全に開発できる
    • 宣言的マージをすることで既存のモジュールを拡張することも可能
  • ファイル内ではトリプルスラッシュディレクティブ(///)などを使って型定義の参照が行われている
  • このファイルで定義されたものはimportせずともグローバルで使用可能になる
saneatsusaneatsu

【2024-02-12】killしてもMiniflareCoreError [ERR_RUNTIME_FAILURE]: The Workers runtime failed to start. There is likely additional logging output above.が出続ける

背景

https://zenn.dev/link/comments/6ad07fefca5e71

2つ前のスクラップでも同じエラーがでていて以下を実行して再度立ち上げで直っていたのだが何回やっても治らない

$ kill $(lsof -t -i:8788)
$ npm run dev

エラー内容

[ERROR] Address already in use (127.0.0.1:8788). Please check that you are not already running a server on this address or specify a different port with --port.


[wrangler:inf] Ready on http://localhost:51672
⎔ Starting local server...

/Users/app_name/node_modules/wrangler/wrangler-dist/cli.js:29572
            throw a;
            ^

MiniflareCoreError [ERR_RUNTIME_FAILURE]: The Workers runtime failed to start. There is likely additional logging output above.

解決方法

立ち上げ前にビルドしたら動いた。謎だ...。

$ kill $(lsof -t -i:8788)
$ npm run build
$ npm run dev
saneatsusaneatsu

【2024-02-12】Joy UIではなく react-hot-toast を選択

https://mui.com/joy-ui/react-snackbar/

現在Joy UIは v5.0.0-beta.27 だが、Snackbarコンポーネントは連打した時に何個も重ねていくことができない。

https://react-hot-toast.com/

前にも使ったことあるし、やりたいことがおおよそできる react-hot-toast を使う。
以下の画像のような感じでスタックしていけるし、スタイルも柔軟に対応できる。

saneatsusaneatsu

【2024-02-13】Remixではv2からメソッド名が大文字になった

公式ドキュメントより

https://remix.run/docs/en/main/start/v2#formmethod

Multiple APIs return the formMethod of a submission. In v1 they returned a lowercase version of the method but in v2 they return the UPPERCASE version. This is to bring it in line with HTTP and fetch specifications.

複数の API は送信の formMethod を返します。 v1 ではメソッドの小文字バージョンが返されましたが、v2 では大文字バージョンが返されました。 これは、HTTP およびフェッチの仕様に準拠させるためです。(Google翻訳)

method も大文字に修正しておいた。

- <form action="/logout" method="post">
+ <form action="/logout" method="POST">

そういやpatchとPATCHは違ったな

https://zenn.dev/neet/articles/de54e08de01c22 #zenn

前に見たこの記事を思い出した。
普通に読み物として面白いし歴史的経緯が詳細に書いていて好き。

saneatsusaneatsu

【2024-02-13】後で読むものメモ

色々調べ物したりTwitter徘徊して見つけた面白そうな技術に関して、少しずつ導入していきたいので一旦メモってみる。

https://zenn.dev/kimitsu/articles/frontend-and-telemetry
ここらへん弱いのでリリース後に見る。

https://qiita.com/KNR109/items/3b14e2e8f89a33c0f959
久々に読み返す。

https://zenn.dev/ako/articles/b8a686843f6b83

今まさにPrettier+ESLintなのでBiome使ってみる。

https://qiita.com/yuppe-namura/items/771ee8a4fb153730270a

Vite, Rollup, Rolldown周りを学んでおきたい。

https://zenn.dev/cybozu_frontend/articles/frontend_weekly_20240116
読む

このスクラップは2ヶ月前にクローズされました