← Level 4 – Data pro

Lesson 5 of 6 ⏱ 55 minutes Premium

A guestbook that lasts forever

Rewrite the API so messages live in SQLite. A server restart won't erase anything anymore.

What we’re building

The same API as in Level 3 – GET /api/vzkazy and POST /api/vzkazy – but instead of an array in memory, we read and write to SQLite. The React frontend can stay almost exactly the same.

Show me: a server with a database

const express = require("express");
const cors = require("cors");
const Database = require("better-sqlite3");

const aplikace = express();
const db = new Database("web.db");

db.exec(`
  CREATE TABLE IF NOT EXISTS vzkazy (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    jmeno TEXT NOT NULL,
    text TEXT NOT NULL,
    vytvoreno TEXT DEFAULT CURRENT_TIMESTAMP
  )
`);

aplikace.use(cors());
aplikace.use(express.json());

// GET – all messages from the database
aplikace.get("/api/vzkazy", (pozadavek, odpoved) => {
  const radky = db.prepare(
    "SELECT id, jmeno, text, vytvoreno FROM vzkazy ORDER BY id DESC"
  ).all();
  odpoved.json(radky);
});

// POST – a new message into the database
aplikace.post("/api/vzkazy", (pozadavek, odpoved) => {
  const { jmeno, text } = pozadavek.body;

  if (!jmeno || !text || jmeno.length > 50 || text.length > 500) {
    return odpoved.status(400).json({ chyba: "Invalid message." });
  }

  const vloz = db.prepare("INSERT INTO vzkazy (jmeno, text) VALUES (?, ?)");
  const vysledek = vloz.run(jmeno, text);

  odpoved.json({ ulozeno: true, id: vysledek.lastInsertRowid });
});

aplikace.listen(3900, () => {
  console.log("Server running at http://localhost:3900");
});

Show me: the frontend

The React component from Level 3 works without changes – the server returns the same JSON, just with extra id and vytvoreno fields. You can show them below the message in small gray text.

Your turn 💪

  1. Replace the vzkazy array in server.js with the code above (or do it gradually – GET first, then POST).
  2. Start the server and send 2 messages from React.
  3. Stop the server, start it again, refresh the page – the messages must remain.
  4. Commit the changes to Git and push to GitHub (without web.db – it’s in .gitignore).
⚠️

On Render/Railway, the disk is sometimes temporaryweb.db may get deleted after a redeploy. In production, use a hosted database later (Render offers free PostgreSQL). For learning and localhost, SQLite is perfect.

Check: The guestbook survives a restart. Validation happens on the server. You’re using parametrized SQL.

What to take away from this lesson