cli icon indicating copy to clipboard operation
cli copied to clipboard

Generating TypeScript types from CLI for custom schema incorrectly uses public schema typings

Open greenstick opened this issue 1 year ago • 1 comments

Bug report

  • [ x ] I confirm this is a bug with Supabase, not with my own application.
  • [ x ] I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

When using the Supabase CLI tool to generate TypeScript types, if a custom schema is provided, for example:

supabase gen types typescript --project-id abcdefghijklmnopqrstuv --schema api > database.d.ts

The CLI tool doesn't use the provided schema when generating the following types in database.d.ts:

export type Json =
  | string
  | number
  | boolean
  | null
  | { [key: string]: Json | undefined }
  | Json[]

// The exported Database type has the custom "api" schema keyed correctly ✅
export type Database = {
  api: {
    Tables: {
      ... // Tables in api schema
    }
    Enums: {
      [_ in never]: never
    }
    CompositeTypes: {
      [_ in never]: never
    }
  }
}

// However, the "public" schema appears to be mistakenly referenced here instead of the custom "api" schema  ❌
type PublicSchema = Database[Extract<keyof Database, "public">]

// And then the issue cascades through to the remaining types below ❌
export type Tables<
  PublicTableNameOrOptions extends
    | keyof (PublicSchema["Tables"] & PublicSchema["Views"])
    | { schema: keyof Database },
  TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
    ? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] &
        Database[PublicTableNameOrOptions["schema"]]["Views"])
    : never = never,
> = PublicTableNameOrOptions extends { schema: keyof Database }
  ? (Database[PublicTableNameOrOptions["schema"]]["Tables"] &
      Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends {
      Row: infer R
    }
    ? R
    : never
  : PublicTableNameOrOptions extends keyof (PublicSchema["Tables"] &
        PublicSchema["Views"])
    ? (PublicSchema["Tables"] &
        PublicSchema["Views"])[PublicTableNameOrOptions] extends {
        Row: infer R
      }
      ? R
      : never
    : never

export type TablesInsert<
  PublicTableNameOrOptions extends
    | keyof PublicSchema["Tables"]
    | { schema: keyof Database },
  TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
    ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"]
    : never = never,
> = PublicTableNameOrOptions extends { schema: keyof Database }
  ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends {
      Insert: infer I
    }
    ? I
    : never
  : PublicTableNameOrOptions extends keyof PublicSchema["Tables"]
    ? PublicSchema["Tables"][PublicTableNameOrOptions] extends {
        Insert: infer I
      }
      ? I
      : never
    : never

export type TablesUpdate<
  PublicTableNameOrOptions extends
    | keyof PublicSchema["Tables"]
    | { schema: keyof Database },
  TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
    ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"]
    : never = never,
> = PublicTableNameOrOptions extends { schema: keyof Database }
  ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends {
      Update: infer U
    }
    ? U
    : never
  : PublicTableNameOrOptions extends keyof PublicSchema["Tables"]
    ? PublicSchema["Tables"][PublicTableNameOrOptions] extends {
        Update: infer U
      }
      ? U
      : never
    : never

export type Enums<
  PublicEnumNameOrOptions extends
    | keyof PublicSchema["Enums"]
    | { schema: keyof Database },
  EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database }
    ? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"]
    : never = never,
> = PublicEnumNameOrOptions extends { schema: keyof Database }
  ? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName]
  : PublicEnumNameOrOptions extends keyof PublicSchema["Enums"]
    ? PublicSchema["Enums"][PublicEnumNameOrOptions]
    : never

export type CompositeTypes<
  PublicCompositeTypeNameOrOptions extends
    | keyof PublicSchema["CompositeTypes"]
    | { schema: keyof Database },
  CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
    schema: keyof Database
  }
    ? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
    : never = never,
> = PublicCompositeTypeNameOrOptions extends { schema: keyof Database }
  ? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
  : PublicCompositeTypeNameOrOptions extends keyof PublicSchema["CompositeTypes"]
    ? PublicSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
    : never

To Reproduce

  1. Create a custom schema with one or more tables
  2. Install the Supabase CLI v2.0.0
  3. Link the Supabase CLI to the Supabase project with the custom schema
  4. Run the typescript type generation tool with the --schema argument referencing the custom schema
  5. View the output file, database.d.ts, and scroll to the bottom (after the custom schema is defined – see code sample above)

Expected behavior

It appears the tool should be outputting something more like this:

export type Json =
  | string
  | number
  | boolean
  | null
  | { [key: string]: Json | undefined }
  | Json[]

export type Database = {
  api: {
    Tables: {
      ... // Tables in api schema
    }
    Enums: {
      [_ in never]: never
    }
    CompositeTypes: {
      [_ in never]: never
    }
  }
}

