← Back to the Build Your Homepage series
🟢
EPISODE 05
Resources · status codes · CRUD · CORS · Persisting state · read/write JSON · race conditions

Building a REST API · File-based Database

Design and implement a REST API: pick correct status codes, model resources around URLs, support CRUD, and enable CORS for browser clients. Before introducing a real database, persist data to disk as JSON. Learn the limitations (concurrency, scale) and when to graduate to SQLite/PostgreSQL.

RESTAPIHTTPstatus codesCORSpersistenceJSONfslowdb
Duration
⏱ About 2.5 hours + About 1.5 hours
Level
📊 Intermediate
Prerequisite
🎯 node-04
OUTCOME
A working /tasks REST API with full CRUD and proper status codes / Persist a REST API's data to a JSON file (good enough for prototypes)

What you'll learn

  • 1Design resource-based URLs
  • 2Return the right status code for each scenario
  • 3Implement create / read / update / delete
  • 4Enable CORS for cross-origin browser requests
  • 5Read/write a JSON file with fs.promises
  • 6Wrap I/O in helper functions
  • 7Recognize race-condition limitations
  • 8Migrate naturally to a real database later

Building a REST API

Design and implement a REST API: pick correct status codes, model resources around URLs, support CRUD, and enable CORS for browser clients.

1. REST Conventions

MethodURLActionSuccess Status
GET/tasksList all tasks200
GET/tasks/:idGet one task200 (or 404)
POST/tasksCreate a task201
PUT/tasks/:idReplace a task200
PATCH/tasks/:idPartial update200
DELETE/tasks/:idDelete a task204 (no body)

2. Sample CRUD

javascript
import express from "express";
import cors from "cors";

const app = express();
app.use(cors());
app.use(express.json());

let tasks = [];
let nextId = 1;

app.get("/tasks", (req, res) => res.json(tasks));
app.get("/tasks/:id", (req, res) => {
  const t = tasks.find(t => t.id == req.params.id);
  if (!t) return res.status(404).json({ error: "Not found" });
  res.json(t);
});
app.post("/tasks", (req, res) => {
  const task = { id: nextId++, title: req.body.title, done: false };
  tasks.push(task);
  res.status(201).json(task);
});
app.delete("/tasks/:id", (req, res) => {
  tasks = tasks.filter(t => t.id != req.params.id);
  res.status(204).end();
});

app.listen(3000);

3. Status Codes Cheat Sheet

  • 2xx — success (200 OK, 201 Created, 204 No Content)
  • 3xx — redirection (301 Moved, 304 Not Modified)
  • 4xx — client error (400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict)
  • 5xx — server error (500 Internal Server Error, 503 Service Unavailable)

4. CORS

Browsers block cross-origin requests by default. Enable CORS so your frontend can call your API.

javascript
import cors from "cors";
// Allow specific origins
app.use(cors({
  origin: ["http://localhost:5173", "https://myapp.com"],
  credentials: true,
}));

File-based Database (JSON)

Before introducing a real database, persist data to disk as JSON. Learn the limitations (concurrency, scale) and when to graduate to SQLite/PostgreSQL.

1. A Tiny JSON Store

javascript
import fs from "node:fs/promises";
const FILE = "./data.json";

async function load() {
  try {
    return JSON.parse(await fs.readFile(FILE, "utf8"));
  } catch {
    return { tasks: [], nextId: 1 };
  }
}

async function save(data) {
  await fs.writeFile(FILE, JSON.stringify(data, null, 2));
}

const db = await load();
db.tasks.push({ id: db.nextId++, title: "buy milk" });
await save(db);

2. Integrate with Express

javascript
app.get("/tasks", async (req, res) => {
  const db = await load();
  res.json(db.tasks);
});
app.post("/tasks", async (req, res) => {
  const db = await load();
  const task = { id: db.nextId++, title: req.body.title, done: false };
  db.tasks.push(task);
  await save(db);
  res.status(201).json(task);
});

3. Limitations

  • Two requests writing at the same time can lose data (race condition)
  • Whole file is read/rewritten on every operation — slow past a few MB
  • No query language, no indexes
  • Fine for: personal tools, prototypes, low-traffic dashboards
  • Not fine for: any production app with concurrent users
💡

Next module — Databases — graduates from JSON to SQLite (single file but with SQL) and beyond.

Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗

Comments 0

No comments yet. Be the first.

Only members can comment.

Comments with ads, abuse, personal information or illegal content are hidden or removed. Comments are shown with your nickname. Privacy Policy