Open5

Supabase で DB の型情報を自動生成する

プログラミングをするパンダプログラミングをするパンダ

出力されるファイルの中の definitions にテーブル情報が格納されている

例えば Users テーブルなら以下。not nullを指定していないカラムは optional になる

export interface definitions {
  users: {
    /**
     * Note:
     * This is a Primary Key.<pk/>
     */
    id: number
    username: string
    avatar: string
    created_at?: string
    updated_at?: string
  }
}
プログラミングをするパンダプログラミングをするパンダ

なお、DB からの返り値(supabase.from(table).select())のオブジェクトのキー名を camel case に変換したい場合、camelcase-keysを使えばいい。

https://www.npmjs.com/package/camelcase-keys

なお、ネスト内も変換したい場合は、以下のような便利関数を作れば良い

import camelcaseKeys from 'camelcase-keys'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const camelizeDeeply = <T extends Record<string, any> | readonly any[]>(arg: T) =>
  camelcaseKeys(arg, { deep: true })
プログラミングをするパンダプログラミングをするパンダ

自動生成した型の扱いを一部公開。

コマンドで generated.ts を生成して型をキャメルケースに変換するだけ。簡単


テーブル名を定数に

//table.ts

export const Table = {
  Companies: 'companies',
  Beers: 'beers',
  Users: 'users',
  Reviews: 'reviews',
  Comments: 'comments',
  ReviewLikes: 'review_likes',
  CommentActivities: 'comment_activities',
  RelationshipActivities: 'relationship_activities',
} as const

view 名を定数に

export const View = {
  // 通知
  CommentActivities: 'comment_activities_view',
} as const

自動生成した型。今開発してるサイトでは約3000行のファイルになっている。テーブルの型が自動生成されている。以下は一部

// generated.ts

export interface definitions {
  beers: {
    /**
     * Note:
     * This is a Primary Key.<pk/>
     */
    id: number;
    name: string;
    /**
     * Note:
     * This is a Foreign Key to `brands.id`.<fk table='brands' column='id'/>
     */
    brand_id?: number;
    description?: string;
    purchase_url?: string;
  };
  // ...
}
// types
import { Session as SupabaseSession, User as SupabaseUser } from '@supabase/supabase-js'
import { CamelCasedPropertiesDeep } from 'type-fest'

import { Table } from '../db'
import { View } from '../views'

import { definitions } from './generated'

// スネークケースをキャメルケースに変換する
type CCD<T> = CamelCasedPropertiesDeep<T>
type R<T> = Required<T>

// Table
export type Beer = CCD<definitions[typeof Table.Beers]>
export type CommentLike = CCD<definitions[typeof Table.CommentLikes]>
export type CommentReply = CCD<definitions[typeof Table.CommentReplies]>
export type Comment = CCD<definitions[typeof Table.Comments]>
export type Review = CCD<definitions[typeof Table.Reviews]>
export type User = CCD<definitions[typeof Table.Users]>

// Enum
export type Taste = definitions[typeof Table.ReviewTaste]['taste']

// View
// 通知系
export type CommentActivitiesView = R<CCD<definitions[typeof View.CommentActivities]>>