> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chartdb.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Import Supabase

> Import your Supabase PostgreSQL database schema into ChartDB using SQL queries

<Info>
  **Want automatic sync?** Use the [Supabase Integration](/docs/integrations/supabase) for one-click OAuth authentication and automatic hourly schema sync.
</Info>

[Supabase](https://supabase.com) is an open-source Firebase alternative that provides a PostgreSQL database with built-in authentication, storage, and real-time subscriptions. This guide covers the manual SQL import method for Supabase databases.

<Note>
  ChartDB never stores or accesses your data - the import query only retrieves schema metadata. The query automatically excludes Supabase system schemas (`auth`, `extensions`, `pgsodium`, `realtime`, `storage`, `vault`) to show only your application tables.
</Note>

## Quick Start

<Steps>
  <Step title="Create New Diagram">
    1. Go to [ChartDB](https://app.chartdb.io/)
    2. Click **File** > **New** in the editor
    3. Select **PostgreSQL** from the database options
    4. Choose **Supabase** as the edition
  </Step>

  <Step title="Open Supabase SQL Editor">
    1. Log in to your [Supabase Dashboard](https://supabase.com/dashboard)
    2. Select your project
    3. Navigate to **SQL Editor** in the left sidebar
    4. Click **New query**
  </Step>

  <Step title="Run Import Query">
    Copy the import query from ChartDB and paste it into the Supabase SQL Editor. Click **Run** to execute.
  </Step>

  <Step title="Copy Results">
    1. After the query runs, you'll see a JSON result
    2. Click on the result cell
    3. Click **Copy cell content** or use the copy button
  </Step>

  <Step title="Import to ChartDB">
    1. Go back to ChartDB
    2. Paste the JSON into the import field
    3. Click **Import** to generate your diagram
  </Step>
</Steps>

## Import Query

Run this query in your Supabase SQL Editor:

<Accordion title="Supabase Import Query" icon="database">
  ```sql theme={null}
  -- Supabase PostgreSQL Database Import Query
  WITH fk_info_supabase AS (
      SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name, '"', ''), '"',
                                              ',"table":"', replace(table_name::text, '"', ''), '"',
                                              ',"column":"', replace(fk_column::text, '"', ''), '"',
                                              ',"foreign_key_name":"', foreign_key_name, '"',
                                              ',"reference_schema":"', COALESCE(reference_schema, 'public'), '"',
                                              ',"reference_table":"', reference_table, '"',
                                              ',"reference_column":"', reference_column, '"',
                                              ',"fk_def":"', replace(fk_def, '"', ''),
                                              '"}')), ',') as fk_metadata
      FROM (
              SELECT c.conname AS foreign_key_name,
                      n.nspname AS schema_name,
                      CASE
                          WHEN position('.' in conrelid::regclass::text) > 0
                          THEN split_part(conrelid::regclass::text, '.', 2)
                          ELSE conrelid::regclass::text
                      END AS table_name,
                      a.attname AS fk_column,
                      nr.nspname AS reference_schema,
                      CASE
                          WHEN position('.' in confrelid::regclass::text) > 0
                          THEN split_part(confrelid::regclass::text, '.', 2)
                          ELSE confrelid::regclass::text
                      END AS reference_table,
                      af.attname AS reference_column,
                      pg_get_constraintdef(c.oid) as fk_def
                  FROM
                      pg_constraint AS c
                  JOIN
                      pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
                  JOIN
                      pg_class AS cl ON cl.oid = c.conrelid
                  JOIN
                      pg_namespace AS n ON n.oid = cl.relnamespace
                  JOIN
                      pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid
                  JOIN
                      pg_class AS clf ON clf.oid = c.confrelid
                  JOIN
                      pg_namespace AS nr ON nr.oid = clf.relnamespace
                  WHERE
                      c.contype = 'f'
                      AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
                  AND connamespace::regnamespace::text NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')

      ) AS x
  ), pk_info AS (
      SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name, '"', ''), '"',
                                              ',"table":"', replace(pk_table, '"', ''), '"',
                                              ',"column":"', replace(pk_column, '"', ''), '"',
                                              ',"pk_def":"', replace(pk_def, '"', ''),
                                              '"}')), ',') AS pk_metadata
      FROM (
              SELECT connamespace::regnamespace::text AS schema_name,
                  CASE
                      WHEN strpos(conrelid::regclass::text, '.') > 0
                      THEN split_part(conrelid::regclass::text, '.', 2)
                      ELSE conrelid::regclass::text
                  END AS pk_table,
                  unnest(string_to_array(substring(pg_get_constraintdef(oid) FROM '\((.*?)\)'), ',')) AS pk_column,
                  pg_get_constraintdef(oid) as pk_def
              FROM
                pg_constraint
              WHERE
                contype = 'p'
                AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
                  AND connamespace::regnamespace::text NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')

      ) AS y
  ),
  indexes_cols AS (
      SELECT  tnsp.nspname                                                                AS schema_name,
          trel.relname                                                                    AS table_name,
              pg_relation_size('"' || tnsp.nspname || '".' || '"' || irel.relname || '"') AS index_size,
              irel.relname                                                                AS index_name,
              am.amname                                                                   AS index_type,
              a.attname                                                                   AS col_name,
              (CASE WHEN i.indisunique = TRUE THEN 'true' ELSE 'false' END)               AS is_unique,
              irel.reltuples                                                              AS cardinality,
              1 + Array_position(i.indkey, a.attnum)                                      AS column_position,
              CASE o.OPTION & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END                         AS direction,
              CASE WHEN indpred IS NOT NULL THEN 'true' ELSE 'false' END                  AS is_partial_index
      FROM pg_index AS i
          JOIN pg_class AS trel ON trel.oid = i.indrelid
          JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
          JOIN pg_class AS irel ON irel.oid = i.indexrelid
          JOIN pg_am AS am ON irel.relam = am.oid
          CROSS JOIN LATERAL unnest (i.indkey)
          WITH ORDINALITY AS c (colnum, ordinality) LEFT JOIN LATERAL unnest (i.indoption)
          WITH ORDINALITY AS o (option, ordinality)
          ON c.ordinality = o.ordinality JOIN pg_attribute AS a ON trel.oid = a.attrelid AND a.attnum = c.colnum
      WHERE tnsp.nspname NOT LIKE 'pg_%'
      GROUP BY tnsp.nspname, trel.relname, irel.relname, am.amname, i.indisunique, i.indexrelid, irel.reltuples, a.attname, Array_position(i.indkey, a.attnum), o.OPTION, i.indpred
  ),
  cols AS (
      SELECT array_to_string(array_agg(CONCAT('{"schema":"', cols.table_schema,
                                              '","table":"', cols.table_name,
                                              '","name":"', cols.column_name,
                                              '","ordinal_position":"', cols.ordinal_position,
                                              '","type":"', LOWER(replace(cols.data_type, '"', '')),
                                              '","character_maximum_length":"', COALESCE(cols.character_maximum_length::text, 'null'),
                                              '","precision":',
                                                  CASE
                                                      WHEN cols.data_type = 'numeric' OR cols.data_type = 'decimal'
                                                      THEN CONCAT('{"precision":', COALESCE(cols.numeric_precision::text, 'null'),
                                                                  ',"scale":', COALESCE(cols.numeric_scale::text, 'null'), '}')
                                                      ELSE 'null'
                                                  END,
                                              ',"nullable":', CASE WHEN (cols.IS_NULLABLE = 'YES') THEN 'true' ELSE 'false' END,
                                              ',"default":"', COALESCE(replace(replace(cols.column_default, '"', '\"'), '\x', '\\x'), ''),
                                              '","collation":"', COALESCE(cols.COLLATION_NAME, ''),
                                              '","comment":"', COALESCE(replace(replace(dsc.description, '"', '\"'), '\x', '\\x'), ''),
                                              '"}')), ',') AS cols_metadata
      FROM information_schema.columns cols
      LEFT JOIN pg_catalog.pg_class c
          ON c.relname = cols.table_name
      JOIN pg_catalog.pg_namespace n
          ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
      LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
                                          AND dsc.objsubid = cols.ordinal_position
      WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
                  AND cols.table_schema NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')

  ), indexes_metadata AS (
      SELECT array_to_string(array_agg(CONCAT('{"schema":"', schema_name,
                                              '","table":"', table_name,
                                              '","name":"', index_name,
                                              '","column":"', replace(col_name :: TEXT, '"', E'"'),
                                              '","index_type":"', index_type,
                                              '","cardinality":', cardinality,
                                              ',"size":', index_size,
                                              ',"unique":', is_unique,
                                              ',"is_partial_index":', is_partial_index,
                                              ',"column_position":', column_position,
                                              ',"direction":"', LOWER(direction),
                                              '"}')), ',') AS indexes_metadata
      FROM indexes_cols x
                  WHERE schema_name NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')

  ), tbls AS (
      SELECT array_to_string(array_agg(CONCAT('{',
                          '"schema":"', tbls.TABLE_SCHEMA, '",',
                          '"table":"', tbls.TABLE_NAME, '",',
                          '"rows":', COALESCE((SELECT s.n_live_tup
                                                  FROM pg_stat_user_tables s
                                                  WHERE tbls.TABLE_SCHEMA = s.schemaname AND tbls.TABLE_NAME = s.relname),
                                                  0), ', "type":"', tbls.TABLE_TYPE, '",', '"engine":"",', '"collation":"",',
                          '"comment":"', COALESCE(replace(replace(dsc.description, '"', '\"'), '\x', '\\x'), ''),
                          '"}'
                  )),
                  ',') AS tbls_metadata
          FROM information_schema.tables tbls
          LEFT JOIN pg_catalog.pg_class c ON c.relname = tbls.TABLE_NAME
          JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
                                              AND n.nspname = tbls.TABLE_SCHEMA
          LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
                                                  AND dsc.objsubid = 0
          WHERE tbls.TABLE_SCHEMA NOT IN ('information_schema', 'pg_catalog')
                  AND tbls.table_schema NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')

  ), config AS (
      SELECT array_to_string(
                        array_agg(CONCAT('{"name":"', conf.name, '","value":"', replace(conf.setting, '"', E'"'), '"}')),
                        ',') AS config_metadata
      FROM pg_settings conf
  ), views AS (
      SELECT array_to_string(array_agg(CONCAT('{"schema":"', views.schemaname,
                        '","view_name":"', viewname,
                        '","view_definition":"', encode(convert_to(REPLACE(definition, '"', '\"'), 'UTF8'), 'base64'),
                      '"}')),
                        ',') AS views_metadata
      FROM pg_views views
      WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
                  AND views.schemaname NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')

  )
  SELECT CONCAT('{    "fk_info": [', COALESCE(fk_metadata, ''),
                      '], "pk_info": [', COALESCE(pk_metadata, ''),
                      '], "columns": [', COALESCE(cols_metadata, ''),
                      '], "indexes": [', COALESCE(indexes_metadata, ''),
                      '], "tables":[', COALESCE(tbls_metadata, ''),
                      '], "views":[', COALESCE(views_metadata, ''),
                      '], "database_name": "', CURRENT_DATABASE(), '', '", "version": "', '',
                '"}') AS metadata_json_to_import
  FROM fk_info_supabase, pk_info, cols, indexes_metadata, tbls, config, views;
  ```
</Accordion>

## Connection Details (Alternative Method)

If you prefer to connect using a database client like pgAdmin or DBeaver, you can find your Supabase connection credentials:

<Steps>
  <Step title="Navigate to Database Settings">
    1. Go to your [Supabase Dashboard](https://supabase.com/dashboard)
    2. Select your project
    3. Click **Project Settings** (gear icon) in the sidebar
    4. Navigate to **Database**
  </Step>

  <Step title="Find Connection Parameters">
    Under **Connection parameters**, you'll find:

    * **Host**: `db.[project-ref].supabase.co`
    * **Database name**: `postgres`
    * **Port**: `5432`
    * **User**: `postgres`
    * **Password**: Your database password (set during project creation)

    <Warning>
      Make sure to use the **Direct connection** string, not the pooled connection. The direct connection is required for schema introspection queries.
    </Warning>
  </Step>

  <Step title="Run Query via psql">
    You can also use the command line to import:

    ```bash theme={null}
    psql "postgresql://postgres:[YOUR-PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres" -c "
    [PASTE IMPORT QUERY HERE]
    " -t -A
    ```
  </Step>
</Steps>

## Excluded Schemas

The Supabase import query automatically excludes these internal schemas to keep your diagram focused on your application tables:

| Schema       | Description                    |
| ------------ | ------------------------------ |
| `auth`       | Supabase Authentication tables |
| `extensions` | PostgreSQL extensions          |
| `pgsodium`   | Encryption utilities           |
| `realtime`   | Real-time subscription system  |
| `storage`    | File storage system            |
| `vault`      | Secrets management             |

<Note>
  If you need to visualize these system schemas, use the standard [PostgreSQL import](/docs/import/postgresql) query instead.
</Note>

## Troubleshooting

<CardGroup cols={2}>
  <Card title="Common Issues" icon="circle-exclamation" href="/docs/troubleshooting/common-issues">
    Solutions for frequently encountered import problems
  </Card>

  <Card title="PostgreSQL Import" icon="database" href="/docs/import/postgresql">
    Full PostgreSQL documentation including alternative import methods
  </Card>
</CardGroup>
