Build, personalize, and optimize landing pages with AI
Welcome to another official UVF IT guide! In the next 45 minutes you’ll spin up a minimal clone of a Ghost‑style landing‑page platform, inspired by GenPage 3.0, using plain Node.js and a free SQLite database.
We’ll cover project scaffolding, a simple page model, a tiny REST API, a basic UI, and how to run and verify everything locally – no paid services required.
npm install --global windows-build-tools for native SQLite bindings.Create a fresh npm project and install Express and better‑sqlite3 – a zero‑config SQLite driver.
npm init -y && npm install express better-sqlite3
# Create a starter entry point
cat > index.js <<'EOF'
import express from 'express';
const app = express();
app.use(express.json());
app.get('/', (req, res) => res.send('Hello UVF IT!'));
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
EOFOn first run the app creates a pages table with id, slug, title, and html columns.
db.js
import Database from 'better-sqlite3';
const db = new Database('pages.db');
// Initialise schema if it doesn't exist
const init = `
CREATE TABLE IF NOT EXISTS pages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
slug TEXT UNIQUE NOT NULL,
title TEXT NOT NULL,
html TEXT NOT NULL
);
`;
db.exec(init);
export function createPage({slug, title, html}) {
const stmt = db.prepare('INSERT INTO pages (slug, title, html) VALUES (?, ?, ?)');
return stmt.run(slug, title, html);
}
export function getPageBySlug(slug) {
const stmt = db.prepare('SELECT * FROM pages WHERE slug = ?');
return stmt.get(slug);
}We add a tiny Express server that does two things: expose a JSON API at /api/pages/:slug so the front‑end can fetch page data, and serve a very simple HTML editor at /edit/:slug for creating or updating a page. The API reads and writes a JSON file on disk, keeping the example lightweight and file‑based. The editor posts back to the same route, storing the HTML so the API can later return it. This gives you a minimal back‑end you can extend later.
index.js
const express = require('express');
const fs = require('fs');
const path = require('path');
const bodyParser = require('body-parser');
const app = express();
const PORT = process.env.PORT || 3000;
const DATA_DIR = path.join(__dirname, 'data');
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Serve simple HTML editor
app.get('/edit/:slug', (req, res) => {
const slug = req.params.slug;
const filePath = path.join(DATA_DIR, `${slug}.json`);
const page = fs.existsSync(filePath) ? JSON.parse(fs.readFileSync(filePath)) : { title: slug, html: '' };
res.send(`<!doctype html><html><head><title>Edit ${slug}</title></head><body>
<h1>Edit ${slug}</h1>
<form method="POST" action="/edit/${slug}">
<input name="title" placeholder="Title" value="${page.title}" required/><br/>
<textarea name="html" rows="10" cols="50" placeholder="HTML content">${page.html}</textarea><br/>
<button type="submit">Save</button>
</form>
</body></html>`);
});
app.post('/edit/:slug', (req, res) => {
const slug = req.params.slug;
const { title, html } = req.body;
const filePath = path.join(DATA_DIR, `${slug}.json`);
fs.writeFileSync(filePath, JSON.stringify({ title, html }, null, 2));
res.redirect(`/edit/${slug}`);
});
// JSON API endpoint
app.get('/api/pages/:slug', (req, res) => {
const slug = req.params.slug;
const filePath = path.join(DATA_DIR, `${slug}.json`);
if (fs.existsSync(filePath)) {
const page = JSON.parse(fs.readFileSync(filePath));
res.json(page);
} else {
res.status(404).json({ error: 'Page not found' });
}
});
app.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));Now that the server code is ready, install its dependencies and start it. The app will listen on port 3000 by default, and you can immediately open the editor in your browser to create a page called "home" or any slug you like. All data is persisted to the data folder, so subsequent runs will load the saved content automatically.
npm install express body-parser
node index.jsWith the server running, verify the two core pieces: the HTML editor works and the JSON API returns the saved page. Use a browser to visit the edit route, fill in a title and some HTML, then hit Save. Afterwards, curl the API endpoint to see the JSON representation. From here you can extend the builder – add markdown parsing, a live preview pane, or hook an AI model to generate content.
curl http://localhost:3000/api/pages/home| Check | Command / Why it matters |
|---|---|
| npm install completes without errors | npm install |
| Server starts and logs the URL | node index.js |
| Visiting http://localhost:3000/edit/home shows the edit form | Open in browser |
| After saving, curl returns JSON with title and html | curl http://localhost:3000/api/pages/home |
Your minimal Ghost‑style landing page builder is up and running. From here you can experiment with markdown parsers, add a preview iframe, or hook in an open‑source AI model for content generation – all without leaving the free stack you just built.