Can we fix this problem
Title
text[].array() default renders inconsistently ({} vs []), causing confusion in tooling and dev UX
Summary
When defining Postgres text[] columns with Drizzle, two equivalent schema styles produce the same underlying type (text[]) but different default literal/rendering:
- Using text().array().default([]) tends to show [] and ["a"] in many UIs
- Using text().$type<string[]>().default(sqlARRAY[]::text[]) shows {} and {"a"} (native PG array literal)
Both are valid Postgres arrays, but the mismatched default literal and display leads developers to think their data is JSON vs Postgres arrays, or worse: that arrays are being stringified. This causes debugging churn and incorrect assumptions about what’s persisted.
Environment
- DB: Postgres (managed and self-hosted; behavior identical)
- Drizzle ORM: pg-core (latest)
- Node: current LTS
- Observed across multiple DB viewers (DBeaver, TablePlus, Supabase UI, pgAdmin) and psql
Minimal Repro
Drizzle schema A (Drizzle “array helper”):
- Column: text[]
- Empty value renders as []
- Non-empty renders as ["foo","bar"] in some tools
Drizzle schema B (explicit SQL default):
- Column: {}{"foo","bar"}```(native PG literal)
Notes
- pg_typeof(items) = text[] in both cases. The difference is purely how defaults/values are rendered by tools.
- The “{}” display (native) makes many devs think they’ve stored strings or broken arrays, especially when mixing with JSON fields or when inspecting via network devtools.
Why this matters
- Arrays in JSON and Postgres use different literal syntaxes (JSON: [], SQL: {}). When UIs flip between them depending on how defaults were defined, it’s easy to mistake a healthy text[] for a broken string or vice versa.
- Developers waste time “fixing” code that works, and may implement unnecessary normalization layers.
Requested improvements
Any of the following would reduce confusion:
1.
Make text().array().default([]) emit a canonical SQL default ARRAY[]::text[] in migrations, but also ensure Drizzle’s generated SQL or metadata hints nudge UIs to display JSON-like [] consistently (if feasible).
2.
Provide a consistent, documented recommendation:
- “Use text().array().default([]) for text[] to get JSON-like display ([]) in most tools.”
- Or “Always use ARRAY[]::text[] as the default; the {} display is expected.”
1. Add a helper for array defaults that emits canonical SQL and improves readability:
arrayText("items", { defaultEmpty: true }) // expands to text[].notNull().default(ARRAY[]::text[])
1. Document a “view as JSON” tip in Drizzle docs:
- SELECT to_jsonb(items) AS items_json FROM … to get []/["a"] during debugging.
What we’re not asking
- Not asking to change Postgres’ native literal ({}). Only asking for Drizzle to pick one defaulting approach and document it so that the developer experience is consistent and predictable.
Impact
- Reduces false-positive bug hunts.
- Makes schema/code reviews clearer.
- Aligns expectations for developers coming from JSON-first ecosystems.
Thanks!
0 Replies