Closed11
Remix on Cloudflare Workersでsupabase realtimeを使うときのメモ
自作のボイラープレートから。
基本的には、RemixのCloudflare Workersテンプレートにwrangler v2用の記載を追加変更しているだけのシンプル版。$ npx create-remix@latest remix-supabase-realtime --template himorishige/remix-cloudflare-workers-boilerplate
supabase関連のパッケージをインストール。
$ yarn add @supabase/supabase-js react-supabase
型定義にグローバルなWorkerに登録する変数を設定。
types/bindings.d.ts
export {};
declare global {
const SUPABASE_ANON_KEY: string;
const SUPABASE_URL: string;
}
Wrangler経由でシークレット変数を登録。
$ wrangler secret put SUPABASE_URL
$ wrangler secret put SUPABASE_ANON_KEY
dev環境等使い分ける場合は--env
を使って登録しておく。
$ wrangler secret put SUPABASE_ANON_KEY --env dev
wranglerで環境を分けてテスト。
package.json
"scripts": {
"build": "remix build",
"deploy": "npm run build && wrangler publish",
"dev:remix": "remix watch",
"dev:miniflare": "cross-env NODE_ENV=development wrangler dev --env dev",
"dev": "remix build && run-p dev:*",
"start": "cross-env NODE_ENV=production wrangler dev"
},
--local
だとシークレットが入らない?
サーバーサイド側で動作するクライアントを用意。
app/libs/supabaseClient.server.ts
import { createClient } from '@supabase/supabase-js';
export const client =
createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
fetch: fetch.bind(globalThis),
});
supabase-js
がcross-fetch
を利用しているのでWokers環境ではglobal fetchを利用するように設定しておく。
supabaseへは一旦下記のようなスキーマを用意。
テーブル名 todo
Name | Type | Default Value | Primary |
---|---|---|---|
id | int8 | --- | ✅ |
title | text | NULL | --- |
created_at | timestamptz | now() | --- |
Remixから接続用関数を用意。
app/models/task.ts
import { client } from '~/libs/supabaseClient.server';
export type Todo = {
id: number;
title: string;
created_at: string;
};
// タスクの一覧を取得
export const getTaskList = async () => {
const { data: todo } = await client
.from<Todo>('todo')
.select('*')
.order('created_at', { ascending: true });
return todo;
};
// タスクを作成する
export const createTask = async (title: string) => {
const { data: todo } = await client.from<Todo>('todo').insert({ title });
return todo;
};
// タスクを削除する
export const deleteTask = async (id: number) => {
const { data: todo } = await client.from<Todo>('todo').delete().eq('id', id);
return todo;
};
トップページにサーバーサイドからの取得データとクライアントサイドでrealtimeのデータを取得してみる。supabaseの管理画面からデータの登録や更新をしてクライアントサイド側のデータが反映されることを確認しておく。
app/routes/index.tsx
import type { LoaderFunction } from '@remix-run/cloudflare';
import { json } from '@remix-run/cloudflare';
import { useLoaderData } from '@remix-run/react';
import type { Todo } from 'models/task';
import { getTaskList } from 'models/task';
import { useRealtime, useSubscription } from 'react-supabase';
export const loader: LoaderFunction = async ({ params, context }) => {
const todo = await getTaskList();
return json(todo);
};
export default function Index() {
const todo = useLoaderData<Todo[] | null>();
const [{ data, error, fetching }, reexecute] = useRealtime('todo');
useSubscription(
(payload) => {
console.log('Change received!', payload);
reexecute();
},
{ event: '*', table: 'todo' },
);
return (
<div style={{ fontFamily: 'system-ui, sans-serif', lineHeight: '1.4' }}>
<h1>Welcome to Remix</h1>
<ul>
{todo?.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
<hr />
<ul>
{data?.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
</div>
);
}
デプロイして本番環境でも問題なさそう。
$ wrangler deploy
このスクラップは2022/07/14にクローズされました