Skip to main content

Async Python orchestrator for HTML-first web applications

Project description

requirements.txt

fastapi>=0.68.0
uvicorn[standard]>=0.15.0
aiohttp>=3.8.0
loguru>=0.6.0
psutil>=5.8.0
python-multipart>=0.0.5
async-property>=0.2.1

HTMLnoJS

A Go-powered HTMX framework for building modern web applications without complex JavaScript.

Features

  • ๐Ÿš€ Zero JavaScript required
  • ๐Ÿ”ง Go-powered backend with Python handlers
  • โšก HTMX for seamless interactivity
  • ๐Ÿ“ File-based routing system
  • ๐ŸŽจ Automatic CSS injection
  • ๐Ÿ”„ Hot reloading during development
  • ๐Ÿ Python functions as API endpoints

Quick Start

Installation

pip install htmlnojs

Basic Usage

from htmlnojs import htmlnojs

# Create and run your app
app = htmlnojs("./my-project")
app.run_forever()

Project Structure

my-project/
โ”œโ”€โ”€ templates/           # HTML files
โ”‚   โ”œโ”€โ”€ index.html      # โ†’ serves at /
โ”‚   โ””โ”€โ”€ about.html      # โ†’ serves at /about
โ”œโ”€โ”€ css/                # CSS files  
โ”‚   โ”œโ”€โ”€ main.css        # Global styles
โ”‚   โ””โ”€โ”€ components.css  # Component styles
โ””โ”€โ”€ py_htmx/           # Python HTMX handlers
    โ”œโ”€โ”€ demo.py        # API handlers
    โ””โ”€โ”€ utils.py       # Utility functions

How It Works

HTMLnoJS combines the speed of Go with the simplicity of Python:

  1. Go Server: Serves HTML, CSS, and handles routing at blazing speed
  2. Python Handlers: Process HTMX requests via FastAPI
  3. Automatic Routing: Files become routes automatically
  4. Zero Config: Just create files and they work

HTML Templates

Create HTML files in the templates/ directory:

<!-- templates/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>My HTMLnoJS App</title>
    <script src="https://unpkg.com/htmx.org@1.9.10"></script>
</head>
<body>
    <div class="container">
        <h1>Welcome to HTMLnoJS</h1>
        
        <!-- Interactive button -->
        <button hx-get="/api/demo/hello" hx-target="#result" class="btn">
            Click me for HTMX magic!
        </button>
        <div id="result"></div>
        
        <!-- Form submission -->
        <form hx-post="/api/demo/form" hx-target="#form-result">
            <input type="text" name="message" placeholder="Enter a message" required>
            <button type="submit">Submit</button>
        </form>
        <div id="form-result"></div>
    </div>
</body>
</html>

Python Handlers

Create Python functions in the py_htmx/ directory:

# py_htmx/demo.py

def htmx_hello(request):
    """Simple HTMX handler"""
    return '<div class="alert">Hello from HTMLnoJS! ๐ŸŽ‰</div>'

def htmx_form(request):
    """Handle form submission"""
    message = request.get('message', 'No message provided')
    return f'''
    <div class="alert success">
        <strong>Form submitted!</strong><br>
        Your message: "{message}"
    </div>
    '''

def htmx_get_users(request):
    """GET request - fetch users"""
    users = ['Alice', 'Bob', 'Charlie']
    html = '<ul>'
    for user in users:
        html += f'<li>{user}</li>'
    html += '</ul>'
    return html

def htmx_post_user(request):
    """POST request - create user"""
    name = request.get('name', '')
    if name:
        return f'<div class="success">Created user: {name}</div>'
    return '<div class="error">Name is required</div>'

CSS Styling

Add CSS files in the css/ directory for automatic injection:

/* css/main.css */
.container {
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
}

.btn {
    background: #3498db;
    color: white;
    border: none;
    padding: 10px 20px;
    border-radius: 4px;
    cursor: pointer;
}

.btn:hover {
    background: #2980b9;
}

.alert {
    padding: 15px;
    margin: 10px 0;
    border-radius: 4px;
    background: #f8f9fa;
    border-left: 4px solid #3498db;
}