// The correct custom schema, "api", is used ✅
type ApiSchema = Database[Extract<keyof Database, "api">]

// The types below correctly name and reference the custom schema ✅
export type Tables<
  ApiTableNameOrOptions extends
    | keyof (ApiSchema["Tables"] & ApiSchema["Views"])
    | { schema: keyof Database },
  TableName extends ApiTableNameOrOptions extends { schema: keyof Database }
    ? keyof (Database[ApiTableNameOrOptions["schema"]]["Tables"] &
        Database[ApiTableNameOrOptions["schema"]]["Views"])
    : never = never,
> = ApiTableNameOrOptions extends { schema: keyof Database }
  ? (Database[ApiTableNameOrOptions["schema"]]["Tables"] &
      Database[ApiTableNameOrOptions["schema"]]["Views"])[TableName] extends {
      Row: infer R
    }
    ? R
    : never
  : ApiTableNameOrOptions extends keyof (ApiSchema["Tables"] &
        ApiSchema["Views"])
    ? (ApiSchema["Tables"] &
        ApiSchema["Views"])[ApiTableNameOrOptions] extends {
        Row: infer R
      }
      ? R
      : never
    : never

export type TablesInsert<
  ApiTableNameOrOptions extends
    | keyof ApiSchema["Tables"]
    | { schema: keyof Database },
  TableName extends ApiTableNameOrOptions extends { schema: keyof Database }
    ? keyof Database[ApiTableNameOrOptions["schema"]]["Tables"]
    : never = never,
> = ApiTableNameOrOptions extends { schema: keyof Database }
  ? Database[ApiTableNameOrOptions["schema"]]["Tables"][TableName] extends {
      Insert: infer I
    }
    ? I
    : never
  : ApiTableNameOrOptions extends keyof ApiSchema["Tables"]
    ? ApiSchema["Tables"][ApiTableNameOrOptions] extends {
        Insert: infer I
      }
      ? I
      : never
    : never

export type TablesUpdate<
  ApiTableNameOrOptions extends
    | keyof ApiSchema["Tables"]
    | { schema: keyof Database },
  TableName extends ApiTableNameOrOptions extends { schema: keyof Database }
    ? keyof Database[ApiTableNameOrOptions["schema"]]["Tables"]
    : never = never,
> = ApiTableNameOrOptions extends { schema: keyof Database }
  ? Database[ApiTableNameOrOptions["schema"]]["Tables"][TableName] extends {
      Update: infer U
    }
    ? U
    : never
  : ApiTableNameOrOptions extends keyof ApiSchema["Tables"]
    ? ApiSchema["Tables"][ApiTableNameOrOptions] extends {
        Update: infer U
      }
      ? U
      : never
    : never

export type Enums<
  ApiEnumNameOrOptions extends
    | keyof ApiSchema["Enums"]
    | { schema: keyof Database },
  EnumName extends ApiEnumNameOrOptions extends { schema: keyof Database }
    ? keyof Database[ApiEnumNameOrOptions["schema"]]["Enums"]
    : never = never,
> = ApiEnumNameOrOptions extends { schema: keyof Database }
  ? Database[ApiEnumNameOrOptions["schema"]]["Enums"][EnumName]
  : ApiEnumNameOrOptions extends keyof ApiSchema["Enums"]
    ? ApiSchema["Enums"][ApiEnumNameOrOptions]
    : never

export type CompositeTypes<
  ApiCompositeTypeNameOrOptions extends
    | keyof ApiSchema["CompositeTypes"]
    | { schema: keyof Database },
  CompositeTypeName extends ApiCompositeTypeNameOrOptions extends {
    schema: keyof Database
  }
    ? keyof Database[ApiCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
    : never = never,
> = ApiCompositeTypeNameOrOptions extends { schema: keyof Database }
  ? Database[ApiCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
  : ApiCompositeTypeNameOrOptions extends keyof ApiSchema["CompositeTypes"]
    ? ApiSchema["CompositeTypes"][ApiCompositeTypeNameOrOptions]
    : never

Note that if no --schema arg is provided to the CLI, the schema defaults to the public schema and these type definitions are correct; it's only when a custom schema , such as the api one, is provided that the type definitions appear to go out of sync.

System information

  • Version of Supabase CLI: 2.0.0

Additional context

I updated to the current version of the Supabase CLI (2.0.0) prior to writing this up, but the issue goes back at least as far as 1.148.6.

greenstick avatar Dec 13 '24 07:12 greenstick

Hi, I've moved this issue over from the supabase repo

Hallidayo avatar May 06 '25 16:05 Hallidayo