Blog post

How to Choose a Database for a Modern Web Application

A practical framework for choosing PostgreSQL, MongoDB, SQLite, or serverless databases for a modern web application, with guidance on consistency, scaling, security, backups, and cost.

Hub-and-spoke layout of database symbols arranged for comparison, guiding selection of the right data store for a web application.

Choosing a database is not a popularity contest. It is an architectural decision about the shape of your data, the consistency your business requires, the way the application will scale, and how much operational work your team can own. A database that is easy to change during the first week can be expensive to replace after a year of migrations, integrations, and production data.

Start with the data model

For most new web applications in 2026, managed PostgreSQL is the sensible starting point. Relational tables, foreign keys, joins, and transactions fit common products such as SaaS platforms, shops, booking systems, and internal tools. PostgreSQL also leaves room for less strictly relational data: jsonb is indexable, and extensions can add capabilities without introducing another service. PostgreSQL 18 adds features such as asynchronous I/O, uuidv7(), and OAuth 2.0 authentication, but the fundamentals—SQL, MVCC, and well-defined constraints—are the more important reason to choose it. MySQL 8.4 LTS remains a strong choice where its ecosystem, hosting options, or existing team expertise are decisive.

A document database such as MongoDB is a better fit when the application naturally reads and updates self-contained aggregates. Embedding related data can make a single-document operation atomic and avoid joins. That model is useful for some catalogs, profiles, and event-shaped records, but it is not a shortcut around data design: MongoDB documents have a 16 MiB limit, and multi-document transactions exist at a greater performance cost. If billing, inventory, or permissions depend on relationships across many records, a relational model is usually easier to reason about.

SQLite deserves more attention than it gets. It is embedded, has no database server to operate, and works well for local-first software, desktop and mobile applications, prototypes, and low-to-medium-traffic sites. SQLite’s own guidance describes roughly 100,000 hits per day as a conservative boundary for many websites. It supports many concurrent readers but only one writer at a time, so a client/server database becomes more appropriate when writes are highly concurrent, the database is separated from the application, or the data grows beyond the practical limits of a single file.

Match the deployment model

Serverless and edge applications change the trade-offs. Cloudflare D1 provides SQLite semantics with a Workers API, read replicas, and Time Travel recovery for the previous 30 days. Turso builds on distributed libSQL and can provide embedded replicas or database-per-tenant patterns. Serverless PostgreSQL services such as Neon separate compute from storage, offer scale-to-zero behavior, and support copy-on-write branches that are useful for testing schema changes. Supabase adds authentication, storage, APIs, realtime features, row-level security, and other platform services around PostgreSQL. Those conveniences can shorten delivery time, but count the platform coupling as part of the decision.

A useful decision matrix looks like this:

  • Relational SaaS, payments, bookings, or inventory: managed PostgreSQL.
  • Existing PostgreSQL with read pressure: query tuning, indexes, connection pooling, then read replicas before sharding.
  • Offline or embedded application: SQLite.
  • Small edge workloads with isolated tenant data: D1 or Turso.
  • Deeply nested aggregates with limited cross-record integrity: MongoDB, or PostgreSQL jsonb if one database is preferable.

Operations are part of the database choice

Managed hosting buys more than a connection string. Depending on the service, it can include patching, high-availability failover, monitoring, backups, and point-in-time recovery. It also adds a bill and some provider-specific behavior. Compare total cost rather than the advertised entry tier: compute, storage, I/O, backup retention, high availability, and egress all matter. Prices change frequently, so verify current pricing before committing to a plan.

Whatever engine you select, define the operational baseline early. Backups are not proven until a restore has been tested. PostgreSQL’s WAL archiving plus a base backup supports point-in-time recovery; pg_dump alone is a logical export, not a replacement for WAL-based recovery. Set a recovery point objective (how much data you can lose) and a recovery time objective (how quickly service must return), then test both.

Version migrations in Git and rehearse them against a staging database or an isolated branch. For a breaking change, use expand-and-contract: add the new column or table, deploy code that can dual-read or dual-write, backfill, switch reads, and only then remove the old structure. This avoids making one deployment depend on an instantly completed rewrite of production data.

Security and observability should be similarly concrete. Use TLS, least-privilege roles, SCRAM authentication where supported, and encryption at rest. For a shared PostgreSQL schema, row-level security can enforce tenant boundaries at the database layer; for example:

ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_invoices ON invoices
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

Treat that policy as defense in depth, not permission to skip application authorization and tests. Monitor connections, replication lag, storage, errors, and slow queries. PostgreSQL’s pg_stat_statements extension is a practical starting point because it shows which statements consume the most calls and execution time.

The best choice is therefore not the database with the most fashionable feature list. Choose the simplest engine that represents your relationships correctly, provides the consistency you need, fits your deployment model, and has an operational path your team can sustain. Start with managed PostgreSQL when the data is relational; choose SQLite when embedding is the advantage; choose a document or edge database only when its data model and deployment benefits are genuinely central to the application.

Sources: PostgreSQL 18 release, PostgreSQL MVCC, PostgreSQL continuous archiving, PostgreSQL row security, SQLite appropriate uses, MongoDB transactions, Cloudflare D1, and Neon branching.

Related areas

These What I Do pages are matched from the subject matter of this article, creating a cleaner path from educational content to implementation work.

Continue reading

Based on shared categories first, then the strongest overlap in tags.