Skip to content

Instantly share code, notes, and snippets.

@rajaravivarma-r
Last active July 9, 2025 16:43
Show Gist options
  • Select an option

  • Save rajaravivarma-r/afc81344873791cb52f3037e43e8d23a to your computer and use it in GitHub Desktop.

Select an option

Save rajaravivarma-r/afc81344873791cb52f3037e43e8d23a to your computer and use it in GitHub Desktop.
Guestbook FAST API equivalent
#!/usr/bin/python3
# Scroll to the bottom for benchmarking results.
import html
import os
import aiosqlite
from datetime import datetime
from typing import List, Optional
from contextlib import asynccontextmanager
from fastapi import FastAPI, Form, Request, HTTPException, Depends
from fastapi.responses import HTMLResponse, RedirectResponse
from pydantic import BaseModel, Field
# Database configuration
DB_PATH = "/tmp/guestbook-py.db"
# HTML templates
TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Guestbook</title>
<style>
body { font-family: sans-serif }
form { margin-bottom: 2em }
textarea { width: 100%; height: 6em }
</style>
</head>
<body>
<h2>Guestbook</h2>
<form method="post" action="/">
<label>Name:<br><input name="name" required></label><br>
<label>Message:<br><textarea name="message" required></textarea></label><br>
<button type="submit">Sign</button>
</form>
{{ entries|safe }}
</body>
</html>
"""
ERROR_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Error</title>
<style>body { font-family: sans-serif }</style>
</head>
<body>
<h2>Error</h2>
<p>An error occurred while processing your request.</p>
<p><a href="/">Back to Guestbook</a></p>
</body>
</html>
"""
# Database Models
class GuestbookEntry(BaseModel):
id: Optional[int] = None
name: str = Field(..., max_length=100)
message: str = Field(..., max_length=1000)
created: Optional[datetime] = None
# Lifespan context manager for database setup and teardown
@asynccontextmanager
async def lifespan(app: FastAPI):
# Database initialization
async with aiosqlite.connect(DB_PATH) as db:
# Create table if it doesn't exist
await db.execute(
"""CREATE TABLE IF NOT EXISTS guestbook(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
message TEXT NOT NULL,
created DATETIME DEFAULT CURRENT_TIMESTAMP
);"""
)
# Create indexes
await db.executescript(
"""CREATE INDEX IF NOT EXISTS idx_guestbook_created ON guestbook(created);
CREATE INDEX IF NOT EXISTS idx_guestbook_name ON guestbook(name);
CREATE INDEX IF NOT EXISTS idx_guestbook_message ON guestbook(message);"""
)
await db.commit()
yield # Application runs here
# Cleanup (if needed)
# Nothing specific to clean up for this app
# Initialize FastAPI app with lifespan
app = FastAPI(title="Guestbook App", lifespan=lifespan)
# Database connection dependency
async def get_db():
"""Create and return an async database connection"""
db = await aiosqlite.connect(DB_PATH)
db.row_factory = aiosqlite.Row
try:
yield db
finally:
await db.close()
def format_entries(rows):
"""Format the database rows into HTML entries"""
parts = []
for row in rows:
entry_html = f"""<div>
<strong>{html.escape(row['name'])}</strong>
<em>{html.escape(row['created'])}</em>
<p>{html.escape(row['message']).replace('\n', '<br>')}</p>
</div><hr>"""
parts.append(entry_html)
return "".join(parts)
@app.get("/", response_class=HTMLResponse)
async def get_guestbook(db: aiosqlite.Connection = Depends(get_db)):
"""Display the guestbook entries"""
try:
async with db.execute(
"SELECT name, message, created FROM guestbook ORDER BY created DESC LIMIT 100"
) as cursor:
rows = await cursor.fetchall()
entries = format_entries(rows)
return TEMPLATE.replace("{{ entries|safe }}", entries)
except Exception as e:
return HTMLResponse(content=ERROR_TEMPLATE, status_code=500)
@app.post("/")
async def post_guestbook_entry(
name: str = Form(...),
message: str = Form(...),
db: aiosqlite.Connection = Depends(get_db),
):
"""Add a new entry to the guestbook"""
try:
# Validate and limit input lengths
name = name.strip()[:100]
message = message.strip()[:1000]
if not name or not message:
raise HTTPException(status_code=400, detail="Name and message are required")
await db.execute(
"INSERT INTO guestbook(name, message) VALUES(?, ?)", (name, message)
)
await db.commit()
# Redirect back to the main page
return RedirectResponse(url="/", status_code=303)
except Exception as e:
await db.rollback()
return HTMLResponse(content=ERROR_TEMPLATE, status_code=500)
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"guestbook:app", host="0.0.0.0", port=8000, workers=2, log_level="critical"
)
# Read
# ```
# Statistics Avg Stdev Max
# Reqs/sec 2019.54 1021.75 10578.27
# Latency 123.45ms 173.88ms 1.95s
# HTTP codes:
# 1xx - 0, 2xx - 30488, 3xx - 0, 4xx - 0, 5xx - 0
# others - 0
# Throughput: 30.29MB/s
# ```
# Write (shown in the graph of the OP)
# ```
# Statistics Avg Stdev Max
# Reqs/sec 931.72 340.79 3654.80
# Latency 267.53ms 443.02ms 2.02s
# HTTP codes:
# 1xx - 0, 2xx - 0, 3xx - 13441, 4xx - 0, 5xx - 215
# others - 572
# Errors:
# timeout - 572
# Throughput: 270.54KB/s
# ```
@rajaravivarma-r
Copy link
Author

With postgres backend (asyncpg)

Write (workers=2, client_connections=100)

Statistics        Avg      Stdev        Max
  Reqs/sec       661.59     377.04    5400.45
  Latency      150.67ms    68.25ms   549.42ms
  HTTP codes:
    1xx - 0, 2xx - 0, 3xx - 9998, 4xx - 0, 5xx - 0
    others - 0
  Throughput:   200.06KB/s

Read (workers=8, client_connections=250)

Statistics        Avg      Stdev        Max
  Reqs/sec       535.88     413.92    5290.89
  Latency      461.28ms   311.73ms      1.80s
  HTTP codes:
    1xx - 0, 2xx - 8237, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:     8.71MB/s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment