From Flat Files to Full Databases
"The right question isn't 'which database is best?' It's 'what does my application actually need?'"
CSE 135 — Full Overview | Review Questions
Six storage options — parallel choices, not a ranking.
| Storage Type | Setup | Query Power | Concurrency | Best For |
|---|---|---|---|---|
| In-Memory | None | Code-level | Single process | Prototyping, caching |
| Flat Files | Minimal | Read/parse entire file | Poor — race conditions | Tutorials, config |
| SQLite | Low | Full SQL | Single writer | Mobile, embedded, dev |
| PostgreSQL/MySQL | Medium | Full SQL + extensions | Excellent — ACID | Production web apps |
| MongoDB | Medium | Document queries | Good — built for scale | Flexible schemas |
| Redis | Medium | Key-value | Excellent — atomic | Caching, sessions |
Read file, parse JSON, modify array, write file back — the simplest persistence.
| Pros | Cons |
|---|---|
| Zero setup — no database server | No query language — load entire file |
| Human-readable — open and see data | Race conditions with concurrent writes |
| Easy to debug — just look at JSON | No indexing — slow with large data |
| Portable — copy one file | No relationships — no JOINs |
| Great for learning CRUD | Rewrites entire file on every change |
Tables, rows, foreign keys, ACID transactions — the backbone of web applications.
| Column | Type | Constraints | Purpose |
|---|---|---|---|
id | SERIAL | PRIMARY KEY | Auto-incrementing unique ID |
title | VARCHAR(200) | NOT NULL | Required, max 200 chars |
description | TEXT | (nullable) | Optional longer text |
priority | VARCHAR(10) | DEFAULT 'medium' | low / medium / high |
status | VARCHAR(20) | DEFAULT 'todo' | todo / in-progress / done |
created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Auto-set on creation |
| CRUD | SQL Statement | HTTP Method | REST Endpoint |
|---|---|---|---|
| Create | INSERT INTO | POST | /api/stories |
| Read (all) | SELECT * FROM | GET | /api/stories |
| Read (one) | SELECT ... WHERE id=$1 | GET | /api/stories/:id |
| Update | UPDATE ... SET ... WHERE | PUT / PATCH | /api/stories/:id |
| Delete | DELETE FROM ... WHERE | DELETE | /api/stories/:id |
RETURNING * clause is a PostgreSQL feature that returns affected rows after INSERT/UPDATE/DELETE — no second query needed. Extremely useful in REST APIs: return the created/updated resource in the response directly.
Node.js (pg) vs PHP (PDO) — same database, different client libraries.
| Concept | Node.js (pg) | PHP (PDO) |
|---|---|---|
| Install | npm install pg | Built-in (pdo_pgsql extension) |
| Connection | new Pool({host, database, ...}) | new PDO($dsn, $user, $password) |
| Simple query | pool.query('SELECT ...') | $pdo->query('SELECT ...') |
| Parameterized | pool.query('...$1...', [val]) | $stmt->execute(['id' => $val]) |
| Placeholders | Positional: $1, $2, $3 | Named: :id, :title or ? |
| Get rows | result.rows | $stmt->fetchAll() |
| Connection pooling | Built into Pool | Persistent connections or external pooler |
| Error handling | try/catch with async/await | try/catch with PDO::ERRMODE_EXCEPTION |
process.env.DB_PASSWORD / getenv('DB_PASSWORD')). Bots scan public repos for leaked credentials.
The #1 web vulnerability — and the most preventable.
| WRONG — Concatenation | RIGHT — Parameterized |
|---|---|
pool.query(`...WHERE id = ${id}`)Attacker sends: 1; DROP TABLE storiesQuery executes the DROP! |
pool.query('...WHERE id = $1', [id])Attacker sends: 1; DROP TABLE storiesDB sees id = '1; DROP TABLE stories' (string value, no damage) |
$pdo->query("...WHERE id = " . $_GET['id'])Same vulnerability in PHP |
$stmt = $pdo->prepare('...WHERE id = :id')$stmt->execute(['id' => $_GET['id']])Safe in PHP |
$1, $2 in pg) or prepared statements (:name in PDO).
Flexible JSON-like documents instead of rigid table rows.
| SQL Concept | MongoDB Equivalent |
|---|---|
| Database | Database |
| Table | Collection |
| Row | Document |
| Column | Field |
SELECT | find() |
INSERT | insertOne() |
UPDATE | updateOne() |
DELETE | deleteOne() |
JOIN | Embedded doc or $lookup |
| Choose SQL (PostgreSQL) When | Choose NoSQL (MongoDB) When |
|---|---|
| Data has consistent, well-defined structure | Data structure varies between records |
| You need JOINs across related data | Related data is naturally nested |
| ACID transactions are critical | Eventual consistency is acceptable |
| Complex queries with aggregation | Simple lookups by key or basic filters |
| Data integrity constraints matter | Schema flexibility is more important |
Mapping tables to classes — convenience vs understanding.
| Operation | Raw SQL | ORM (Sequelize / Eloquent) |
|---|---|---|
| Get all | SELECT * FROM stories | Story.findAll() / Story::all() |
| Get by ID | SELECT ... WHERE id = $1 | Story.findByPk(id) / Story::find($id) |
| Create | INSERT INTO stories ... | Story.create({title}) / Story::create([...]) |
| Update | UPDATE ... SET ... WHERE | story.update({status}) / $story->update([...]) |
| Delete | DELETE FROM ... WHERE | story.destroy() / $story->delete() |
| Pattern | How It Works | Examples | Trade-off |
|---|---|---|---|
| Active Record | Object saves itself: story.save() | Sequelize, Eloquent | Simple; harder to test |
| Data Mapper | Separate mapper handles persistence | Prisma, Doctrine | More code; cleaner separation |
| Language | Tool | Type |
|---|---|---|
| Node.js | Sequelize | ORM (Active Record) |
| Node.js | Prisma | ORM (Data Mapper-ish) |
| Node.js | Knex | Query Builder |
| PHP | Eloquent | ORM (Active Record) |
| PHP | Doctrine | ORM (Data Mapper) |
Match the tool to the problem — not the hype.
| Use Case | Recommended | Why |
|---|---|---|
| Learning CRUD / tutorials | Flat files or in-memory | Zero setup; focus on HTTP and REST |
| Personal project / prototype | SQLite | Real SQL, no server, one file |
| Production web app | PostgreSQL | ACID, battle-tested, rich ecosystem |
| CMS / content management | PostgreSQL or MongoDB | Depends on content structure |
| Sessions / caching | Redis | Blazing fast, built-in TTL |
| Analytics / event logging | Log files → PostgreSQL | Append-only capture; SQL analysis |
| Mobile with offline | SQLite + PostgreSQL | SQLite offline, sync to server |
| Microservices | Mix per service | "Polyglot persistence" |
9 concepts of data storage in one table.
| Concept | Key Takeaway |
|---|---|
| Storage Landscape | Six options (in-memory, flat files, SQLite, PostgreSQL, MongoDB, Redis) — parallel choices, not a ranking |
| Flat Files | Read/write entire JSON file; zero setup; no concurrency; fine for tutorials, bad for production |
| Relational Databases | Tables, rows, columns; SQL for CRUD; foreign keys; ACID transactions for integrity |
| SQL ↔ CRUD ↔ REST | INSERT=POST, SELECT=GET, UPDATE=PUT/PATCH, DELETE=DELETE — same pattern at every layer |
| Connecting from Code | Node.js pg with Pool + $1; PHP PDO with :name placeholders |
| SQL Injection | #1 web vulnerability; string concatenation is the cause; parameterized queries are the fix |
| NoSQL / MongoDB | Flexible documents; good for varied schemas; use SQL when you need JOINs and transactions |
| ORMs | Map tables to objects (Sequelize, Prisma, Eloquent, Doctrine); learn raw SQL first |
| Choosing Storage | Match tool to problem; PostgreSQL is the safe default; don't over-engineer for tutorials |