🐥

Supabaseのフィルタークエリ(PostgrestFilterBuilder)を動的に作成する

2024/04/03に公開

※全体的に未確認事項が多くふわっとしてます🐥

https://example.com/list?id=1&name=foo&enabled=true

たとえばこんなクエリパラメータを受け取ってSupabaseのFilterAPIで検索したい場合

const { id, name, enabled } = queryPrams;

return supabase
    .from('articles')
    .select('*')
    .eq('id', id || '')
    .eq('name', name || '')
    .eq('enabled', enabled || '');

これだとクエリパラメータが存在しないときに意図しない検索結果になる(たぶん)

ので、フィルター部分を動的に合体させてみようと思う。

const { id, name, enabled } = queryPrams;

let baseQuery = await supabase.from('articles').select('*');

if (id) {
    baseQuery = baseQuery.eq('id', id || '');
}
if (name) {
    baseQuery = baseQuery.eq('name', name || '');
}
if (enabled) {
    baseQuery = baseQuery.eq('enabled', enabled || '');
}

return baseQuery;

うーん、絶妙にダサい気がする。他に方法ないんかなー

ここからわからんこと

上記で記事のタイトルにあるようなことはまあギリできる。ここからはどうでもいけどわからんこと。

この条件分岐の実装を別関数に切り出したいときの型定義の方法がまたわからん

const { id, name, enabled } = queryPrams;

let baseQuery = await supabase.from('articles').select('*');

return replaceFilter(baseQuery, {id, name, enabled});
replaceFilter
const replaceFilter = (baseQuery, params) => {

...

return query
}

このときreplaceFilterで受け取るbaseQueryの型の指定の仕方がわからぬ。

PostgrestFilterBuilderという型を使いそう。この型どっから持ってくるんや?

https://github.com/orgs/supabase/discussions/2733

import type { PostgrestFilterBuilder } from "@supabase/postgrest-js";

これで使えそう。こんな感じか?

 baseQuery: PostgrestFilterBuilder<
    Database['public'],
    Record<string, unknown>,
    unknown
  >;

絶対違う、第1引数はあってそうやけど、あと2つもっと詳細にできそう。
第2引数にはDatabase['public']~~~~["articles"]["row"]みたいなの指定できるのでは?と思うんやけど、実際のQueryはJoinしたセレクト文書いてることが多いのでちょっと現実的じゃない気がする(ゴリ押しで書けそうやけど)

https://supabase.com/docs/reference/javascript/typescript-support#response-types-for-complex-queries

このQueryData使えばいけんじゃね?って感じはするんやけど、これもなんか、複数ファイルにまたがって型定義共有しようとするとうまく挙動しないことがある。Supabaseクライアントがクライアント向けまたはサーバー向けに作成されたのかによって挙動が違うことが要因そうに見えるが、ここは自分でも確認しきれていない。

わかったら追記しようと思う。

Discussion