エラーの概要

Supabase の 400 エラーは、API へのリクエストの形式または内容に誤りがあることを示します。PostgREST クエリのフィルタ構文の誤り、必須ヘッダーの不足、認証 API パラメータの型ミスなど、クライアント側の問題が主な原因です。このエラーが返された場合、リクエスト自体を修正する必要があり、サーバーの状態ではなく送信側の実装を見直すべき合図です。

実際のエラーメッセージ例

Supabase JavaScript クライアントでの例:

{
  "error": "400 Bad Request",
  "message": "Invalid filter: Column 'user_id' must use one of the following operators: eq, neq, gt, gte, lt, lte, like, ilike, is, in, cs, cd, sl, sr, nxl, nxr, adj, not, or, and",
  "status": 400
}

cURL での直接リクエスト例:

curl -X GET "https://<your-project>.supabase.co/rest/v1/users?status=eq.active&age=gt.25" \
  -H "apikey: <your-api-key>" \
  -H "Content-Type: application/json"

上記のように正しいフィルタ構文を使用しない場合、400 が返ります。

よくある原因と解決手順

原因 1:PostgREST フィルタ構文の誤り

Supabase は PostgreSQL の高度なフィルタリング機能を提供していますが、正しい演算子と記号を使わなければ 400 エラーが返ります。>< などの SQL 記号をそのままクエリに含めると、URL エンコーディングの問題やパーサーエラーが発生します。

Before(エラーが起きるコード):

// ❌ 不正な演算子 > を直接使用
const { data, error } = await supabase
  .from('users')
  .select('*')
  .filter('age > 25');

After(修正後):

// ✅ 正しい PostgREST 演算子 gt を使用
const { data, error } = await supabase
  .from('users')
  .select('*')
  .gt('age', 25);

別の例として、複数条件の指定時に誤った区切り文字を使う場合もあります。

Before(エラーが起きるコード):

// ❌ URL クエリパラメータで不正な構文
const { data, error } = await supabase
  .from('posts')
  .select('*')
  .filter('status=published AND user_id=5');

After(修正後):

// ✅ メソッドチェーンで正しく複数条件を指定
const { data, error } = await supabase
  .from('posts')
  .select('*')
  .eq('status', 'published')
  .eq('user_id', 5);

原因 2:必須リクエストヘッダーの不足

Supabase API へのすべてのリクエストには Content-Typeapikey ヘッダーが必須です。特に POST や PATCH リクエストJSON ボディを送信する場合、Content-Type: application/json を明記しないと 400 が返ります。

Before(エラーが起きるコード):

# ❌ Content-Type ヘッダーが指定されていない
curl -X POST "https://<your-project>.supabase.co/rest/v1/users" \
  -H "apikey: <your-api-key>" \
  -d '{"name":"John","email":"john@example.com"}'

After(修正後):

# ✅ Content-Type を明示的に指定
curl -X POST "https://<your-project>.supabase.co/rest/v1/users" \
  -H "apikey: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{"name":"John","email":"john@example.com"}'

JavaScript クライアントライブラリを使う場合は、通常自動的にヘッダーが付与されます。

Before(エラーが起きるコード):

// ❌ 標準 fetch API 使用時、ヘッダーが省略されている
const response = await fetch('https://<your-project>.supabase.co/rest/v1/users', {
  method: 'POST',
  body: JSON.stringify({ name: 'John', email: 'john@example.com' })
});

After(修正後):

// ✅ Supabase クライアントを使用するか、明示的にヘッダー指定
const response = await fetch('https://<your-project>.supabase.co/rest/v1/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'apikey': '<your-api-key>'
  },
  body: JSON.stringify({ name: 'John', email: 'john@example.com' })
});

原因 3:Auth API パラメータの型ミスまたは無効な値

Supabase Auth API(ユーザー登録・ログイン)では、メールアドレスやパスワード、その他メタデータのパラメータが厳密に検証されます。必須フィールドが欠けていたり、データ型が違ったり、無効な形式だったりすると 400 が返ります。

Before(エラーが起きるコード):

// ❌ パスワードが 6 文字未満、またはメールアドレスの形式が不正
const { data, error } = await supabase.auth.signUp({
  email: 'invalid-email',
  password: '12345'
});

if (error) {
  console.log('400 エラー:', error.message);
}

After(修正後):

// ✅ 正しい形式でパラメータを指定(デフォルトでは 6 文字以上のパスワード)
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password-123'
});

if (error) {
  console.log('エラー:', error.message);
}

メタデータの追加時にオブジェクト以外の値を渡す場合も同様です。

Before(エラーが起きるコード):

// ❌ user_metadata が文字列で指定されている
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password-123',
  options: {
    data: 'invalid-metadata'  // ❌ 文字列ではなくオブジェクト必須
  }
});

After(修正後):

// ✅ user_metadata をオブジェクトで指定
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password-123',
  options: {
    data: { role: 'user', company: 'ACME Inc' }  // ✅ オブジェクト形式
  }
});

ツール固有の注意点

Supabase はエラーレスポンスmessage フィールドに詳細な情報を含めます。400 エラーが返された場合、その message を確認することが問題解決の第一歩です。例えば「Invalid filter」と明記されれば PostgREST フィルタの誤り、「Invalid credentials」なら認証パラメータの誤りなど、原因が特定しやすくなります。

また、Supabase ダッシュボードの「Table Editor」機能を活用して、クエリを直接ブラウザで試すことで、フィルタ構文の正確さを確認できます。正しく動作するクエリがダッシュボードで作成できれば、それと同じロジックをコード側に実装することで 400 エラーを防げます。

さらに、JavaScript クライアントライブラリは頻繁に更新されており、古いバージョンを使用していると API の変更に追従できず、正規のリクエストまで 400 が返されることがあります。npm install @supabase/supabase-js@latest で常に最新版を保つようにしてください。

それでも解決しない場合

まずはブラウザーの開発者ツール(F12)のネットワークタブで、実際に送信されているリクエストヘッダーと URL を確認します。Supabase ダッシュボードの「Logs」セクションでは、API に到達したリクエストの詳細ログが記録されており、どの部分が不正と判定されたかを追跡できます。

以下のコマンドで、リクエストの詳細を verbose モードで確認することも有効です。

curl -v -X GET "https://<your-project>.supabase.co/rest/v1/users?status=eq.active" \
  -H "apikey: <your-api-key>" \
  -H "Content-Type: application/json"

-v フラグにより、送受信されるヘッダーレスポンス全体が表示されます。

また、Supabase の公式ドキュメント(https://supabase.com/docs/reference/javascript/select)で PostgREST フィルタ演算子の完全なリストを確認し、使用している演算子が正しいものであることを再度確認してください。Auth APIパラメータについても公式リファレンス(https://supabase.com/docs/reference/javascript/auth-signup)で仕様を熟読することで、型や形式の誤りを防げます。


免責事項:本記事の内容は、執筆時点の公開情報をもとに作成したものです。ソフトウェアの仕様は予告なく変更されることがあります。最新の情報は各ツールの公式サポートページをご確認ください。本記事の情報を利用した結果生じたいかなる損害についても、著者および運営者は責任を負いかねます。