Workload decides the database. Not database-model loyalty, not whichever pattern won last year's conference talk, not the scaling story you read from a company three orders of magnitude bigger than your team. We've watched this decision get made wrong more times than we can count, and it never actually breaks down at the SQL-versus-NoSQL layer. It breaks down at connection behavior under traffic bursts, at schema debt nobody noticed accruing, and at cache architecture nobody designed on purpose. Every database choice carries production costs. The only real question is which costs you're willing to carry, and workload answers that long before the database model does.
Two things changed that answer on Vercel specifically. Fluid compute rewired connection behavior for relational databases inside serverless functions, closing the failure mode that used to make NoSQL the safe default. Marketplace storage, paired with a caching layer that removes most reads before they ever reach a database, absorbed most of what used to require a database-model decision at all. What's left is a short workload map. Start there instead of starting with an opinion about documents versus rows.
Copy link to headingKey takeaways
Fluid compute and
attachDatabasePoolclose the serverless connection-exhaustion problem that used to push teams toward NoSQL by default.Where TCP is blocked entirely, in Vercel Functions running the Edge Runtime, HTTP-based drivers like the Neon serverless driver keep Postgres viable.
Database migration outcomes hinge on workload fit: moving between NoSQL and SQL can improve or worsen costs depending on access patterns and implementation choices.
ISR, the Data Cache, and Edge Config remove more database load than any database-model switch would.
The practical default on Vercel: Postgres for relational data, Redis for sessions and caching, Edge Config for feature flags, Blob for files, and NoSQL only when the access pattern demands it.
PostgreSQL is widely used among developers and has ranked ahead of MongoDB in developer surveys, so "NoSQL is the modern default" is out of date.
Copy link to headingPostgreSQL passed MongoDB in developer surveys, but the workload still decides
We don't read PostgreSQL outranking MongoDB in developer surveys as a verdict on document databases. We read it as confirmation that "NoSQL is the modern default" stopped being true a while ago, and most of the industry caught up before the messaging did. The survey number doesn't change, which database your checkout flow needs. It means you can stop treating Postgres as the legacy option and NoSQL as the ambitious one, and start asking the only question that was ever going to matter: what your access pattern looks like.
Copy link to headingWhat SQL databases provide
A SQL database stores data as tables of rows with enforced relationships between them, and it guarantees ACID transactions, atomicity, consistency, isolation, and durability. Atomicity is the one that matters operationally. A transaction finishes completely or not at all, so a checkout that deducts inventory and records an order can't leave you with one write applied and the other missing. We see the same guarantee matter any time a workflow can't tolerate partial completion. Checkouts are the clearest example, but it shows up anywhere a multi-step write needs to succeed or fail as a unit.
Joins are the other thing SQL hands you without a fight. A single query pulls data across multiple relations, something NoSQL databases push into application-layer logic instead, and Google Cloud's own guidance on the matter is blunt about how that holds up across distributed nodes, not well. The tradeoff shows up on the scaling side. SQL databases scale vertically by default, and read replicas buy you horizontal read capacity cheaply. Horizontal read-write scaling is the part that costs you, because it needs partitioning or sharding, and that complexity is real at genuine scale and mostly theoretical below it. Don't design for sharding before your access pattern has forced the question.
Copy link to headingWhat NoSQL databases are built to do
NoSQL isn't one thing structurally. It covers four different shapes: document, key-value, graph, and columnar, all trading some portion of ACID for horizontal scalability and schema flexibility. Their consistency model is BASE, which prioritizes availability, soft state, and eventual consistency over the guarantees SQL insists on. That's not a downgrade. It's a deliberate trade, and for workloads that tolerate a moment of staleness, a few milliseconds of lag costs nothing while the availability gain is worth real money.
We see Redis earn its place in this category through access pattern, not ideology. High-volume key-value workloads with simple, predictable shapes, session stores, leaderboards, and rate limiters get sub-millisecond responses because there's no relational overhead standing between the request and the answer. Document storage earns its place the same way. When a record's structure genuinely varies, IoT telemetry, CMS content where every content type carries different fields, the document model maps directly onto the application object instead of forcing a rigid schema onto data that was never going to hold still.
The ACID line between the two categories has softened without disappearing. MongoDB added multi-document ACID transactions in version 4.0, which closed a real gap. It still doesn't conform to ACID by default, and that distinction matters more than the version number suggests. A database that can do transactions when asked isn't the same as one that guarantees them as baseline behavior.
Copy link to headingSQL and NoSQL, side by side
Every dimension below traces back to the same root trade. SQL enforces structure and consistency at write time, NoSQL defers that enforcement in exchange for scale and flexibility. Look at the connection model row specifically. It's the row Fluid compute changed, and it's the reason the rest of this piece exists.
Copy link to headingWhy these tradeoffs exist at the mechanism level
We've traced most of the tradeoffs above back to a single root cause: where each database type does its enforcement work. SQL enforces structure and referential integrity at write time, inside the database engine, before data is considered durable. That's why joins are native and transactions are atomic. The engine does the enforcement, so your application doesn't have to. NoSQL defers that enforcement, usually to the application layer or to eventual consistency, and that deferral is exactly what buys it horizontal scale. A database that isn't checking foreign keys on every write can distribute writes across nodes without coordinating on every one of them.
This is also why schema flexibility isn't free. It's a deferred cost, not an avoided one.
Copy link to headingSchema flexibility becomes debt when data has implicit structure
We've watched flexible schemas do exactly what they're supposed to do, right up until the data has implicit structure nobody chose to enforce. Skipping upfront data modeling works exactly as advertised for data whose structure is genuinely irregular, IoT telemetry, CMS content, logging pipelines. It stops working the moment the underlying data has relational structure the schema simply isn't enforcing.
VideoAmp's engineers watched this play out over 18 months. A schema-less MongoDB environment let their data drift into inconsistency gradually enough that no single commit looked like the problem. The eventual fix, a move to Postgres, took 3 to 4 days, most of it spent converting Mongo arrays into enumerated types and child documents into relational tables. The debt didn't show up as a crisis. It showed up as a slow accumulation that only became visible once someone tried to enforce structure retroactively.
The same pattern shows up in key-value stores, not just document databases. A DynamoDB migration writeup from a team three years into that database put it plainly: schema migrations on large tables were nearly impossible, the kind of rigidity usually associated with relational databases, not the flexible NoSQL store they'd chosen specifically to avoid it.
Migrating away from schema debt doesn't guarantee the fix goes in the direction you'd expect, either. A team that moved from PostgreSQL to DynamoDB, expecting a 40% infrastructure cost reduction, watched its AWS bill jump from $3,200 to $18,400 in a single month, something close to $200,000 in total learning cost once the dust settled. Other teams ran the migration in the opposite direction and it worked. The Guardian moved from MongoDB to Postgres. Infisical migrated millions, if not billions, of database records the same way. Direction isn't the variable that predicts the outcome. Workload fit is.
We lived a smaller version of this ourselves. Our Ship conference platform started on Redis, where duplicated data and limited querying meant every update took extra steps, which pushed us to consolidate on Postgres with Payload CMS as a single source of truth.
Copy link to headingInsert speed no longer breaks the tie
We stopped weighing model choice on raw insert speed once the numbers converged this closely. On larger documents, a JSONB benchmark puts Postgres and MongoDB close enough that speed alone can't settle anything.
The gap is inside noise. If your decision was resting on insert speed, it isn't resting on anything anymore. Decide on access pattern instead.
Copy link to headingWhat your symptoms are telling you about the database
We built this table from the failure patterns above, organized as triage instead of a return to first principles. Most of them announce themselves as something that looks unrelated to the database decision that actually caused it.
Copy link to headingWhen to use SQL
SQL is the right default whenever the data has real relationships and correctness under concurrent writes matters more than raw write throughput. That covers more of a typical Vercel app than teams expect, not just the obvious accounts-and-billing case. We default new relational workloads to Postgres for exactly this reason.
Transactional OLTP, user accounts, billing workflows, anything where a checkout deducting inventory and recording an order needs to happen atomically or not at all
Relational queries that benefit from native joins instead of stitching data together in application code
Any workload with ACID guarantees materially reduces the amount of correctness logic your application has to own itself
Postgres through Marketplace storage, Neon, Supabase, or Prisma Postgres covers all three. Pair the pg driver with attachDatabasePool under Fluid compute so pooled connections get established once and cleaned up before an instance suspends. From the Edge Runtime, where TCP is blocked outright, the Neon serverless driver takes over instead. That correctness costs something, too. Vertical scaling caps out before horizontal write scaling would, and getting past that cap means sharding, which is real complexity you don't need until you've hit genuine scale.
Copy link to headingWhen to use NoSQL
NoSQL is the right call when the access pattern is simple and predictable, or when the data's structure is genuinely irregular record-to-record. Neither condition is about scale for its own sake. Both are about what an enforced schema or ACID guarantee would be buying you. We route sessions and rate limiters to Redis by default, not because NoSQL is faster in the abstract, but because the access pattern is a close match for what Redis is built for.
Upstash Redis handles sessions, caching, rate limiting, and leaderboards, with a REST API that works in edge runtimes where TCP connections aren't available at all
Redis or DynamoDB cover high-throughput key-value work with predictable access, carts, check-ins, real-time state, where consistent latency at scale matters more than relational integrity
MongoDB Atlas covers variable-schema documents and IoT telemetry, where the document model maps directly to application objects instead of forcing irregular data into rigid tables. Convex covers the real-time sync case in the same category
The cost is the one you're already trading for, weaker consistency and less enforcement at write time, so the flexibility only pays off if your data's structure stays irregular instead of quietly becoming relational over time.
Two categories sit adjacent to this list without belonging to either side of the split. Edge Config handles feature flags and runtime config as edge-replicated key-value data that never touches a database at read time. Vercel Blob handles large files and media as object storage. Neither is really a database decision. Both are decisions to keep that data out of the database question entirely.
Copy link to headingBuilding with both on Vercel
Here's what running SQL and NoSQL side by side on Vercel looks like in practice: connection lifecycle, storage routing, and how much of the workload never needs a database decision at all.
Copy link to headingFluid compute and the connection lifecycle
For years, serverless pushed teams toward NoSQL because traditional serverless made relational connection management harder. Traffic bursts could spin up additional function instances, and without global pooling or a managed pooler, each new instance could create unnecessary new TCP connections to the database. A burst of 200 concurrent invocations can saturate a 0.25 CU Neon instance, where max_connections defaults to 104. That failure mode made REST-native stores like DynamoDB and Upstash Redis the safe serverless default, since HTTP calls hold no connections.
Fluid compute changes the instance model. Multiple invocations share a single function instance and its global state, so a connection pool initialized at module level gets established once and reused across requests instead of rebuilt per invocation. When an instance suspends mid-lifecycle, though, its idle connections leak instead of closing on their own. We built attachDatabasePool in @vercel/functions specifically to close idle connections before suspension, with support for pg (node-postgres), MySQL2, MariaDB, MongoDB, Redis via ioredis, and Cassandra. No competing serverless platform ships an equivalent connection-lifecycle primitive at the platform level. Teams manage pooling and suspension themselves on platforms like Netlify, Cloudflare Workers, and AWS Amplify. Rolling releases handle the deploy-time version of the same problem, shifting traffic gradually so a release doesn't send a thundering herd of fresh instances at the database at once.
Vercel Functions running the Edge Runtime still block TCP entirely, so the standard pg driver can't connect at all there. The Neon serverless driver replaces TCP with HTTP for one-shot stateless queries and WebSocket for multi-query sessions. Simple HTTP queries complete in around 10ms, up to a 40% speed increase over the prior path. The connection problem that once made SQL a serverless liability now applies to a narrower slice of architectures on the platform than it did two years ago.
Copy link to headingThe Marketplace workload map
Marketplace storage reflects the workload split visible across the platform, expressed as products rather than abstractions.
We retired the first-party Postgres and KV products and moved every existing database to Neon and Upstash automatically in December 2024, once the workload categories had settled enough to know teams needed provider-run Postgres and Redis rather than first-party versions of either. Object storage was the other need that showed up consistently enough to warrant its own first-party product, Vercel Blob. If your workload fits a row in that table, the decision is already made.
Copy link to headingWhat never reaches the database
Count the reads that never reach the database before picking anything from the table above. ISR serves cached responses from the nearest CDN region. The Data Cache serves repeat reads from a regional cache close to where your function executes, and unstable_cache or getCache pulls ORM queries into that same cache layer. GitBook serves 30,000 documentation sites with sub-second content updates by targeting near-100% cache hits, without changing its underlying database. Your database type matters most for the write path and for the reads that can't be cached, which is a smaller set than most teams assume going in.
Copy link to headingThe SQL/NoSQL boundary is blurring. The workload question isn't.
The SQL/NoSQL feature boundary blurring, Postgres absorbing document workloads through JSONB, MongoDB adding transactions and joins, looks like it should be the headline here. It isn't. The operational cost was never about the model. It was about connection handling under serverless bursts and schema discipline over time. Fluid compute handles the first for you by default now. The second only becomes a real cost when a team skips the workload-fit question and picks a model on reputation instead of access pattern. Pick the database your write path needs. The rest of the boundary can keep blurring without changing that answer.
Copy link to headingFAQ
Copy link to headingDoes Vercel offer its own managed SQL or NoSQL database?
No. We deprecated our first-party Postgres and KV products in December 2024 and moved existing databases to Neon and Upstash automatically. Database products now come through Vercel Marketplace as integrations. Vercel Blob for object storage and Edge Config for edge key-value data remain first-party primitives.
Copy link to headingCan I use PostgreSQL from the Edge Runtime?
Yes, with a driver that swaps TCP for HTTP or WebSocket. The Neon serverless driver runs stateless single queries over HTTP and multi-query sessions over WebSocket, both in runtimes where the standard pg driver's TCP connection is blocked.
Copy link to headingIs MongoDB a good choice for a Next.js app on Vercel?
For genuinely variable-schema document workloads, yes. MongoDB Atlas is available on Marketplace, and Convex covers real-time sync. For data that carries implicit relational structure, the migration record earlier in this piece points toward Postgres producing fewer long-term maintenance problems.
Copy link to headingHow does Fluid compute affect database connection costs?
Function instances handle multiple concurrent requests, so a module-scope connection pool gets reused instead of rebuilt per request, and attachDatabasePool closes idle connections before an instance suspends. Billing counts active CPU, so time spent waiting on database I/O adds no compute cost.