Open5

Supabase 開発はじめ memo

tokitoki

typescriptの型の自動生成

Generating TypeScript Typesの通りにやればできる

tokitoki

コード

package.json

  "scripts": {
...
    "type-gen": "source .env.local && npx supabase gen types --lang=typescript --project-id $SUPABASE_PROJECT_ID --schema public > src/types/database.d.ts"
  },

※ .env.localにダッシュボード(Project Settings)で確認できるReference IDを追加する SUPABASE_PROJECT_ID=hogehoge

supabase.ts

import { Database } from "types/database";
...
export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey,
...
tokitoki

スネークケース → キャメルケースの変換

tokitoki

スネークケースからキャメルケースに変換

import camelcaseKeys from "camelcase-keys";

export type SnakeToCamelCase<S extends string> =
  S extends `${infer T}_${infer U}`
    ? `${Lowercase<T>}${Capitalize<SnakeToCamelCase<U>>}`
    : S;
export type SnakeToCamelCaseNested<T> = T extends (infer U)[]
  ? SnakeToCamelCaseNested<U>[]
  : T extends object
    ? {
        [K in keyof T as SnakeToCamelCase<K & string>]: SnakeToCamelCaseNested<
          T[K]
        >;
      }
    : T;

export const snakeToCamel = <
  T extends Record<string, unknown> | readonly Record<string, unknown>[],
>(
  request: T
) => camelcaseKeys(request) as unknown as SnakeToCamelCaseNested<T>;

使い方

取得処理を共通化して変換処理を適応してみる

import { PostgrestError } from "@supabase/supabase-js";
import { useEffect, useState } from "react";
import { snakeToCamel } from "utils/snakeToCamel";

type Fetcher<T> = () => Promise<{
  data: T | null;
  error: PostgrestError | null;
}>;

export const useFetchFromSupabase = <T>(fetcher: Fetcher<T>) => {
  const [data, setDate] = useState<T | null>(null);
  const [error, setError] = useState<PostgrestError | null>(null);
  useEffect(() => {
    const fetchData = async () => {
      const { data, error } = await fetcher();
      setDate(data);
      setError(error);
    };
    fetchData();
  }, [fetcher]);

  return { data: data ? snakeToCamel(data) : null, error };
};

実際に使ってみる

import { useFetchFromSupabase } from "hooks/useFetchFromSupabase";
import { supabase } from "libs/supabase";

export const useGetRecipes = () => {
  const { data, error } = useFetchFromSupabase(fetcher);
  return { data, error };
};

const fetcher = async () => {
  const { data, error } = await supabase.from("recipes").select("*");
  return { data, error };
};

確認

snake case camel case value
スクリーンショット 2024-08-04 18 29 40 スクリーンショット 2024-08-04 18 29 46 スクリーンショット 2024-08-04 18 36 43スクリーンショット 2024-08-04 18 37 22