An open API service indexing awesome lists of open source software.

https://github.com/usecross/cross-inertia

Python support for Inertia.js
https://github.com/usecross/cross-inertia

django fastapi inertiajs python

Last synced: 5 days ago
JSON representation

Python support for Inertia.js

Awesome Lists containing this project

README

          

# Inertia.js Adapter for Python Web Frameworks

[![Tests](https://github.com/patrick91/cross-inertia/actions/workflows/test.yml/badge.svg)](https://github.com/patrick91/cross-inertia/actions/workflows/test.yml)
[![Lint](https://github.com/patrick91/cross-inertia/actions/workflows/lint.yml/badge.svg)](https://github.com/patrick91/cross-inertia/actions/workflows/lint.yml)
[![PyPI version](https://badge.fury.io/py/cross-inertia.svg)](https://badge.fury.io/py/cross-inertia)
[![Python Versions](https://img.shields.io/pypi/pyversions/cross-inertia.svg)](https://pypi.org/project/cross-inertia/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A Python adapter for using [Inertia.js](https://inertiajs.com/) with FastAPI and Django.

> [!CAUTION]
> This library is still in active development so it might not be fully stable yet.

**[πŸ“š Documentation](https://inertia.patrick.wtf)** | **[πŸš€ Quick Start](#quick-start)** | **[πŸ—ΊοΈ Roadmap](./ROADMAP.md)**

## Features

- βœ… Full Inertia.js protocol support
- βœ… Vite integration (dev & production)
- βœ… Auto-detection of Vite entry point from vite.config.ts/js
- βœ… Asset versioning for cache busting
- βœ… Validation error handling (`props.errors`)
- βœ… History encryption for sensitive data
- βœ… External redirects (OAuth, payments, etc.)
- βœ… Partial reloads & shared data
- βœ… Merging props (infinite scroll support)
- βœ… View data (server-side template variables)
- βœ… TypeScript support

## Installation

```bash
# Install from PyPI using uv (recommended)
uv pip install cross-inertia

# Or using pip
pip install cross-inertia

# Or install from source
uv pip install -e .
```

## Try the Demo

We have a full-featured cat adoption demo app in `examples/fastapi/`:

```bash
# Using just (recommended)
just demo-install # Install dependencies
just demo-fastapi # Run the demo

# Or manually
cd examples/fastapi
bun install
./run-dev.sh
```

Visit http://127.0.0.1:8000 to see Inertia.js + FastAPI in action!

## Quick Start

### 1. Basic Setup

```python
from fastapi import FastAPI
from cross_inertia.fastapi import InertiaDep

app = FastAPI()

@app.get("/")
async def home(inertia: InertiaDep):
return inertia.render(
"Home",
{
"message": "Hello from Inertia!"
}
)
```

### 2. Custom Configuration

If you need to customize the Inertia configuration (e.g., different template directory or Vite settings):

```python
from fastapi import FastAPI, Request, Depends
from cross_inertia.fastapi import InertiaResponse, Inertia

# Create custom InertiaResponse instance
inertia_response = InertiaResponse(
template_dir="my_templates",
vite_dev_url="http://localhost:5173",
manifest_path="dist/.vite/manifest.json",
vite_entry="src/main.tsx", # Optional: auto-detected from vite.config
vite_config_path="vite.config.ts" # Optional: defaults to vite.config.ts
)

app = FastAPI()

def get_custom_inertia(request: Request) -> Inertia:
from cross_web import StarletteRequestAdapter
adapter = StarletteRequestAdapter(request)
return Inertia(request, adapter, inertia_response)

@app.get("/")
async def home(inertia: Inertia = Depends(get_custom_inertia)):
return inertia.render("Home", {"message": "Hello!"})
```

## Configuration Options

### InertiaResponse Parameters

| Parameter | Type | Default | Description |
| ------------------ | ------------- | ------------------------------------ | ---------------------------------------------------- |
| `template_dir` | `str` | `"templates"` | Directory containing your root HTML template |
| `vite_dev_url` | `str` | `"http://localhost:5173"` | Vite dev server URL |
| `manifest_path` | `str` | `"static/build/.vite/manifest.json"` | Path to Vite manifest file (production) |
| `vite_entry` | `str \| None` | `None` | Vite entry point (auto-detected from config if None) |
| `vite_config_path` | `str` | `"vite.config.ts"` | Path to vite.config.ts/js for auto-detection |

### Auto-Detection of Vite Entry

By default, the adapter will attempt to read your `vite.config.ts` (or `.js`) file and extract the entry point from:

```typescript
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
input: "frontend/app.tsx", // ← Auto-detected
},
},
});
```

This means you don't need to specify `vite_entry` manually - it will match your Vite configuration automatically!

## Root Template

Create a template file (default: `templates/app.html`):

```html




{{ vite()|safe }}



{{ page|safe }}


```

The `{{ vite() }}` function will automatically include:

- React Fast Refresh scripts (dev mode)
- Vite client scripts (dev mode)
- Your entry point script
- Built CSS and JS files (production mode)

When using `@inertiajs/react` 2.3+, enable the matching client-side bootstrap format:

```tsx
createInertiaApp({
defaults: {
future: {
useScriptElementForInitialPage: true,
},
},
// ...
});
```

### Using Custom Entry Points

```html

{{ vite()|safe }}

{{ vite('admin/app.js')|safe }}
```

**Backward compatibility**: The old `{{ vite_tags|safe }}` variable is still supported.

## Validation Errors

Validation errors are automatically handled:

```python
@app.post("/users")
async def create_user(inertia: InertiaDep):
errors = validate_user(request_data)

if errors:
# Returns 200 with props.errors for Inertia requests
return inertia.render(
"Users/Create",
{"user": request_data},
errors=errors
)

# Create user...
return inertia.render("Users/Show", {"user": new_user})
```

## External Redirects

Use `inertia.location()` to redirect to external websites or non-Inertia pages:

```python
@app.get("/auth/github")
async def github_oauth(inertia: InertiaDep):
"""Redirect to GitHub OAuth"""
oauth_url = f"https://github.com/login/oauth/authorize?client_id={CLIENT_ID}"
return inertia.location(oauth_url)

@app.get("/shelter/{id}/directions")
async def get_directions(id: int, inertia: InertiaDep):
"""Redirect to Google Maps"""
shelter = get_shelter(id)
maps_url = f"https://maps.google.com/?q={shelter.address}"
return inertia.location(maps_url)
```

This returns a `409 Conflict` response with `X-Inertia-Location` header, which the client automatically follows with a full page navigation.

## History Encryption

Protect sensitive data in browser history by encrypting page state. This prevents users from viewing sensitive information after logging out by pressing the back button.

```python
# Encrypt sensitive pages
@app.get("/account/transactions")
async def transactions(inertia: InertiaDep):
inertia.encrypt_history() # Enable encryption
return inertia.render("Transactions", {
"balance": user.balance,
"transactions": user.get_transactions()
})

# Clear encrypted history on logout
@app.post("/logout")
async def logout(inertia: InertiaDep):
clear_user_session()
inertia.clear_history() # Rotate encryption keys
return inertia.render("Login", {})
```

**How it works:**

- Uses browser's Web Crypto API (AES-GCM encryption)
- Encryption keys stored in sessionStorage
- `clear_history()` rotates keys, making old history unreadable
- Only works over HTTPS (except localhost)

**Use cases:** Banking apps, healthcare (HIPAA), admin panels, any sensitive data

## Development vs Production

The adapter automatically detects whether Vite dev server is running:

- **Dev mode**: Includes Vite dev server scripts and React Fast Refresh
- **Production mode**: Reads from manifest.json and includes built assets

No configuration changes needed - it just works!

## Feature Status

| Feature | Laravel Inertia | FastAPI Inertia | Priority |
| ----------------------------------- | --------------- | --------------- | --------- |
| Basic protocol | βœ… | βœ… | - |
| Template function `{{ vite() }}` | βœ… `@vite` | βœ… | - |
| Auto Vite entry detection | βœ… | βœ… | - |
| Dev/Prod mode detection | βœ… | βœ… | - |
| Validation errors (`props.errors`) | βœ… | βœ… | - |
| Asset versioning (basic) | βœ… | βœ… | - |
| **Asset version mismatch (409)** | βœ… | βœ… | - |
| **Partial reloads** | βœ… | βœ… | - |
| **Shared data** | βœ… | βœ… | - |
| **External redirects** | βœ… | βœ… | - |
| **History encryption** | βœ… | βœ… | - |
| **Merging props (infinite scroll)** | βœ… | βœ… | - |
| **View data** | βœ… | βœ… | - |
| Lazy props | βœ… | ⏳ Planned | 🟑 Medium |
| Deferred props | βœ… | ⏳ Planned | 🟑 Medium |
| Error bags | βœ… | ⏳ Planned | 🟒 Low |
| Prefetching | βœ… | ⏳ Planned | 🟒 Low |
| SSR | βœ… | ❌ Not planned | - |

**See [ROADMAP.md](./ROADMAP.md) for detailed implementation plans and progress tracking.**

## Current Status

**Version:** v0.4.0 "Advanced Features"

This adapter implements all production-critical Inertia features and is **ready for production use**.

**Production-ready features:**

- βœ… Basic page rendering
- βœ… Form submissions with validation
- βœ… Navigation between pages
- βœ… Development with Vite HMR
- βœ… Asset version mismatch handling (409 Conflict)
- βœ… Partial reloads for performance
- βœ… Shared data (auth, flash messages)
- βœ… External redirects (OAuth, payments)
- βœ… History encryption (sensitive data protection)
- βœ… Merging props (infinite scroll)
- βœ… View data (server-side template variables)

**Coming soon:**

- ⏳ Lazy props evaluation
- ⏳ Deferred props

## Contributing

Contributions are very welcome! This adapter aims to match the Laravel adapter's feature set.

**How to contribute:**

1. Check [GitHub Issues](https://github.com/patrick91/cross-inertia/issues) for open tasks
2. Pick a feature (look for `good first issue` or `high-priority` labels)
3. Read the linked Inertia.js documentation
4. Implement following existing patterns
5. Write tests and update documentation
6. Submit a PR!

**See [ROADMAP.md](./ROADMAP.md) for the full project roadmap and milestone planning.**

## License

MIT