NoSQL / Document Databases
"NoSQL" is a catch-all term for databases that don't use the traditional table-and-row relational model. The most common type for web development is the document database, where data is stored as flexible JSON-like documents instead of rigid table rows.
MongoDB as Example
MongoDB is the most popular document database. Instead of tables with fixed columns, you have collections of documents that can each have different structures:
Notice how documents can contain arrays and nested objects natively — something that requires separate tables and JOINs in a relational database.
SQL vs NoSQL Terminology
| SQL Concept | NoSQL Equivalent (MongoDB) |
|---|---|
| Database | Database |
| Table | Collection |
| Row | Document |
| Column | Field |
SELECT |
find() |
INSERT |
insertOne() / insertMany() |
UPDATE |
updateOne() / updateMany() |
DELETE |
deleteOne() / deleteMany() |
JOIN |
Embedded document or $lookup (aggregation) |
When NoSQL vs SQL
| Choose SQL (PostgreSQL) When | Choose NoSQL (MongoDB) When |
|---|---|
| Data has a consistent, well-defined structure | Data structure varies between records |
| You need JOINs across related data | Related data is naturally nested (embedded docs) |
| ACID transactions are critical (banking, orders) | Eventual consistency is acceptable |
| Complex queries with aggregation and filtering | Simple lookups by key or basic filters |
| Data integrity constraints are important | Schema flexibility is more important than strictness |
MongoDB Code Examples
Node.js with MongoDB (using the official driver):
PHP with MongoDB (using the mongodb extension):
ORMs and Query Builders
Writing raw SQL for every database operation works, but it's tedious and error-prone for complex applications. ORMs (Object-Relational Mappers) and query builders provide higher-level abstractions that map database rows to objects in your programming language.
What ORMs do:
- Map tables to classes and rows to objects
- Generate SQL from method calls (you write code, the ORM writes SQL)
- Handle relationships — load related data automatically (eager/lazy loading)
- Manage schema changes via migrations (version-controlled database changes)
- Abstract database differences — switch between PostgreSQL and MySQL with minimal code changes
Raw SQL vs ORM Comparison
| Operation | Raw SQL | ORM (Sequelize / Eloquent) |
|---|---|---|
| Get all | SELECT * FROM stories |
Story.findAll() / Story::all() |
| Get by ID | SELECT * FROM stories WHERE id = $1 |
Story.findByPk(id) / Story::find($id) |
| Create | INSERT INTO stories (title) VALUES ($1) |
Story.create({title}) / Story::create([...]) |
| Update | UPDATE stories SET status = $1 WHERE id = $2 |
story.update({status}) / $story->update([...]) |
| Delete | DELETE FROM stories WHERE id = $1 |
story.destroy() / $story->delete() |
| Filter | SELECT * FROM stories WHERE priority = $1 |
Story.findAll({where: {priority}}) / Story::where('priority', $p)->get() |
Popular ORMs and Query Builders
| Language | Tool | Type | Notes |
|---|---|---|---|
| Node.js | Sequelize | ORM (Active Record) | Most established Node.js ORM; supports PostgreSQL, MySQL, SQLite |
| Node.js | Prisma | ORM (Data Mapper-ish) | Modern, type-safe; uses its own schema language; auto-generates client |
| Node.js | Knex | Query Builder | Not a full ORM — builds SQL from JS, gives you more control |
| PHP | Eloquent | ORM (Active Record) | Part of Laravel; very expressive; models = tables |
| PHP | Doctrine | ORM (Data Mapper) | Used by Symfony; more explicit; entities are plain PHP objects |
Active Record vs Data Mapper
The two main ORM patterns differ in how tightly your objects are coupled to the database:
| Pattern | How It Works | Examples | Trade-off |
|---|---|---|---|
| Active Record | Each object knows how to save itself. story.save() writes to the DB directly. |
Sequelize, Eloquent, Rails ActiveRecord | Simple and fast to develop; harder to test in isolation |
| Data Mapper | Objects are plain data. A separate "mapper" handles persistence. Object doesn't know about the DB. | Prisma, Doctrine, Hibernate | More code upfront; cleaner separation of concerns; easier to test |
Choosing Your Storage
Here's a practical decision framework for picking the right storage for your use case:
| Use Case | Recommended Storage | Why |
|---|---|---|
| Learning CRUD / tutorials | Flat files (JSON) or in-memory | Zero setup; focus on HTTP and REST first |
| Personal project / prototype | SQLite | Real SQL with no server to manage; one file to deploy |
| Production web app | PostgreSQL | Battle-tested, ACID compliant, rich feature set, excellent ecosystem |
| Content management / CMS | PostgreSQL or MongoDB | PostgreSQL if structured; MongoDB if content types vary wildly |
| User sessions / caching | Redis | Blazing fast reads/writes; built-in TTL (expiration) for sessions |
| Analytics / event logging | Ingestion: flat files (log files) — append-only, fast, no DB overhead Analysis: PostgreSQL (or specialized: ClickHouse, TimescaleDB) |
Log files are ideal for high-speed write capture; databases with SQL aggregation shine when you need to query and analyze the collected data |
| Mobile app with offline support | SQLite (on device) + PostgreSQL (server) | SQLite works offline; sync to server when connected |
| Microservices with different data shapes | Mix — each service picks what fits | "Polyglot persistence": use the right DB for each service's needs |