273 lines
9.0 KiB
HTML
273 lines
9.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>ASGI Mounting - pyserve</title>
|
|
<link rel="stylesheet" href="../style.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
|
<script>hljs.highlightAll();</script>
|
|
</head>
|
|
<body>
|
|
<div id="container">
|
|
<div id="header">
|
|
<h1>pyserve</h1>
|
|
<div class="tagline">python application orchestrator</div>
|
|
</div>
|
|
|
|
<div class="breadcrumb">
|
|
<a href="../index.html">Home</a> / <a href="index.html">Guides</a> / ASGI Mounting
|
|
</div>
|
|
|
|
<div id="content">
|
|
<h2>ASGI Application Mounting (In-Process)</h2>
|
|
|
|
<p>The <code>asgi</code> extension mounts ASGI and WSGI applications directly in the pyserve process.
|
|
This is simpler and has lower latency, but all apps share the same process.</p>
|
|
|
|
<div class="note">
|
|
<strong>For production use cases requiring isolation, consider
|
|
<a href="process-orchestration.html">Process Orchestration</a></strong> which runs each app
|
|
in a separate subprocess with health monitoring and auto-restart.
|
|
</div>
|
|
|
|
<h3>Overview</h3>
|
|
<p>The ASGI mounting system provides:</p>
|
|
<ul class="indent">
|
|
<li><strong>Multi-framework support</strong> — Mount FastAPI, Flask, Django, Starlette, or custom ASGI apps</li>
|
|
<li><strong>Path-based routing</strong> — Each app handles requests at its mounted path</li>
|
|
<li><strong>WSGI compatibility</strong> — Automatic WSGI-to-ASGI conversion for Flask/Django</li>
|
|
<li><strong>Factory pattern support</strong> — Create apps dynamically with arguments</li>
|
|
<li><strong>Path stripping</strong> — Optionally strip mount path from requests</li>
|
|
</ul>
|
|
|
|
<h3>Configuration</h3>
|
|
<p>ASGI applications are mounted via the <code>asgi</code> extension:</p>
|
|
|
|
<pre><code class="language-yaml">extensions:
|
|
- type: asgi
|
|
config:
|
|
mounts:
|
|
- path: "/api"
|
|
app_path: "myapp.api:app"
|
|
app_type: asgi
|
|
name: "api-app"
|
|
strip_path: true</code></pre>
|
|
|
|
<h3>Mount Configuration Options</h3>
|
|
<dl>
|
|
<dt>path</dt>
|
|
<dd>URL path where the application will be mounted. Example: <code>/api</code></dd>
|
|
|
|
<dt>app_path</dt>
|
|
<dd>Python import path to the application. Format: <code>module.submodule:attribute</code></dd>
|
|
|
|
<dt>app_type</dt>
|
|
<dd>Application type: <code>asgi</code> or <code>wsgi</code>. Default: <code>asgi</code></dd>
|
|
|
|
<dt>module_path</dt>
|
|
<dd>Optional path to add to <code>sys.path</code> for module resolution</dd>
|
|
|
|
<dt>factory</dt>
|
|
<dd>If <code>true</code>, <code>app_path</code> points to a factory function. Default: <code>false</code></dd>
|
|
|
|
<dt>factory_args</dt>
|
|
<dd>Dictionary of arguments to pass to the factory function</dd>
|
|
|
|
<dt>name</dt>
|
|
<dd>Friendly name for logging. Default: uses <code>app_path</code></dd>
|
|
|
|
<dt>strip_path</dt>
|
|
<dd>Remove mount path from request URL. Default: <code>true</code></dd>
|
|
</dl>
|
|
|
|
<h3>Mounting FastAPI</h3>
|
|
<p>FastAPI applications are native ASGI:</p>
|
|
|
|
<pre><code class="language-python"># myapp/api.py
|
|
from fastapi import FastAPI
|
|
|
|
app = FastAPI()
|
|
|
|
@app.get("/users")
|
|
async def get_users():
|
|
return [{"id": 1, "name": "Alice"}]</code></pre>
|
|
|
|
<pre><code class="language-yaml">extensions:
|
|
- type: asgi
|
|
config:
|
|
mounts:
|
|
- path: "/api"
|
|
app_path: "myapp.api:app"
|
|
app_type: asgi
|
|
name: "fastapi-app"</code></pre>
|
|
|
|
<p>With this configuration:</p>
|
|
<ul class="indent">
|
|
<li><code>GET /api/users</code> → handled by FastAPI as <code>GET /users</code></li>
|
|
<li>FastAPI docs available at <code>/api/docs</code></li>
|
|
</ul>
|
|
|
|
<h3>Mounting Flask</h3>
|
|
<p>Flask applications are WSGI and will be automatically wrapped:</p>
|
|
|
|
<pre><code class="language-python"># myapp/flask_api.py
|
|
from flask import Flask
|
|
|
|
app = Flask(__name__)
|
|
|
|
@app.route("/hello")
|
|
def hello():
|
|
return {"message": "Hello from Flask!"}</code></pre>
|
|
|
|
<pre><code class="language-yaml">extensions:
|
|
- type: asgi
|
|
config:
|
|
mounts:
|
|
- path: "/flask"
|
|
app_path: "myapp.flask_api:app"
|
|
app_type: wsgi
|
|
name: "flask-app"</code></pre>
|
|
|
|
<div class="note">
|
|
<strong>Note:</strong> WSGI wrapping requires either <code>a2wsgi</code> or <code>asgiref</code>
|
|
to be installed. Install with: <code>pip install a2wsgi</code>
|
|
</div>
|
|
|
|
<h3>Mounting Django</h3>
|
|
<p>Django can be mounted using its ASGI application:</p>
|
|
|
|
<pre><code class="language-yaml">extensions:
|
|
- type: asgi
|
|
config:
|
|
mounts:
|
|
- path: "/django"
|
|
django_settings: "myproject.settings"
|
|
module_path: "/path/to/django/project"
|
|
name: "django-app"</code></pre>
|
|
|
|
<h3>Factory Pattern</h3>
|
|
<p>Use factory functions to create apps with custom configuration:</p>
|
|
|
|
<pre><code class="language-python"># myapp/api.py
|
|
from fastapi import FastAPI
|
|
|
|
def create_app(debug: bool = False, prefix: str = "/v1") -> FastAPI:
|
|
app = FastAPI(debug=debug)
|
|
|
|
@app.get(f"{prefix}/status")
|
|
async def status():
|
|
return {"debug": debug}
|
|
|
|
return app</code></pre>
|
|
|
|
<pre><code class="language-yaml">extensions:
|
|
- type: asgi
|
|
config:
|
|
mounts:
|
|
- path: "/api"
|
|
app_path: "myapp.api:create_app"
|
|
app_type: asgi
|
|
factory: true
|
|
factory_args:
|
|
debug: true
|
|
prefix: "/v2"</code></pre>
|
|
|
|
<h3>Path Stripping</h3>
|
|
<p>By default, <code>strip_path: true</code> removes the mount prefix from requests:</p>
|
|
|
|
<table class="dirindex">
|
|
<tr>
|
|
<td>Request</td>
|
|
<td><code>strip_path: true</code></td>
|
|
<td><code>strip_path: false</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>GET /api/users</code></td>
|
|
<td>App sees <code>/users</code></td>
|
|
<td>App sees <code>/api/users</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>GET /api/</code></td>
|
|
<td>App sees <code>/</code></td>
|
|
<td>App sees <code>/api/</code></td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h3>Multiple Mounts</h3>
|
|
<p>Mount multiple applications at different paths:</p>
|
|
|
|
<pre><code class="language-yaml">extensions:
|
|
- type: asgi
|
|
config:
|
|
mounts:
|
|
# FastAPI for REST API
|
|
- path: "/api"
|
|
app_path: "apps.api:app"
|
|
app_type: asgi
|
|
|
|
# Flask admin panel
|
|
- path: "/admin"
|
|
app_path: "apps.admin:app"
|
|
app_type: wsgi
|
|
|
|
# Starlette websocket handler
|
|
- path: "/ws"
|
|
app_path: "apps.websocket:app"
|
|
app_type: asgi
|
|
|
|
# Standard routing for static files
|
|
- type: routing
|
|
config:
|
|
regex_locations:
|
|
"__default__":
|
|
root: "./static"</code></pre>
|
|
|
|
<h3>Mount Priority</h3>
|
|
<p>Mounts are matched by path length (longest first). Given mounts at
|
|
<code>/api</code> and <code>/api/v2</code>:</p>
|
|
<ul class="indent">
|
|
<li><code>/api/v2/users</code> → matches <code>/api/v2</code> mount</li>
|
|
<li><code>/api/users</code> → matches <code>/api</code> mount</li>
|
|
</ul>
|
|
|
|
<h3>Combining with Routing</h3>
|
|
<p>ASGI mounts work alongside the routing extension. The <code>asgi</code> extension
|
|
should be listed before <code>routing</code> to handle mounted paths first:</p>
|
|
|
|
<pre><code class="language-yaml">extensions:
|
|
# ASGI apps handle /api/* and /admin/*
|
|
- type: asgi
|
|
config:
|
|
mounts:
|
|
- path: "/api"
|
|
app_path: "myapp:api"
|
|
app_type: asgi
|
|
|
|
# Routing handles everything else
|
|
- type: routing
|
|
config:
|
|
regex_locations:
|
|
"=/health":
|
|
return: "200 OK"
|
|
"__default__":
|
|
spa_fallback: true
|
|
root: "./dist"</code></pre>
|
|
|
|
<h3>Python API</h3>
|
|
<p>For programmatic mounting, see <a href="../reference/asgi-mount.html">ASGI Mount API Reference</a>.</p>
|
|
|
|
<div class="warning">
|
|
<strong>Warning:</strong> Mounted applications share the same process.
|
|
Ensure your applications are compatible and don't have conflicting global state.
|
|
</div>
|
|
</div>
|
|
|
|
<div id="footer">
|
|
<p>pyserve © 2024-2025 | MIT License</p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|