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”):
import { text } from "drizzle-orm/pg-core";

export const TableA = pgTable("a", {
id: serial("id").primaryKey(),
items: text("items").array().notNull().default([]),
});
import { text } from "drizzle-orm/pg-core";

export const TableA = pgTable("a", {
id: serial("id").primaryKey(),
items: text("items").array().notNull().default([]),
});
- Column: text[] - Empty value renders as [] - Non-empty renders as ["foo","bar"] in some tools Drizzle schema B (explicit SQL default):
import { text, sql } from "drizzle-orm/pg-core";

export const TableB = pgTable("b", {
id: serial("id").primaryKey(),
items: text("items").$type<string[]>().notNull().default(sql`ARRAY[]::text[]`),
});
import { text, sql } from "drizzle-orm/pg-core";

export const TableB = pgTable("b", {
id: serial("id").primaryKey(),
items: text("items").$type<string[]>().notNull().default(sql`ARRAY[]::text[]`),
});
- Column:
- Empty renders as
- Empty renders as
{}
- Non-empty renders as
- Non-empty renders as
{"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
No replies yetBe the first to reply to this messageJoin

Did you find this page helpful?