Chapter 1: Good Evening. The Third Service.
Good evening. Thank you for joining me. Whether this is your first visit or your third, you are welcome at this table, and I intend to be worth your time.
Most teams that run MongoDB also run PostgreSQL. The documents live in one database; the reporting, the joins, the transactions live in the other. Between them: a sync pipeline, two sets of backups, two connection pools, and a consistency model that is optimistic at best. Two databases is not twice the capability. It is twice the responsibility.
If you have read the previous books in this series, you will recognize the pattern. The first examined caching and queues — the workloads teams delegate to Redis. The second examined search and aggregation — the workloads teams delegate to Elasticsearch. In both cases, we found that PostgreSQL had been quietly capable for years. What was missing was not the feature but the introduction. If you have not read the earlier books, that single paragraph is all the context you need. The pattern will become clear enough over the next twenty chapters.
This book turns to the third service: the document database. The argument is familiar, because the truth it rests on has not changed. PostgreSQL has stored, indexed, and queried JSON documents natively since version 9.4, released in December 2014. Twelve years. The most underused capability in any database is the one that arrived quietly and never insisted on attention.
Gold Lapel provides the introduction that was overdue. And I am pleased to make it.
Why Teams Add MongoDB
Before this book argues that you do not need MongoDB, it owes you an honest account of why so many thoughtful teams decided that they did. MongoDB was not adopted by accident. It was adopted because it offered the right experience at the right moment, and it is worth understanding why that experience was compelling. Millions of developers made this choice; the courteous thing — and, not coincidentally, the useful thing — is to understand the choice before one reconsiders it.
The appeal begins with the API. A developer building a JSON backend opens the MongoDB documentation and finds this:
db.users.insertOne({ name: "Alice", age: 30, preferences: { theme: "dark" } }) No CREATE TABLE. No column definitions. No migration file. No ORM layer translating between the shape of the data and the shape of the schema. The database accepts JSON and stores it as a document. For applications that already think in JSON — which is most modern applications — MongoDB feels like the database was designed for them. It was. That is not a small achievement, and I do not say so lightly. Designing a database around the developer's actual workflow, rather than asking the developer to reshape their workflow around the database, is a meaningful contribution to this industry. It changed how an entire generation of developers thought about data storage, and the influence is permanent even for teams that ultimately choose a different path.
Semi-structured data compounds the case. User preferences are nested objects. Product catalogs have attributes that vary by category — a shirt has sizes and colors, a laptop has specs and ports, and no single schema describes both. Event payloads differ by event type. Form submissions carry whatever fields the form included that week. This data resists the fixed-column relational schema, and MongoDB's answer — store the document whole, query it whole — is the right shape for the problem. The elegance is genuine, and I have no interest in pretending otherwise.
Schema evolution adds further appeal. A new feature requires a new field. In MongoDB, old documents simply lack it, and new documents include it. No migration. No ALTER TABLE on a table with hundreds of millions of rows while your operations team monitors the lock queue. No deploy-day anxiety over a schema change that touches every row in the table. For teams iterating quickly on a product still finding its shape, this flexibility has real, daily value. It is the difference between shipping a feature today and shipping it after a migration window.
And the managed ecosystem deserves recognition on its own merits. Atlas provides hosting, backups, monitoring, and scaling from a single dashboard. Compass offers visual data exploration. Charts provides built-in visualization. These are not afterthoughts bolted onto a database engine. They are carefully designed tools that reduce the operational burden of running MongoDB in production. A good tool, well integrated, is a form of hospitality toward the developer — and I recognize hospitality when I see it.
Every one of these reasons is legitimate. I have named them not as a courtesy before a rebuttal, but because they are true, and a book that fails to acknowledge what a competitor does well has no standing to discuss what a competitor does not do at all. The question this book raises is narrower than it might first appear. It is not whether MongoDB is good software. It is whether MongoDB is necessary software — specifically, for the team that already runs PostgreSQL and added MongoDB because they needed to store documents.
A service added is a service maintained. The question is never whether the service is excellent. It is whether the service is required.
What It Actually Costs
The Atlas invoice is the cost that appears on the spreadsheet. It is not the cost that keeps your team up at night.
Most teams running MongoDB alongside PostgreSQL have constructed — deliberately or one incident at a time — a dual-database architecture. MongoDB handles the document writes. PostgreSQL handles the reporting, the cross-entity joins, the transactional workflows. Between the two sits a synchronization mechanism: Kafka, Debezium, a custom ETL job, or a script that someone wrote at 2 AM during an outage and nobody has touched since. A sync pipeline, when you examine it honestly, is an admission that your data belongs together but your databases do not know it.
Consider a scenario that most dual-database teams will recognize: a user updates their profile in your application. The write goes to MongoDB. A downstream service reads that profile to send a confirmation email — but it reads from PostgreSQL, through the sync pipeline. The pipeline is three seconds behind. The email goes out with the user's old name. No code is wrong. Every system behaved as designed. The architecture produced a bug that lives in no codebase, and no test suite will catch it, because no test suite models sync pipeline lag.
The full cost of this arrangement extends well beyond that single scenario: two deployments to maintain, two backup strategies to test, two restore procedures to document, two connection pools to tune, two sets of credentials to rotate, two monitoring dashboards to watch, two query languages your team must know, and a consistency model that became eventual the moment the second database arrived. Chapter 2 opens the complete ledger.
For now, I will leave you with a question that I believe deserves an honest answer: did you add a second database because PostgreSQL could not store your documents, or because no one had shown you that it could?
The Equation
Here is the claim this book will demonstrate across twenty chapters, with working code in seven languages:
MongoDB ≈ JSONB + GIN indexes + triggers + LISTEN/NOTIFY
Allow me to introduce each briefly. They will become familiar companions over the course of this book.
JSONB is PostgreSQL's binary JSON storage format. Documents are parsed once on write, stored in a decomposed binary structure, and never re-parsed on read. It has been available since PostgreSQL 9.4, released in December 2014. A capability that has spent twelve years in production across every industry is not experimental. It is infrastructure.
GIN indexes make JSONB documents queryable at the speed your application requires. Containment queries, key-existence checks, and path expressions — all indexed, all optimized by the query planner. A single GIN index on a JSONB column covers queries on every field, at every depth, without the developer declaring which fields to index. The database does not scan your documents looking for matches. It knows where they are.
Triggers provide the operational features that complete the document store. Change streams, TTL-based document expiry, and capped collections are each implemented through PostgreSQL's native trigger mechanism. No polling. No external scheduler. No additional process. The database handles its own housekeeping, as a well-run establishment should.
LISTEN/NOTIFY delivers real-time events. A trigger fires on document change, a notification reaches your application, and your change stream callback executes. The event shape matches MongoDB's change streams. The behavior is real-time. The mechanism is built into the database you already trust.
Gold Lapel provides the developer experience that was missing — the layer that makes these capabilities feel as natural as MongoDB's API. Twenty-one methods across seven languages give you MongoDB's syntax on PostgreSQL:
gl = goldlapel.start("postgres://...")
gl.doc_insert("users", {"name": "Alice", "age": 30})
results = gl.doc_find("users", {"age": {"$gte": 25}}) Same filter syntax. Same aggregation pipeline. Same update operators — $set, $inc, $push, $pull, $addToSet. Same operational features — change streams, TTL indexes, capped collections. The difference between your current MongoDB code and Gold Lapel is one import statement and a connection string. The database underneath is the one you already run, already back up, and already understand.
What you gain by remaining in PostgreSQL is not a marginal improvement. It is a category change.
ACID transactions across collections. Begin a transaction. Insert into orders. Update inventory. Commit. Both succeed or both fail. PostgreSQL has had multi-table transactions for over thirty years. MongoDB added multi-document transactions in 2018, with documented performance penalties and a sixty-second timeout. This is not a close comparison.
SQL JOINs between documents and relational tables. Your JSONB document tables are ordinary PostgreSQL tables. Join them with your user table, your permissions table, your billing table — using the query planner, with full index support. MongoDB's $lookup is limited to same-database collections and does not use indexes on the foreign collection. SQL JOINs have no such constraints.
The full ecosystem. PostGIS for spatial queries on documents that contain location data. pgvector for embeddings stored alongside the documents they describe — similarity search and document storage in the same table, the same query, the same transaction. Full-text search on document fields without a separate search cluster. Row-level security that enforces per-user document access at the database level, not in application middleware.
One backup. pg_dump or pg_basebackup captures everything — relational tables, document collections, indexes, functions, permissions. One command. One point in time. One restore procedure. A backup that covers everything is not a convenience. It is the only kind of backup that deserves the name.
One database to manage, to monitor, and to answer for when the phone rings at 3 AM. Simplicity is not a limitation. It is the result of having enough in one place.
The Scope
A book that argues you do not need something owes you a precise account of when you do. I would rather draw the boundary myself than have you discover it on your own. Trust, as I have had occasion to observe, begins with candor.
This book addresses application document storage: the use case where your backend stores, queries, and aggregates semi-structured data. User profiles, product catalogs, event logs, form submissions, configuration objects, content management. The documents that live alongside your relational data and currently require a second database to house them. If your application stores JSON and your team already runs PostgreSQL, this book was written for you.
This book does not address time-series analytics at IoT scale, or planetary-scale ingestion pipelines processing millions of writes per second across a distributed cluster. Those are workloads where purpose-built systems — MongoDB among them, alongside TimescaleDB, ClickHouse, and Kafka — have earned their place through engineering that PostgreSQL was not designed to replicate. Respecting a tool means understanding where it belongs and where it does not. That principle applies to MongoDB. It applies equally to PostgreSQL. And it applies, I should add, to this book.
MongoDB Atlas extends well beyond the database engine. Atlas Search, Charts, Data Federation, and the broader Atlas ecosystem constitute an integrated cloud platform — not just a database but a managed environment with tooling that many teams find genuinely valuable. This book addresses MongoDB as a database engine. The managed platform is a separate product category and a separate purchasing decision — one that many teams make for sound reasons. It is acknowledged here with respect and set aside.
Two engineering constraints deserve candor now and will receive their full treatment in Chapter 15.
Write throughput. MongoDB is 2.5 to 4 times faster than PostgreSQL for individual writes — inserts, updates, and deletes. WiredTiger's journal-based storage engine is a genuinely impressive piece of engineering, optimized for write throughput in ways that PostgreSQL's WAL-based architecture is not. Gold Lapel does not cache writes. This is a real gap, and I will not diminish it by calling it minor. Write acceleration — parallel COPY and pipeline mode — is on the Gold Lapel roadmap. It is not available today. Where it is today is honestly where I will leave it.
Extreme horizontal sharding. MongoDB has native, mature sharding across hundreds of nodes. PostgreSQL can shard via Citus, and Gold Lapel detects Citus automatically at startup, but at the far end of the scale spectrum — fifty or more nodes, terabytes of unstructured documents — MongoDB's native sharding remains the more mature tooling. For teams that genuinely operate at that scale, MongoDB has earned its position, and this book does not argue otherwise.
For the vast majority of applications — the ones that need ACID guarantees, relational joins, the full PostgreSQL ecosystem, and one database to manage — the document store they need is the one they already have.
A recommendation that does not name its own boundaries is not a recommendation. It is a sales pitch. I do not deal in those. I deal in place settings, properly arranged.
The next chapter opens the full accounting — every cost, visible and hidden, of maintaining a second database alongside PostgreSQL. Chapter 3 then introduces you to the document store that has been available inside your PostgreSQL instance for over a decade, waiting patiently for someone to make the proper introductions.
I have given you the thesis. I have named the reasons MongoDB is adopted, and I have named them honestly, because those reasons earned their honesty. I have shown you the equation, the scope, and the boundaries. What remains is the evidence — twenty chapters of it, with working code in seven languages, building from the PostgreSQL document layer through the Gold Lapel API to the migration that removes the second database from your architecture.
I believe you will find the evening worthwhile. There are twenty chapters ahead of us, and by the last one, your architecture will be simpler by one database and one sync pipeline — with, I should mention, one fewer invoice to consider along the way.
Shall we begin.