DEV Community

Cover image for You Don't Need MongoDB for "Flexible" Data. You Need One Column Type.
Chizee
Chizee

Posted on Originally published at omenabyte.com

You Don't Need MongoDB for "Flexible" Data. You Need One Column Type.

Product catalogs, form submissions, webhook payloads — a lot of real data genuinely doesn't want a fixed schema. The usual response is reaching for MongoDB purely for that flexibility, which means a second connection pool, a second backup strategy, and a second place your data can quietly drift out of sync with the relational tables that still describe your users and orders.

jsonb solves the actual problem — flexible structure — without giving up the database you already trust for everything else.

Store anything

CREATE TABLE products (
  id          bigserial PRIMARY KEY,
  name        text NOT NULL,
  attributes  jsonb NOT NULL DEFAULT '{}'
);

CREATE INDEX idx_products_attributes
  ON products USING GIN (attributes);
Enter fullscreen mode Exit fullscreen mode

Use jsonb, not json. The binary form is stored decomposed at write time instead of as text re-parsed on every read — that's the difference between something you can actually index and something you can only display.

Query it like you mean it

SELECT name FROM products
WHERE attributes @> '{"color": "red", "size": "M"}';
Enter fullscreen mode Exit fullscreen mode

The GIN index makes this fast the same way a book's index works: it maps individual keys and values straight to the rows that contain them, instead of scanning every row to check.

The part MongoDB genuinely can't do as cleanly

SELECT p.name, o.order_date
FROM products p
JOIN order_items oi ON oi.product_id = p.id
JOIN orders o ON o.id = oi.order_id
WHERE p.attributes->'specs'->>'material' = 'leather';
Enter fullscreen mode Exit fullscreen mode

That's a document-style attribute joined directly against relational order history, in one statement, inside one transaction. Try that across two databases and you're writing application-layer stitching code — and hoping nothing changes between the two calls.

Why this beats standing up a document database

  • One transaction, not two systems — the "document" and the relational rows referencing it commit or roll back together.
  • No sync jobs, because there's no second database to keep consistent.
  • You can still join it, which is the thing you actually lose by going full-document.

Where MongoDB still wins

Genuinely schema-less workloads at a write scale where horizontal partitioning across many nodes is a day-one requirement, or built-in multi-region active-active replication out of the box. Most projects reaching for Mongo on day one aren't actually at that scale yet.


This is one of eight infrastructure swaps in Just Use Postgres, a 24-page field manual on replacing MongoDB, Redis, Elasticsearch, Pinecone, and more with the database you're probably already running. Every recipe in it — including this one — was run against a live Postgres instance before it went in the book.

👉 Get the field manual — launch price $14 instead of multiplying connection pools, backup jobs, and consistency headaches: https://payhip.com/omenabyte
🐳 Or take the $24 bundle with the full docker-compose up starter repo — all 8 modules as tested, runnable migrations + seed data: https://4693433176360.gumroad.com/

Read this on omenabyte.com → https://omenabyte.com/blog/replace-mongodb-with-jsonb

Top comments (0)