.alert.success {
    background: #d4edda;
    border-color: #28a745;
    color: #155724;
}

.alert.error {
    background: #f8d7da;
    border-color: #dc3545;
    color: #721c24;
}

HTTP Method Control

Control HTTP methods via function naming:

# Explicit method specification
def htmx_get_hello(request):      # โ†’ GET /api/demo/hello
    return '<div>Hello World!</div>'

def htmx_post_create(request):    # โ†’ POST /api/demo/create
    return '<div>Created!</div>'

def htmx_put_update(request):     # โ†’ PUT /api/demo/update
    return '<div>Updated!</div>'

def htmx_delete_remove(request):  # โ†’ DELETE /api/demo/remove
    return '<div>Deleted!</div>'

# Automatic detection
def htmx_hello(request):          # โ†’ GET (default)
    pass
def htmx_form(request):           # โ†’ POST (semantic detection)
    pass
def htmx_create_user(request):    # โ†’ POST (contains "create")
    pass

Advanced Usage

Async Context Manager

async with htmlnojs("./my-project") as app:
    # Do other async work while server runs
    await some_other_async_task()
    # Server automatically stops when exiting

Manual Control

app = htmlnojs("./my-project", port=8080, verbose=True)

# Start the server
await app.start()

# Do other work...
await asyncio.sleep(10)

# Stop the server
await app.stop()

Multiple Instances

# Run multiple apps on different ports
app1 = htmlnojs("./frontend", port=3000)
app2 = htmlnojs("./admin", port=3001)

await app1.start()
await app2.start()

# Both servers running simultaneously

Signal Handling

# Graceful shutdown on Ctrl+C
app = htmlnojs("./my-project")
try:
    app.run_forever()  # Handles signals automatically
except KeyboardInterrupt:
    print("Shutting down...")

Request Handling

The request parameter contains form data, query parameters, and more:

def htmx_example(request):
    # Form data (POST requests)
    username = request.get('username', '')
    email = request.get('email', '')
    
    # Query parameters (GET requests)
    page = request.get('page', '1')
    
    # Multiple values
    tags = request.get('tags', [])  # For multiple checkboxes
    
    # Check if field exists
    if 'username' in request:
        return f'<div>Hello {username}!</div>'
    else:
        return '<div>Username required</div>'

File Organization

Large Applications

my-app/
โ”œโ”€โ”€ templates/
โ”‚   โ”œโ”€โ”€ index.html
โ”‚   โ”œโ”€โ”€ auth/
โ”‚   โ”‚   โ”œโ”€โ”€ login.html      # โ†’ /auth/login
โ”‚   โ”‚   โ””โ”€โ”€ register.html   # โ†’ /auth/register
โ”‚   โ””โ”€โ”€ dashboard/
โ”‚       โ””โ”€โ”€ index.html      # โ†’ /dashboard/
โ”œโ”€โ”€ css/
โ”‚   โ”œโ”€โ”€ main.css           # Global styles
โ”‚   โ”œโ”€โ”€ components.css     # Reusable components
โ”‚   โ””โ”€โ”€ pages.css          # Page-specific styles
โ””โ”€โ”€ py_htmx/
    โ”œโ”€โ”€ auth.py            # Authentication handlers
    โ”œโ”€โ”€ dashboard.py       # Dashboard handlers
    โ””โ”€โ”€ utils.py           # Utility functions

Handler Organization

# py_htmx/auth.py
def htmx_login(request):
    username = request.get('username')
    password = request.get('password')
    # Handle login logic
    return '<div>Login successful!</div>'

def htmx_register(request):
    # Handle registration
    return '<div>Registration complete!</div>'

# py_htmx/dashboard.py  
def htmx_get_stats(request):
    # Return dashboard statistics
    return '<div class="stats">...</div>'

def htmx_update_profile(request):
    # Handle profile updates
    return '<div>Profile updated!</div>'

Error Handling

def htmx_safe_handler(request):
    try:
        # Your logic here
        result = some_operation(request.get('data'))
        return f'<div class="success">{result}</div>'
    except ValueError as e:
        return f'<div class="error">Invalid input: {e}</div>'
    except Exception as e:
        return f'<div class="error">Something went wrong: {e}</div>'

