TypeScript Type Generation for Self-Hosted Supabase: A Complete Guide

Learn how to generate TypeScript types from your self-hosted Supabase database for type-safe development.

Cover Image for TypeScript Type Generation for Self-Hosted Supabase: A Complete Guide

One of Supabase's best developer experience features is automatic TypeScript type generation. Your database schema becomes TypeScript types, giving you autocomplete, compile-time checks, and fewer runtime errors. But if you're running self-hosted Supabase, this feature requires some extra configuration that isn't well documented.

The Supabase CLI's gen types command was designed primarily for Supabase Cloud, where you can link your project with an access token. For self-hosted instances, you need to use the --db-url flag—but getting it working correctly involves understanding Docker networking, connection strings, and a few gotchas the documentation glosses over.

This guide covers everything you need to know about generating TypeScript types from your self-hosted Supabase database.

Why Type Generation Matters

Before diving into the how, let's quickly cover the why. Consider this query without types:

const { data } = await supabase
  .from('users')
  .select('naem, email'); // Typo: "naem" instead of "name"

Without generated types, this compiles and runs—then fails silently at runtime. With proper types, your IDE catches the typo immediately.

Generated types also provide:

  • Autocomplete for table names, column names, and function parameters
  • Return type inference so you know exactly what shape your data has
  • Refactoring safety when you rename columns or change schemas
  • Documentation through type definitions that describe your database structure

For teams building production applications on self-hosted Supabase, type generation is essential for maintaining code quality.

The Basic Command

The Supabase CLI provides the gen types command for generating TypeScript definitions:

npx supabase gen types typescript --db-url "postgres://..." > database.types.ts

Simple enough, right? The challenge is getting the connection string correct for your self-hosted setup.

Generating Types from Local Development

If you're running Supabase locally with supabase start, generating types is straightforward:

npx supabase gen types typescript --local > src/database.types.ts

The --local flag tells the CLI to connect to your locally running Supabase stack. No connection string needed.

This is the approach recommended in our local development workflow guide. Generate types from your local instance, commit them to version control, and ensure they stay in sync with your migrations.

Generating Types from Self-Hosted Production

For self-hosted production instances, you'll use the --db-url flag. The connection string format is:

postgres://[user]:[password]@[host]:[port]/[database]

Here's a complete example:

npx supabase gen types typescript \
  --db-url "postgres://postgres:[email protected]:5432/postgres" \
  > src/database.types.ts

Understanding the Connection

Several parts of this connection string deserve attention:

User: Usually postgres unless you've configured different roles. For database security best practices, you might have a read-only role for type generation.

Password: Your PostgreSQL password from your environment configuration. Escape special characters in the URL.

Host: Your server's domain or IP address. If you're using a reverse proxy, this is typically your public domain.

Port: The PostgreSQL port—usually 5432. Note that if you're using Supavisor for connection pooling, you should connect directly to PostgreSQL for type generation, not through the pooler.

The Docker Networking Gotcha

If you're running the Supabase CLI from inside Docker (common in CI/CD pipelines), you can't use localhost to reach your database container. Instead, use the Docker network hostname or host.docker.internal:

# From inside Docker, connecting to host machine
npx supabase gen types typescript \
  --db-url "postgres://postgres:[email protected]:5432/postgres" \
  > database.types.ts

This is a common source of "connection refused" errors when setting up CI/CD pipelines.

Schema Selection

By default, type generation includes only the public schema. If you're using other schemas (common in multi-tenant architectures), specify them:

npx supabase gen types typescript \
  --db-url "postgres://..." \
  --schema public,auth,storage \
  > database.types.ts

Note that generating types from the auth schema exposes internal Supabase tables. This might be useful for advanced use cases but isn't typically necessary.

Automating Type Generation

Manually running type generation gets tedious. Here are several automation approaches.

Package.json Script

Add a script to your package.json:

{
  "scripts": {
    "db:types": "supabase gen types typescript --local > src/database.types.ts",
    "db:types:prod": "supabase gen types typescript --db-url $SUPABASE_DB_URL > src/database.types.ts"
  }
}

