How Flat-File Storage Works
The simplest form of persistent storage: read data from a file, modify it in memory, write it back. This is exactly what our Module 05 REST tutorials do with items.json.
Request comes in:
1. Read entire file from disk ┌──────────────┐
─────────────────────────────▶ │ items.json │
│ [{"id":1, │
2. Parse JSON into memory │ "name":"…"}│
items = JSON.parse(data) │ ... │
└──────────────┘
3. Modify array in memory
items.push(newItem)
4. Write entire file back ┌──────────────┐
─────────────────────────────▶ │ items.json │
│ [{"id":1, │
│ "name":"…"},│
│ {"id":2, │
│ "name":"…"}]│
└──────────────┘
Node.js Implementation
In Node.js, the fs module provides synchronous methods for reading and writing files. Here's the pattern used in our REST API tutorial:
The pattern is straightforward: read the entire file, parse it from JSON into a JavaScript array, modify the array, then serialize it back to JSON and write the entire file. Every operation — create, update, delete — follows this same read-modify-write cycle.
PHP Implementation
PHP uses file_get_contents() and file_put_contents() for the same pattern. Because PHP starts fresh on every request, there's no option for in-memory persistence — file storage is the simplest persistent option available:
The Race Condition Problem
The biggest weakness of flat-file storage is what happens when two requests arrive at the same time. Consider this scenario:
Time Request A Request B
──── ───────── ─────────
1 Read items.json
[item1, item2]
2 Read items.json
[item1, item2]
3 Add item3 to array
[item1, item2, item3]
4 Add item4 to array
[item1, item2, item4]
5 Write items.json
[item1, item2, item3]
6 Write items.json
[item1, item2, item4]
Result: item3 is LOST! Request B overwrote Request A's changes.
This is called a race condition — the result depends on the timing of the requests. With flat files, there's no built-in mechanism to prevent two processes from reading and writing the same file simultaneously.
File locking (flock() in PHP, advisory locks in Node.js) can mitigate this, but it adds complexity and doesn't scale well. Databases solve this problem properly with transactions and row-level locking.
Pros and Cons
| Pros | Cons |
|---|---|
| Zero setup — no database server to install | No query language — must load entire file to find anything |
| Human-readable — open the file and see your data | Race conditions — two requests writing simultaneously corrupt data |
| Easy to debug — just look at the JSON | No indexing — searching gets slow with large datasets |
| Portable — copy one file to move all your data | No relationships — no JOINs, no foreign keys |
| Great for learning the CRUD pattern | Rewrites entire file on every change — inefficient |