Open5

Expo + Supabase + NativeWind + Resendのセットアップ

kiiitakiiita

ExpoにSupabaseを導入

基本的に公式Doc通り進めれば問題ない。

パッケージをインストール

$ npx expo install @supabase/supabase-js @react-native-async-storage/async-storage react-native-elements react-native-url-polyfill

AsyncStorageでのsession管理のセットアップ

lib/supabase.tsを作成する

import 'react-native-url-polyfill/auto'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = YOUR_REACT_NATIVE_SUPABASE_URL
const supabaseAnonKey = YOUR_REACT_NATIVE_SUPABASE_ANON_KEY

export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
  auth: {
    storage: AsyncStorage,
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: false,
  },
})
kiiitakiiita

Expo routerを導入

パッケージをインストール。

$ npx expo install expo-router react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar react-native-gesture-handler

package.jsonを修正

-   "main": "node_modules/expo/AppEntry.js",
+   "main": "expo-router/entry",

app.jsonを修正

SupabaseでのDeep Linkに対応するためにschemeは"com.supabase"を指定する。

{
  "expo": {
    "name": "Sample App",
    "slug": "sample-app",
+   "scheme": "com.supabase",

schemeを指定したら、SupabaseのAuth Settingsの画面からリダイレクトURLにcom.supabase://**を追加する。
FYI: https://supabase.com/docs/guides/auth/native-mobile-deep-linking

babel.config.jsを修正

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
+   plugins: ["expo-router/babel"],
  };
};

キャッシュをクリアして再起動

$ npx expo start -c
kiiitakiiita

ExpoにNativeWindを導入

パッケージをインストール

$ npm install nativewind 
$ npm install --dev tailwindcss@3.3.2

[補足] 公式Docではtailwincssのバージョンが指定されておらず、yarn add --dev tailwindcssとなっていたが、これだと"tailwindcss": "^3.3.6",がインストールされ、アプリ起動時に以下のエラーが発生した(Expo49)

Use process(css).then(cb) to work with async plugins

詳細まで追えていないが、Tailwindcssの方でBreaking changesがあり、それが要因ぽい。
対処法として、上記の通りバージョンを3.3.2を指定する。

tailwind.config.jsを以下のコマンドで作成

$ npx tailwindcss init

tailwind.config.jsを修正

Tailwindcssを適用するファイルパスを指定する

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
+    "./App.{js,jsx,ts,tsx}",
+    "index.tsx",
+    "./app/**/*.{js,jsx,ts,tsx}",
+    "./components/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

babel.config.jsのpluginに"nativewind/babel"を追加

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
-   plugins: ["expo-router/babel"],
+   plugins: ["expo-router/babel", "nativewind/babel"],
  };
};

このままだと<View className='flex-1'>のようにした時にclassNameで型エラーが発生するので、app.d.tsをルートディレクトリ配下に作成する。

+ /// <reference types="nativewind/types" />

FYI: https://www.nativewind.dev/getting-started/typescript

あとはいつも通りclassをあてていくだけでOK。

    <View className="items-center justify-center w-full h-full px-4">
      <Input
        placeholder="Email"
        value={email}
        onChangeText={(text) => setEmail(text)}
        className="bg-white rounded-md p-4 text-slate-900"
      />
      <View>
        <TouchableOpacity
          disabled={loading}
          onPress={() => signUp()}
          className="bg-slate-900 text-white rounded-md py-4 px-8 mt-4"
        >
          <Text className="text-white font-bold text-md">Sign In</Text>
        </TouchableOpacity>
      </View>
    </View>

スタイルが反映されてなければ、Expoサーバーをキャッシュをクリアして再起動する

$ npm expo start -c

サーバーを再起動してもスタイルが反映されてなければ、tailwind.config.jsのファイルパスの設定が正しいか確認すると良いかもしれない。

kiiitakiiita

Resendの導入

Supabaseにもとから組み込まれているメール送信機能は1時間あたり4件という上限が設定されているため、開発時にそれ以上使いたい場合は外部のメール配信サービスと連携し、カスタムSMTPの設定が必要になる。

以前少し使ったことがあるSendGridを検討したが、アカウント開設に1-2日ほどリードタイムがあるので、SupabaseのWebサイトでも紹介されていたResendを試してみる。

[補足] Resendは10,000通/月、100通/月まで無料プランで使える。SendGridは12,000通/月まで無料。

Resendのアカウントを作成し、次のResendのページからSupabaseを連携する。
https://resend.com/settings/integrations

連携ボタンを押す。

権限付与をするorgを選択する。

選択したorgの中のどのプロジェクトと連携するかを設定する。

次にAdd an API Keyでドメインを選択するのだが、このドメインは取得済みのカスタムドメインを登録する必要があるっぽく、まだドメインを取得していない段階では使えない...?

Resend自体はカスタムドメインを使わずとも標準の@resend.devが利用可能っぽいが、上記のSupabaseとの連携画面ではそれが選択できなさそうだったので困った。

kiiitakiiita

Resend連携の続き

上記のようにResendのIntegrationから設定を進めるとカスタムドメインが必要になるので、別の方法で進めたらカスタムドメインなしで連携ができた。

APIキーを作成する(Resend側)

Resendの方でまずAPIキーを発行する。

APIキーの名前はわかりやすいもので適当でOK。
APIキーは閉じたら再表示はできないので控えておく(1)。

SMTPの設定をする(Supabase側)

Settings > Authentication > SMTP Settingsに進み、Enable Custom SMTPをオンにする。

各フィールドを埋めて登録する。

  • Sender email: {任意の文字列}@resend.dev
  • Sender name: 任意の文字列
  • Host: smtp.resend.com
  • Port number: 465
  • Minimum interval between emails being sent: 1
  • Username: resend
  • Password: (1)で発行したAPIキー

これで登録すれば、SupabaseでのAuthで送信されるメールが{任意の文字列}@resend.devから送信される🎉

メール認証

Supabaseが提供しているワンタイムパスワードを試してみる

  async function signUp() {
    setLoading(true);
    const { error } = await supabase.auth.signInWithOtp({
      email,
    });

    if (error) {
      Alert.alert(error.message);
      setLoading(false);
      return;
    }

    setLoading(false);
    router.push({
      pathname: "/opt",
      params: { email },
    });
  }

メールアドレスを入力して送信すると...

先程連携したSMTPからOTPが送信される。

Resendのダッシュボード

こんな感じのサイバー感のあるUIでログが確認できる。

Resendはキャリアドメイン宛に送信がうまく行かないという話もあるので、Productionへの導入はそのあたりを調査した上で検討だが、一旦SendGridのアカウント開設を待つ間にサクッとローカルでのメール配信環境を作るのには気楽にできていいかなと思った。