WeCodeAcademy

Step 6: Save to a file

Right now everything is lost when the server stops. The simplest fix is to save the state to a JSON file after each change and load it when the server starts. (Real applications use a database, which the next step designs.) This example saves a to-do, then simulates a restart by loading the file again.

ExampleNode.js
const http = require("http");
const fs = require("fs");
const os = require("os");
const path = require("path");

const FILE = path.join(os.tmpdir(), "todos-demo.json");

function load() {
  try {
    return JSON.parse(fs.readFileSync(FILE, "utf8"));
  } catch {
    return { nextId: 1, todos: [] };
  }
}
function save(state) {
  fs.writeFileSync(FILE, JSON.stringify(state, null, 2));
}

const state = load();

function send(res, status, data) {
  res.writeHead(status, { "Content-Type": "application/json" });
  res.end(JSON.stringify(data));
}

function readBody(req) {
  return new Promise((resolve) => {
    let raw = "";
    req.on("data", (c) => (raw += c));
    req.on("end", () => resolve(raw ? JSON.parse(raw) : {}));
  });
}

async function handle(req, res) {
  if (req.method === "GET" && req.url === "/todos") return send(res, 200, state.todos);
  if (req.method === "POST" && req.url === "/todos") {
    const body = await readBody(req);
    const todo = { id: state.nextId++, title: body.title, done: false };
    state.todos.push(todo);
    save(state);            // write to disk after every change
    return send(res, 201, todo);
  }
  send(res, 404, { error: "Not found" });
}

const server = http.createServer(handle);
server.listen(0, async () => {
  const base = `http://localhost:${server.address().port}`;
  await fetch(base + "/todos", { method: "POST", body: JSON.stringify({ title: "Saved to disk" }) });
  console.log("on disk:", JSON.parse(fs.readFileSync(FILE, "utf8")).todos.length, "todo");
  server.close();

  // pretend the server restarted: a fresh load() finds the saved data
  const again = load();
  console.log("after restart:", again.todos.map((t) => t.title));
  fs.unlinkSync(FILE);
});
Output
on disk: 1 todo
after restart: [ 'Saved to disk' ]

Writing the whole file on every change is fine for a small app, but it does not scale and two writes at the same moment could clash. That is exactly the problem databases solve.

Quick check

Why is writing a whole JSON file after every change limited?

Finish the quick check (and any code exercise) to complete this lesson, or mark it done yourself.

Tip: use the left and right arrow keys to move between lessons.