TechLead
Lesson 11 of 22
5 min read
Supabase

Supabase with TypeScript

Build type-safe Supabase applications with generated types, typed queries, and generic helpers

Type-Safe Supabase Development

TypeScript and Supabase work together seamlessly. The Supabase CLI can generate TypeScript types directly from your database schema, giving you autocomplete, compile-time checks, and confidence that your queries match your actual tables and columns.

🚀 Benefits of Typed Supabase

  • Autocomplete: IDE suggestions for table names, columns, and filters
  • Compile-time safety: Catch typos and schema mismatches before runtime
  • Refactoring: Rename columns with confidence across your codebase
  • Documentation: Types serve as living documentation of your schema

Generating Types from Your Database

# Install the Supabase CLI
npm install -D supabase

# Login to Supabase
npx supabase login

# Generate types from your remote database
npx supabase gen types typescript \
  --project-id your-project-id \
  --schema public \
  > src/types/database.types.ts

# Or from a local database
npx supabase gen types typescript --local > src/types/database.types.ts

The Generated Database Type

// database.types.ts (auto-generated)
export type Database = {
  public: {
    Tables: {
      posts: {
        Row: {
          id: string
          title: string
          content: string | null
          user_id: string
          created_at: string
        }
        Insert: {
          id?: string
          title: string
          content?: string | null
          user_id: string
          created_at?: string
        }
        Update: {
          id?: string
          title?: string
          content?: string | null
          user_id?: string
          created_at?: string
        }
      }
    }
    Functions: {
      get_user_stats: {
        Args: { user_id: string }
        Returns: { post_count: number; comment_count: number }
      }
    }
  }
}

Creating a Typed Supabase Client

import { createClient } from '@supabase/supabase-js'
import type { Database } from '@/types/database.types'

// Pass the Database type to createClient
export const supabase = createClient<Database>(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

// Now all queries are fully typed!
const { data } = await supabase
  .from('posts')         // autocomplete for table names
  .select('id, title')   // autocomplete for column names
  .eq('user_id', userId) // type-checked filter values

// data is typed as Pick<Post, 'id' | 'title'>[]

Helper Types for Convenience

import type { Database } from '@/types/database.types'

// Shorthand types for your tables
type Tables = Database['public']['Tables']

type Post = Tables['posts']['Row']
type PostInsert = Tables['posts']['Insert']
type PostUpdate = Tables['posts']['Update']

// Use in your components and functions
async function createPost(post: PostInsert) {
  const { data, error } = await supabase
    .from('posts')
    .insert(post)
    .select()
    .single()

  return data // typed as Post
}

// Type-safe RPC calls
const { data } = await supabase.rpc('get_user_stats', {
  user_id: 'some-uuid' // Args type is enforced
})
// data is typed as { post_count: number; comment_count: number }

⚠️ Keep Types in Sync

Re-generate types every time you change your database schema. Add npx supabase gen types to your CI pipeline or create a script in package.json: "gen:types": "supabase gen types typescript --project-id $PROJECT_ID > src/types/database.types.ts"

💡 Key Takeaways

  • • Generate types with supabase gen types typescript
  • • Pass the Database type to createClient for full type safety
  • • Create helper types like Post, PostInsert, PostUpdate
  • • Re-generate types whenever your schema changes
  • • Typed RPC calls catch argument mismatches at compile time

📚 Learn More

Continue Learning