2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Supabase】多対多のテーブルで中間テーブルの記述を省いてデータを取得すると400エラーになる(Searched for a foreign key relationship between 'users' and 'skills' in the schema 'public', but no matches were found.)

Last updated at Posted at 2025-01-07

はじめに

お疲れ様です。

少しハマってしまったので備忘録も兼ねて記事に残しておこうと思います。

問題

Supabaseのドキュメントにて、多対多のテーブルの場合、以下のように記載することでリレーション先の情報も合わせて取得可能という記載がありました。

const { data, error } = await supabase.from('teams').select(`
  id, 
  team_name, 
  users ( id, name )
`)

データ取得時に上記の方法を試してみたのですが、後述するエラーが発生してしまい、うまくいきませんでした。

設定内容

ユーザーが複数の技術を選択できる状態にするため、以下の3つのテーブルを用意していました。

  • users
  • skills
  • user_skill
設定内容
  • usersテーブル
    image.png

  • skillsテーブル
    image.png

  • user_skillテーブル
    image.png

データ取得用のソースコードは以下の通りです。

src/utils/supabaseFunctions.ts
import { supabase } from './supabase';
import { User } from '@/domain/user';

export const fetchUser = async (id: string): Promise<User> => {
  const { data, error } = await supabase.from('users').select('*,skills(id,name)').eq('user_id', id).limit(1).single();

  if (error) throw new Error(error.message);

  const userData = User.createUser(
    data.user_id,
    data.name,
    data.description,
    data.user_skill,
    data.github_id,
    data.qiita_id,
    data.x_id,
    data.created_at
  );

  return userData;
};

コンソールのエラー内容

ブラウザ上には以下のようなコンソールエラーが表示されました。

コンソールエラー
GET
https://{Supabaseアカウント}.supabase.co/rest/v1/users?select=*%2Cskills%28name%29&user_id=eq.sample-id3&limit=1
400 (Bad Request)

レスポンスのエラー内容

試しに該当のソースコードのレスポンスをコンソールで確認してみたところ、以下の内容がerrorに返却されていました。

src/utils/supabaseFunctions.ts
import { supabase } from './supabase';
import { User } from '@/domain/user';

export const fetchUser = async (id: string): Promise<User> => {
  const { data, error } = await supabase.from('users').select('*,skills(name)').eq('user_id', id).limit(1).single();

+ console.log(data);
+ console.log(error);

  if (error) throw new Error(error.message);

  const userData = User.createUser(
    data.user_id,
    data.name,
    data.description,
    data.user_skill,
    data.github_id,
    data.qiita_id,
    data.x_id,
    data.created_at
  );

  return userData;
};
レスポンス内容(error)
{
    "code": "PGRST200",
    "details": "Searched for a foreign key relationship between 'users' and 'skills' in the schema 'public', but no matches were found.",
    "hint": "Perhaps you meant 'user_skill' instead of 'skills'.",
    "message": "Could not find a relationship between 'users' and 'skills' in the schema cache"
}

解決方法

Supabaseのテーブル設定で、中間テーブルのリレーション用のカラム(今回の場合はuser_skillテーブルの、user_idskill_id)をプライマリーキーに指定する必要がありました。

  • user_skillテーブル
    image.png
レスポンス内容(data)
{
    "user_id": "sample-id3",
    "name": "テスト三郎",
    "description": "<h3>テスト三郎の自己紹介</h3>",
    "github_id": "",
    "qiita_id": "ritsu21ctws3",
    "x_id": "ritsu21ctws3",
    "created_at": "2025-01-03T03:31:32.036016+00:00",
    "skills": [
        {
            "id": 1,
            "name": "React"
        },
        {
            "id": 2,
            "name": "TypeScript"
        },
        {
            "id": 3,
            "name": "GitHub"
        }
    ]
}

おわりに

まとめると、Supabaseで多対多のテーブルのリレーション先も合わせて(省略記法で)取得するには、中間テーブルのリレーション用のカラムに対して、以下の設定が必要になるというお話でした。

  • プライマリーキーの設定
  • 外部キーの設定

公式ドキュメントの以下の例にちゃんと書かれていたのですが、完全に見逃していました。。

create table members (
  "user_id" int references users,
  "team_id" int references teams,
  primary key (user_id, team_id)
);

補足

補足ですが、「問題」セクションの状態のままでも、select句で中間テーブル名を記述した場合はデータ取得が成功しました。

src/utils/supabaseFunctions.ts
import { supabase } from './supabase';
import { User } from '@/domain/user';

export const fetchUser = async (id: string): Promise<User> => {
  const { data, error } = await supabase.from('users').select('*,user_skill(skills(id,name))').eq('user_id', id).limit(1).single();

  if (error) throw new Error(error.message);

  const userData = User.createUser(
    data.user_id,
    data.name,
    data.description,
    data.user_skill,
    data.github_id,
    data.qiita_id,
    data.x_id,
    data.created_at
  );

  return userData;
};
レスポンス内容(data)
{
    "user_id": "sample-id3",
    "name": "テスト三郎",
    "description": "<h3>テスト三郎の自己紹介</h3>",
    "github_id": "",
    "qiita_id": "ritsu21ctws3",
    "x_id": "ritsu21ctws3",
    "created_at": "2025-01-03T03:31:32.036016+00:00",
    "user_skill": [
        {
            "skills": {
                "id": 1,
                "name": "React"
            }
        },
        {
            "skills": {
                "id": 2,
                "name": "TypeScript"
            }
        },
        {
            "skills": {
                "id": 3,
                "name": "GitHub"
            }
        }
    ]
}

ただ、中間テーブルを省略した記法の方がレスポンス内容はわかりやすくなるため、今後は中間テーブルを省略した記述方法でデータ取得を行いたいと思います。

参考

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?