Import, Export & Handoff
Import a Postgres schema
The Database Visualizer is not Bubble-only. Paste in a Postgres schema from Supabase, a migration file or a pg_dump, and you get the same diagram.
What is supported
Postgres only. MySQL, SQL Server and SQLite dialects are not parsed.
Upload a .sql file, or paste the SQL into the Postgres / SQL panel on the Database Visualizer. Uploads are routed by extension: .sql goes to the SQL parser, .json and .bubble go to the Bubble JSON parser, and any other extension is refused before the file is read. Pasted text has no filename, so it is routed by what is in it instead.
What gets read
| Construct | Becomes |
|---|---|
CREATE TABLE | A data type |
CREATE TYPE … AS ENUM | An option set |
REFERENCES on a column | A one-to-one relationship |
ALTER TABLE … ADD CONSTRAINT … FOREIGN KEY | A one-to-one relationship |
CHECK (col IN ('a','b')) | An option set named <table>_<column> |
A <name>_options table with value and label columns | An option set |
NOT NULL, UNIQUE | Kept for the round trip back out to SQL |
type[] | A list field |
INSERT INTO … VALUES | Option set values |
Column types are mapped onto Bubble's vocabulary: booleans become boolean, timestamps and dates become date, all numeric types become number, and everything else including uuid, jsonb and varchar becomes text.
id, created_at and updated_at columns are suppressed as fields, because Bubble creates its own equivalents.
Three ways an option set is recognised
This trips people up, so it is worth stating explicitly. A fixed set of choices in Postgres can be modelled at least three ways, and LumiDevKit understands all three:
- A native
CREATE TYPE … AS ENUM. - An inline
CHECK (status IN ('draft','published')). - A lookup table whose name ends in
_optionsand which has both avalueand alabelcolumn.
The lumidevkit: markers
Some intent cannot be recovered from SQL alone, so LumiDevKit's own SQL export writes comment markers that it reads back on import:
COMMENT ON TABLE priority_options IS 'lumidevkit:option_set';
COMMENT ON TABLE tasks_watchers IS 'lumidevkit:join';
COMMENT ON TABLE profiles IS 'lumidevkit:user';
COMMENT ON COLUMN tasks.owner_id IS 'lumidevkit:display=Owner';
The join marker is the significant one. A junction table is folded back into a list field on its owner only when it is explicitly marked. Unmarked two-foreign-key tables are kept as data types on purpose: a table like votes or memberships with its own surrogate id is indistinguishable from a pure join table once it has been parsed, and keeping it is the lossless choice.
What is dropped by design
Row-level security policies, triggers, functions, indexes, views, sequences, extensions, DEFAULT values, numeric precision, ON DELETE behaviour, composite primary keys and schema qualification are all discarded. The diagram models entities and relationships, not a full database.
Import notes
Where the parser makes a judgement call, it tells you underneath the drop zone:
- Treated "X" as an option set (looks like a lookup table).
- Folded join table "X" into a list field on "Y".
- Skipped join table "X" (needs two foreign keys).
- Skipped join table "X" (owner "Y" not found).
Read them. They are the difference between a diagram you can trust and one you assume is right.
If it fails
You get "Could not parse SQL: …" with the underlying parser's message and a line number. The usual causes are a non-Postgres dialect, or a file that is a data dump rather than a schema.
Going the other way
See export your schema for turning a Bubble schema into Postgres.