Python · Vercel Functions
Quote Generator
Fetch a random quote from the backend and save favorites server-side rather than in local storage.
Every quote comes from the backend's quote pool, and favorites are saved and removed through a server-side Python API rather than browser local storage — meaning favorites are a real piece of server state, not a client trick.
Live demo
embedfrom http.server import BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
import json
QUOTES = [
{'id': '1', 'text': 'Simplicity is the soul of efficiency.', 'author': 'Austin Freeman'},
{'id': '2', 'text': 'Make it work, make it right, make it fast.', 'author': 'Kent Beck'},
{'id': '3', 'text': 'Programs must be written for people to read.', 'author': 'Harold Abelson'},
{'id': '4', 'text': 'The best error message is the one that never shows up.', 'author': 'Thomas Fuchs'},
{'id': '5', 'text': 'Code is like humor. When you have to explain it, it is bad.', 'author': 'Cory House'},
{'id': '6', 'text': 'First, solve the problem. Then, write the code.', 'author': 'John Johnson'},
{'id': '7', 'text': 'Any fool can write code that a computer can understand.', 'author': 'Martin Fowler'},
{'id': '8', 'text': 'Fix the cause, not the symptom.', 'author': 'Steve Maguire'},
]
QUOTES_BY_ID = {q['id']: q for q in QUOTES}
# Module-level state persists across warm invocations of this function
# (resets on cold start) — fine for a demo, same tradeoff as the in-memory
# stores in the Node.js and Go examples in this repo.
favorite_ids = set()
class handler(BaseHTTPRequestHandler):
def do_GET(self):
favorites = [q for q in QUOTES if q['id'] in favorite_ids]
self._json(200, favorites)
def do_POST(self):
length = int(self.headers.get('Content-Length', 0))
raw = self.rfile.read(length) if length else b'{}'
try:
payload = json.loads(raw or b'{}')
except json.JSONDecodeError:
payload = {}
quote_id = str(payload.get('id', ''))
if quote_id not in QUOTES_BY_ID:
self._json(404, {'error': 'quote not found'})
return
favorite_ids.add(quote_id)
self._json(201, {'ok': True})
def do_DELETE(self):
query = parse_qs(urlparse(self.path).query)
quote_id = (query.get('id', [''])[0] or '')
favorite_ids.discard(quote_id)
self._json(200, {'ok': True})
def _json(self, status, payload):
body = json.dumps(payload).encode('utf-8')
self.send_response(status)
self.send_header('Content-type', 'application/json')
self.send_header('Content-length', str(len(body)))
self.end_headers()
self.wfile.write(body)
let current = null;
async function nextQuote() {
const res = await fetch('/api/quote');
current = await res.json();
document.getElementById('text').textContent = `“${current.text}”`;
document.getElementById('author').textContent = `— ${current.author}`;
}
async function loadFavorites() {
const res = await fetch('/api/favorites');
const favs = await res.json();
const list = document.getElementById('list');
list.innerHTML = favs.map((q) => `
<div class="item">
<span class="t">“${q.text}”</span>
<span class="a">${q.author}</span>
<button class="icon" data-id="${q.id}" title="Remove">✕</button>
</div>
`).join('') || '<p class="empty">No favorites saved yet.</p>';
list.querySelectorAll('button.icon').forEach((btn) => {
btn.addEventListener('click', async () => {
await fetch(`/api/favorites?id=${encodeURIComponent(btn.dataset.id)}`, { method: 'DELETE' });
loadFavorites();
});
});
}
document.getElementById('next').addEventListener('click', nextQuote);
document.getElementById('fav').addEventListener('click', async () => {
if (!current) return;
await fetch('/api/favorites', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: current.id }),
});
loadFavorites();
});
nextQuote();
loadFavorites();