Rorkアプリで環境変数がundefinedになるエラーとは
Rork で生成したアプリに Supabase 連携を組み込んだとき、.env に書いたはずの URL がアプリ側で undefined になり、原因の切り分けに小一時間を費やしたことがあります。コードは正しい。ファイルも存在する。それでも値が読めない。犯人は Metro バンドラーのキャッシュでした。
Expo / React Native の環境変数は「ビルド時に静的に埋め込まれる」という独特の仕組みで動いています。サーバーサイドや Web 開発の「実行時に読む」感覚のままだと、必ずどこかでつまずきます。逆に言えば、この仕組みさえ頭に入れば、undefined エラーの原因は数パターンに絞り込めます。
環境変数が読み込めない主な原因
Rorkアプリ(Expo / React Native ベース)で環境変数に問題が起きる原因は、大きく分けて以下の5つです。
1. EXPO_PUBLIC_ プレフィックスの欠落
Expo SDK 49 以降では、クライアント側(アプリ内)で参照する環境変数には必ず EXPO_PUBLIC_ プレフィックスが必要です。このプレフィックスがない変数はビルド時にバンドルに含まれず、アプリから参照できません。経験上、undefined エラーの過半数はこれが原因です。
2. .env ファイルの配置ミス
.env ファイルがプロジェクトルートではなく、src/ などのサブディレクトリに配置されていると読み込みに失敗します。
3. Metro バンドラーのキャッシュ
.env を変更した後に Metro のキャッシュをクリアしないと、古い値が使われ続けます。冒頭で触れた私の失敗もこれでした。「正しく直したのに反映されない」ときは、まずキャッシュを疑ってください。
4. ビルド時変数とランタイム変数の混同
EAS Build で使う環境変数(eas.json の env や EAS の環境変数管理)と、ローカル開発で使う .env ファイルは別のメカニズムです。混同するとビルドは通るがアプリ内で変数が読めない、という状況になります。
5. EAS 側のシークレット設定ミス
EAS に登録した変数が、環境(development / preview / production)の指定違いなどで正しくビルドプロセスに渡されていないケースです。
Step by Stepの解決手順
ステップ1: .env ファイルの確認と修正
まず、プロジェクトルートに .env ファイルが正しく配置されているか確認します。
# プロジェクトルートにいることを確認
ls -la .env*
# .env ファイルの内容を確認
cat .env正しい .env ファイルの形式は以下の通りです。
# ✅ 正しい形式(クライアント側で使う変数)
EXPO_PUBLIC_API_URL=https://api.example.com
EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
# ✅ 正しい形式(サーバー側・ビルド時のみ使う変数)
SENTRY_AUTH_TOKEN=your_sentry_token
EAS_PROJECT_ID=your_project_idよくある間違いパターンを確認してください。
# ❌ 間違い: EXPO_PUBLIC_ プレフィックスがない
API_URL=https://api.example.com
SUPABASE_URL=https://your-project.supabase.co
# ❌ 間違い: 値をクォーテーションで囲んでいる
EXPO_PUBLIC_API_URL="https://api.example.com"
# ❌ 間違い: スペースが含まれている
EXPO_PUBLIC_API_URL = https://api.example.comステップ2: アプリ内での参照方法を確認
Expo では process.env を通じて環境変数にアクセスします。
// ✅ 正しい参照方法
const apiUrl = process.env.EXPO_PUBLIC_API_URL;
const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_URL;
// デバッグ用: 値が正しく読み込まれているか確認
console.log('API URL:', process.env.EXPO_PUBLIC_API_URL);
console.log('Supabase URL:', process.env.EXPO_PUBLIC_SUPABASE_URL);一点だけ注意があります。process.env[name] のような動的なキー指定では値を取得できません。ビルド時の静的解析が process.env.EXPO_PUBLIC_API_URL という「そのままの記述」を探して値に置換するためです。必ずドット記法で直接参照してください。
react-native-dotenv や expo-constants 経由で読み込む方法もありますが、Expo SDK 49 以降では process.env が推奨されています。
// ⚠️ 旧方式(非推奨だが動作する)
import Constants from 'expo-constants';
const apiUrl = Constants.expoConfig?.extra?.apiUrl;
// ✅ 新方式(推奨)
const apiUrl = process.env.EXPO_PUBLIC_API_URL;ステップ3: Metro キャッシュのクリア
.env ファイルを変更した後は、必ず Metro バンドラーのキャッシュをクリアしてから再起動します。
# Metro キャッシュをクリアして再起動
npx expo start --clear
# より徹底的にクリアする場合
rm -rf node_modules/.cache
npx expo start --clearキャッシュをクリアせずに npx expo start を実行すると、変更前の .env の値が使われ続けます。これは環境変数がビルド時に静的に埋め込まれるためです。私自身、この仕様を知らずに .env を何度も書き直して時間を溶かしました。.env を触ったら --clear。手癖にしておく価値があります。
ステップ4: EAS Build での環境変数設定
EAS Build(クラウドビルド)で環境変数を使う場合は、eas.json で明示的に設定する必要があります。
{
"build": {
"development": {
"env": {
"EXPO_PUBLIC_API_URL": "https://dev-api.example.com",
"EXPO_PUBLIC_SUPABASE_URL": "https://your-project.supabase.co"
}
},
"preview": {
"env": {
"EXPO_PUBLIC_API_URL": "https://staging-api.example.com",
"EXPO_PUBLIC_SUPABASE_URL": "https://your-project.supabase.co"
}
},
"production": {
"env": {
"EXPO_PUBLIC_API_URL": "https://api.example.com",
"EXPO_PUBLIC_SUPABASE_URL": "https://your-project.supabase.co"
}
}
}
}開発・ステージング・本番で接続先を分けておくと、「テストデータが本番 DB に書き込まれていた」という事故を構造的に防げます。個人開発では環境を分けるのを面倒に感じがちですが、リリース後にユーザーデータが入り始めてからでは手遅れになるので、最初のビルド時点で分けておくことをおすすめします。
ステップ5: シークレットの管理 — eas secret から EAS 環境変数へ
API キーなどの機密情報は eas.json に直接書かず、EAS 側で管理します。以前は eas secret:create コマンドで登録する方式でしたが、現在の EAS CLI では環境別に管理できる「EAS 環境変数」(eas env:* コマンド群)へ統合が進み、eas secret 系は非推奨の扱いになっています。
# ✅ 現行方式: EAS 環境変数(環境ごとに登録)
eas env:create --name EXPO_PUBLIC_SUPABASE_URL \
--value "https://your-project.supabase.co" \
--environment production --visibility plaintext
# 機密値は visibility を secret にする(登録後は値を読み出せない)
eas env:create --name SENTRY_AUTH_TOKEN \
--value "your_sentry_token" \
--environment production --visibility secret
# 登録済みの環境変数一覧を確認
eas env:list --environment production
# 開発環境の変数をローカルの .env.local に取得(plaintext のみ)
eas env:pull --environment developmenteas env:pull でローカルと EAS の値を同期できるようになったのが旧 Secrets との大きな違いで、「ローカルでは動くのにクラウドビルドだけ変数が無い」という古典的な食い違いを検出しやすくなりました。古い記事やドキュメントには eas secret:create の手順が残っていますが、これから設定するなら eas env 系に寄せておくのが安全です。
正しく設定できたかの確認方法
修正が完了したら、以下の手順で環境変数が正しく読み込まれているか検証します。
// app/debug-env.tsx(デバッグ用一時ファイル)
import { View, Text, ScrollView } from 'react-native';
export default function DebugEnvScreen() {
const envVars = {
API_URL: process.env.EXPO_PUBLIC_API_URL,
SUPABASE_URL: process.env.EXPO_PUBLIC_SUPABASE_URL,
SUPABASE_ANON_KEY: process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY
? '✅ Set (hidden)'
: '❌ Not set',
};
return (
<ScrollView style={{ padding: 20 }}>
<Text style={{ fontSize: 20, fontWeight: 'bold', marginBottom: 16 }}>
Environment Variables Debug
</Text>
{Object.entries(envVars).map(([key, value]) => (
<View key={key} style={{ marginBottom: 12 }}>
<Text style={{ fontWeight: 'bold' }}>{key}</Text>
<Text style={{ color: value ? '#22c55e' : '#ef4444' }}>
{value || 'undefined ❌'}
</Text>
</View>
))}
</ScrollView>
);
}確認後は必ずこのデバッグ画面を削除してください。機密情報の漏洩を防ぐために、本番ビルドには含めない点が肝心です。
再発防止のベストプラクティス
環境変数のトラブルを繰り返さないために、以下のルールをプロジェクトに導入しましょう。
EXPO_PUBLIC_ に本当の秘密を入れない
見落とされがちですが、最も重要なルールです。EXPO_PUBLIC_ 付きの変数は JS バンドルに平文で埋め込まれます。ストアから配布されたアプリのバンドルを解析すれば誰でも取り出せるため、ここに置いてよいのは「公開されても困らない値」だけです。Supabase の anon key は Row Level Security 前提で公開を想定した設計なので問題ありませんが、service_role キーや決済系のシークレットキーを EXPO_PUBLIC_ にした瞬間、それは全ユーザーへの配布と同じ意味になります。こうした値はクライアントに渡さず、Supabase Edge Functions などサーバー側だけに置いてください。
.env.example をリポジトリに含める
チーム開発や将来の自分のために、必要な環境変数のテンプレートをリポジトリに含めておきます。
# .env.example(値は空またはダミー)
EXPO_PUBLIC_API_URL=
EXPO_PUBLIC_SUPABASE_URL=
EXPO_PUBLIC_SUPABASE_ANON_KEY=.gitignore に .env を追加する
機密情報を含む .env ファイルは必ず .gitignore に追加して、リポジトリにコミットされないようにします。
# .gitignore
.env
.env.local
.env.production型安全な環境変数アクセス
TypeScript プロジェクトでは、環境変数を型安全にアクセスするユーティリティを作成すると、設定ミスを早期に発見できます。
// utils/env.ts
function getEnvVar(key: string): string {
const value = process.env[key];
if (!value) {
console.warn(`⚠️ Environment variable ${key} is not set`);
}
return value ?? '';
}
export const ENV = {
API_URL: getEnvVar('EXPO_PUBLIC_API_URL'),
SUPABASE_URL: getEnvVar('EXPO_PUBLIC_SUPABASE_URL'),
SUPABASE_ANON_KEY: getEnvVar('EXPO_PUBLIC_SUPABASE_ANON_KEY'),
} as const;なお、この getEnvVar のような動的参照は「埋め込み済みの値を整理して再輸出する」用途では機能しますが、ステップ2で述べた通り、バンドルへの埋め込み自体は ENV オブジェクト内の静的な記述によって行われます。アプリ全体でこの ENV オブジェクトを使えば、変数名のタイプミスを防ぎ、未設定の変数を起動直後の警告で検出できます。
全体を振り返って
環境変数が undefined になったとき、確認する順番は決まっています。①EXPO_PUBLIC_ プレフィックス → ②.env の位置と書式 → ③npx expo start --clear → ④EAS 側の環境設定。私の経験では、この順で見ればほとんどのケースが①か③で解決します。
次の一歩としては、.env.example と型安全な ENV オブジェクトを今日のうちにプロジェクトへ入れておくことをおすすめします。同じエラーで二度悩まない仕組みを作るところまでが、トラブルシューティングです。