Best Practices

1. Return HTML Fragments

# Good - returns HTML fragment
def htmx_get_user(request):
    return '<div class="user">John Doe</div>'

# Avoid - returns JSON (use regular API for this)
def htmx_get_user(request):
    return {"name": "John Doe"}  # Won't render in HTML

2. Use Semantic Class Names

<div class="user-card">
    <button hx-delete="/api/users/delete" 
            hx-target="#user-list"
            class="btn btn-danger">
        Delete User
    </button>
</div>

3. Handle Empty States

def htmx_get_items(request):
    items = get_items_from_db()
    
    if not items:
        return '<div class="empty-state">No items found</div>'
    
    html = '<div class="item-list">'
    for item in items:
        html += f'<div class="item">{item.name}</div>'
    html += '</div>'
    return html

4. Progressive Enhancement

<!-- Works without JavaScript, enhanced with HTMX -->
<form action="/api/contact/send" method="post" 
      hx-post="/api/contact/send" hx-target="#result">
    <input type="email" name="email" required>
    <button type="submit">Subscribe</button>
</form>

Configuration

Port Configuration

# Custom ports
app = htmlnojs(
    project_dir="./my-app",
    port=8080,           # Go server port
    verbose=True         # Enable debug logging
)

Instance Management

from htmlnojs import list_instances, get, stop_all

# List all running instances
instances = list_instances()

# Get specific instance by ID
app = get("my-app-id")

# Stop all instances
await stop_all()

Troubleshooting

Common Issues

  1. Port Already in Use

    # HTMLnoJS automatically finds available ports
    app = htmlnojs("./my-project")  # Will use 3000, 3001, etc.
    
  2. Go Server Not Found

    # HTMLnoJS includes Go server automatically
    # No need to install Go separately
    
  3. Form Data Not Received

    # Make sure to use the correct method
    def htmx_form(request):  # POST by default
        message = request.get('message', '')  # Will work
    
  4. CSS Not Loading

    # CSS files must be in css/ directory
    css/
    โ”œโ”€โ”€ main.css        โœ… Will load
    โ””โ”€โ”€ styles/
        โ””โ”€โ”€ theme.css   โœ… Will load as styles/theme.css
    

Debug Mode

# Enable verbose logging
app = htmlnojs("./my-project", verbose=True)
app.run_forever()

# Check logs for detailed request/response info

Requirements

  • Python 3.8+
  • Go 1.19+ (automatically managed)
  • Modern web browser with HTMX support

Performance

HTMLnoJS is designed for speed:

  • Go server: Handles static files and routing at C-like speeds
  • Python handlers: Only process dynamic content
  • Minimal overhead: Direct proxy between Go and Python
  • Concurrent: Handles multiple requests simultaneously

License

MIT License - see LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

htmlnojs-0.1.0.tar.gz (51.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

htmlnojs-0.1.0-py3-none-any.whl (19.2 kB view details)

Uploaded Python 3

File details

Details for the file htmlnojs-0.1.0.tar.gz.

File metadata

  • Download URL: htmlnojs-0.1.0.tar.gz
  • Upload date:
  • Size: 51.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.2

File hashes

Hashes for htmlnojs-0.1.0.tar.gz
Algorithm Hash digest
SHA256 778ade230b9db803787bc7a6744016d1e619da6db5fb1731527ef847d8866382
MD5 3db405df92a29fe0c3524c876a758497
BLAKE2b-256 ad4e46e46d6968c13c9ab48e7fc7d02203585528629c655c4da9e3ca0af4c513

See more details on using hashes here.

File details

Details for the file htmlnojs-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: htmlnojs-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.2

File hashes

Hashes for htmlnojs-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d3b43fdccbc91d40d52cdccf53a97056fcdccf1e5c6b478cff959e90f3836bf9
MD5 0e0928a5bd24b7aa754f8467f41361ba
BLAKE2b-256 17f1e27807f63b5dcd47a86bcd2f389cebe97cf5171bde63529873b97086d196

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page