dbbasic-web
A Unix-philosophy micro-framework that restores the simplicity of CGI while delivering modern performance (~4000 req/s vs 100 req/s for traditional CGI).
Philosophy
Modern web frameworks moved away from Unix principles and lost key capabilities:
- ❌ Message passing → bolted-on queue systems
- ❌ Cron jobs → separate background job libraries
- ❌ Filesystem routing → giant route tables
- ❌ Flat files → everything forced into SQL
- ❌ Streams → poor SSE/WebSocket support
dbbasic-web restores these capabilities using:
- ✅ Filesystem routing (like CGI, but async)
- ✅ TSV-based storage, queues, and streams (no Redis/SQL required)
- ✅ First-class WebSockets and Server-Sent Events
- ✅ Background jobs with dbbasic-queue
- ✅ Message bus with dbbasic-pipe
- ✅ Flat files alongside databases
Features
1. Filesystem Routing
No route tables. No decorators. Just files:
api/hello.py → handles /hello
api/tasks.py → handles /tasks (collection)
api/tasks/[id].py → handles /tasks/123 (item with pattern matching)
api/users/[username].py → handles /users/alice
templates/about.html → renders /about
public/css/app.css → serves /css/app.css
Use method-specific functions (GET, POST, PUT, DELETE) or generic handle() function.
2. Hierarchical API Handlers
Each handler can manage its own sub-routes:
# api/user.py
def handle(request):
path_parts = request['path_parts'] # ['user', '123', 'edit']
if len(path_parts) == 1:
return list_users()
elif len(path_parts) == 2:
return get_user(path_parts[1])
elif len(path_parts) == 3:
return edit_user(path_parts[1], path_parts[2])
3. TSV-Based Storage (No SQL Required)
from dbbasic_web.storage import write_text, read_text
# Flat files with automatic directory creation
write_text("notes/2024-01-15.txt", "Meeting notes...")
content = read_text("notes/2024-01-15.txt")
4. Background Jobs (No Celery/Redis)
from dbbasic_web.jobs import enqueue
# Jobs stored in TSV files
enqueue("write_flatfile", relpath="exports/data.csv", content=csv_data)
Run worker:
python manage.py worker
5. Message Bus (No Kafka/Redis)
from dbbasic_web.bus import EventBus
bus = EventBus()
await bus.publish("notifications", {"user_id": 123, "event": "login"})
async for message in bus.consume("notifications", group="processors", consumer="worker-1"):
print(message['data'])
6. WebSockets & Server-Sent Events
Built-in, no configuration needed:
WebSocket:
const ws = new WebSocket('ws://localhost:8000/ws/room-name');
ws.send(JSON.stringify({message: 'Hello!'}));
SSE:
const events = new EventSource('/sse/counter');
events.addEventListener('tick', (e) => console.log(e.data));
Quick Start
Installation
pip install dbbasic-web
Or install from source:
git clone https://github.com/askrobots/dbbasic-web.git
cd dbbasic-web
pip install -e .
Run the Server
python manage.py serve
Visit: http://localhost:8000
Project Structure
dbbasic-web/
├── dbbasic_web/
│ ├── api/ # API handlers (filesystem routing)
│ │ ├── hello.py # /hello
│ │ └── user.py # /user, /user/*, /user/*/posts
│ ├── templates/ # Jinja2 templates
│ │ ├── base.html
│ │ └── index.html
│ ├── public/ # Static files
│ │ ├── css/app.css
│ │ └── js/app.js
│ ├── asgi.py # ASGI application
│ ├── router.py # Filesystem router
│ ├── jobs.py # Background jobs
│ ├── bus.py # Message bus
│ ├── storage.py # Flat-file storage
│ ├── websocket.py # WebSocket hub
│ └── sse.py # Server-Sent Events
├── _data/ # Auto-created data directory
│ ├── jobs.tsv # Job queue
│ └── streams/ # Message bus streams
├── manage.py # CLI
└── pyproject.toml
Creating API Endpoints
Simple Endpoint
# api/status.py
import json
from dbbasic_web.responses import json as json_response
def handle(request):
return json_response(json.dumps({"status": "ok"}))
Access: GET /status
REST Resource with Sub-Routes (Classic Pattern)
# api/posts.py
def handle(request):
parts = request['path_parts']
method = request['method']
# /posts
if len(parts) == 1:
if method == 'GET':
return list_posts()
elif method == 'POST':
return create_post(request)
# /posts/123
elif len(parts) == 2:
post_id = parts[1]
if method == 'GET':
return get_post(post_id)
elif method == 'PUT':
return update_post(post_id, request)
# /posts/123/comments
elif len(parts) == 3 and parts[2] == 'comments':
return get_comments(parts[1])
Pattern Routing with Method Functions (New in 0.1.7)
Split routes by file structure and HTTP method for cleaner code:
Collection endpoints:
# api/tasks.py
def GET(request):
"""List all tasks"""
return json(json.dumps({"tasks": [...]}))
def POST(request):
"""Create new task"""
data = json.loads(request.body)
return json(json.dumps({"task": data}), status=201)
Item endpoints with pattern matching:
# api/tasks/[id].py - matches /tasks/123
def GET(request, id):
"""Get single task - id is automatically extracted"""
task = tasks.query_one(id=id)
return json(json.dumps({"task": task}))
def PUT(request, id):
"""Update task"""
data = json.loads(request.body)
tasks.update({'id': id}, data)
return json(json.dumps({"success": True}))
def DELETE(request, id):
"""Delete task"""
tasks.delete(id=id)
return json(json.dumps({"success": True}))
Pattern routing works with any parameter name:
api/users/[username].py → /users/alice → GET(request, username)
api/posts/[slug].py → /posts/hello → GET(request, slug)
api/@[handle].py → /@alice → GET(request, handle)
Benefits:
- File structure mirrors URL structure (
ls api/tasks/shows available routes) - HTTP method = function name (no nested IFs)
- Parameters extracted from URL and passed as function arguments
- Backward compatible (old
handle()pattern still works)
Performance
| Implementation | Requests/sec |
|---|---|
| Traditional CGI | ~100 |
| dbbasic-web | ~4000 |
Achieved by combining:
- Async I/O (ASGI/uvicorn)
- Filesystem routing (no regex matching)
- Direct module imports (no middleware chains)
- TSV files instead of database round-trips
Dependencies
Minimal and purposeful:
uvicorn- ASGI serverjinja2- Templatesdbbasic-tsv- TSV databasedbbasic-queue- Job queuedbbasic-pipe- Message streamsdbbasic-sessions- Authenticationwebsockets- WebSocket support
No Redis. No Celery. No PostgreSQL required.
Commands
# Run development server
python manage.py serve
# Run background job worker
python manage.py worker
# Interactive shell
python manage.py shell
Examples
See complete working examples at dbbasic-examples:
- blog - Simple blog with posts
- microblog - Twitter-like microblog with follows
- api - REST API with Bearer token auth, demonstrating pattern routing
Philosophy in Action
This framework proves that:
- Simplicity scales - Filesystem routing is faster than route tables
- Flat files work - TSV beats Redis for many use cases
- Unix was right - Pipes, files, and processes are enough
- Less is more - 8000 lines total vs 200k+ for Django
License
MIT
Contributing
Built for clarity and hackability. Every module is under 500 lines. Read the source.
Release files for dbbasic-web 0.1.11
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dbbasic_web-0.1.11.tar.gz | 28.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dbbasic_web-0.1.11-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 52.1 kB
Release files / dbbasic_web-0.1.11.tar.gz
| Download URL | dbbasic_web-0.1.11.tar.gz |
|---|---|
| Size | 28.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e361a2a52fa5c958a42da6d12a64fbd462c02e1cd2f2a0537c93e01fb0b0cb90
|
|
BLAKE2b-256 checksum How to use checksums |
17b304dc857befc2ce175741b3e2f05b6013efd26e31099cb3e252f686c939a2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.1
|
Release files / dbbasic_web-0.1.11-py3-none-any.whl
| Download URL | dbbasic_web-0.1.11-py3-none-any.whl |
|---|---|
| Size | 23.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bb567b7e4d6d2fc2c3c684a4b5a1b839a87a42cf53e80d46fc64f85438124112
|
|
BLAKE2b-256 checksum How to use checksums |
971141609b9fbad432f04b6be4a22fd2b18331472fa62527c61838d296eaf219
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.1
|