Run npm run db:types during development, or npm run db:types:prod when you need to sync with production.

Pre-commit Hook

Generate types automatically before each commit using Husky:

# .husky/pre-commit
#!/bin/sh
npm run db:types
git add src/database.types.ts

This ensures types are always up to date in version control.

GitHub Actions

For teams, generate types in CI when migrations change:

name: Generate Types

on:
  push:
    paths:
      - 'supabase/migrations/**'

jobs:
  generate-types:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Supabase CLI
        uses: supabase/setup-cli@v1
        
      - name: Start local Supabase
        run: supabase start
        
      - name: Apply migrations
        run: supabase db reset
        
      - name: Generate types
        run: supabase gen types typescript --local > src/database.types.ts
        
      - name: Commit types
        uses: stefanzweifel/git-auto-commit-action@v5
        with:
          commit_message: "chore: regenerate database types"
          file_pattern: "src/database.types.ts"

This workflow spins up a local Supabase instance, applies your migrations, generates types, and commits them back to the repository.

Using Generated Types

Once you have your database.types.ts file, configure your Supabase client to use it:

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

const supabase = createClient<Database>(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_ANON_KEY!
);

Now queries are fully typed:

// Type-safe query with autocomplete
const { data: users } = await supabase
  .from('users')  // Autocomplete shows available tables
  .select('id, email, created_at')  // Autocomplete shows columns
  .eq('status', 'active');

// data is typed as { id: string; email: string; created_at: string }[] | null

Type Helpers

The generated types include helper types for common operations:

import { Database } from './database.types';

// Row type for a table
type User = Database['public']['Tables']['users']['Row'];

// Insert type (some fields optional)
type NewUser = Database['public']['Tables']['users']['Insert'];

// Update type (all fields optional)
type UserUpdate = Database['public']['Tables']['users']['Update'];

These helpers are invaluable for typing function parameters and component props.

Troubleshooting Common Issues

Connection Refused

If you get "connection refused" errors:

  1. Verify your database is accessible from wherever the CLI is running
  2. Check firewall rules on your VPS
  3. Confirm the port is correct (5432 for direct Postgres, not 6543 for Supavisor)
  4. Use --debug flag for detailed connection logs
npx supabase gen types typescript --db-url "postgres://..." --debug

SSL Connection Required

For production databases with SSL:

npx supabase gen types typescript \
  --db-url "postgres://user:pass@host:5432/db?sslmode=require" \
  > database.types.ts

Password with Special Characters

URL-encode special characters in your password:

# If password is "p@ss:word/123"
# Encode as "p%40ss%3Aword%2F123"
npx supabase gen types typescript \
  --db-url "postgres://postgres:p%40ss%3Aword%2F123@host:5432/postgres"

Types Don't Match Schema

If generated types don't reflect recent schema changes:

  1. Ensure migrations have been applied to your database
  2. Check you're connecting to the correct database
  3. Verify the schema parameter includes your target schema

Best Practices

Keep Types in Version Control

Commit your database.types.ts file. This ensures:

  • All team members have consistent types
  • You can diff type changes in pull requests
  • CI/CD doesn't need database access to build

Generate from Local During Development

Use --local for development, not production connection strings. This:

  • Avoids exposing production credentials
  • Works without network access
  • Generates types from your latest local migrations

Validate Types in CI

Add a CI step that generates types and fails if they differ from committed types:

npm run db:types
git diff --exit-code src/database.types.ts

This catches situations where someone changed the schema but forgot to regenerate types.

Conclusion

TypeScript type generation transforms the Supabase development experience from guesswork to confidence. Every query is type-checked, every column name autocompletes, and schema changes surface as compile errors rather than production bugs.

For self-hosted Supabase users, the key is understanding that the --db-url flag replaces the cloud-focused supabase link workflow. Once configured correctly, you get the same type-safe development experience as managed Supabase users.

If you're looking for a simpler way to manage your self-hosted Supabase infrastructure—including automated backups, custom domains, and OAuth configuration—check out Supascale. Our management platform handles the operational complexity so you can focus on building your application.

Further Reading