Refactor documentation for reverse proxy and routing guides
Deploy to Production / deploy (push) Successful in 5s

This commit is contained in:
Илья Глазунов
2025-12-08 01:05:52 +03:00
parent 58660ec8d4
commit 00119ce463
12 changed files with 521 additions and 517 deletions
+89 -89
View File
@@ -45,15 +45,15 @@
<h3>Configuration</h3>
<p>ASGI applications are mounted via the <code>asgi</code> extension:</p>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">asgi</span>
<span class="directive">config:</span>
<span class="directive">mounts:</span>
- <span class="directive">path:</span> <span class="value">"/api"</span>
<span class="directive">app_path:</span> <span class="value">"myapp.api:app"</span>
<span class="directive">app_type:</span> <span class="value">asgi</span>
<span class="directive">name:</span> <span class="value">"api-app"</span>
<span class="directive">strip_path:</span> <span class="value">true</span></pre>
<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>
@@ -85,23 +85,23 @@
<h3>Mounting FastAPI</h3>
<p>FastAPI applications are native ASGI:</p>
<pre><span class="comment"># myapp/api.py</span>
<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"}]</pre>
return [{"id": 1, "name": "Alice"}]</code></pre>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">asgi</span>
<span class="directive">config:</span>
<span class="directive">mounts:</span>
- <span class="directive">path:</span> <span class="value">"/api"</span>
<span class="directive">app_path:</span> <span class="value">"myapp.api:app"</span>
<span class="directive">app_type:</span> <span class="value">asgi</span>
<span class="directive">name:</span> <span class="value">"fastapi-app"</span></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">
@@ -112,23 +112,23 @@ async def get_users():
<h3>Mounting Flask</h3>
<p>Flask applications are WSGI and will be automatically wrapped:</p>
<pre><span class="comment"># myapp/flask_api.py</span>
<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!"}</pre>
return {"message": "Hello from Flask!"}</code></pre>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">asgi</span>
<span class="directive">config:</span>
<span class="directive">mounts:</span>
- <span class="directive">path:</span> <span class="value">"/flask"</span>
<span class="directive">app_path:</span> <span class="value">"myapp.flask_api:app"</span>
<span class="directive">app_type:</span> <span class="value">wsgi</span>
<span class="directive">name:</span> <span class="value">"flask-app"</span></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>
@@ -138,19 +138,19 @@ def hello():
<h3>Mounting Django</h3>
<p>Django can be mounted using its ASGI application:</p>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">asgi</span>
<span class="directive">config:</span>
<span class="directive">mounts:</span>
- <span class="directive">path:</span> <span class="value">"/django"</span>
<span class="directive">django_settings:</span> <span class="value">"myproject.settings"</span>
<span class="directive">module_path:</span> <span class="value">"/path/to/django/project"</span>
<span class="directive">name:</span> <span class="value">"django-app"</span></pre>
<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><span class="comment"># myapp/api.py</span>
<pre><code class="language-python"># myapp/api.py
from fastapi import FastAPI
def create_app(debug: bool = False, prefix: str = "/v1") -> FastAPI:
@@ -160,19 +160,19 @@ def create_app(debug: bool = False, prefix: str = "/v1") -> FastAPI:
async def status():
return {"debug": debug}
return app</pre>
return app</code></pre>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">asgi</span>
<span class="directive">config:</span>
<span class="directive">mounts:</span>
- <span class="directive">path:</span> <span class="value">"/api"</span>
<span class="directive">app_path:</span> <span class="value">"myapp.api:create_app"</span>
<span class="directive">app_type:</span> <span class="value">asgi</span>
<span class="directive">factory:</span> <span class="value">true</span>
<span class="directive">factory_args:</span>
<span class="directive">debug:</span> <span class="value">true</span>
<span class="directive">prefix:</span> <span class="value">"/v2"</span></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>
@@ -198,31 +198,31 @@ def create_app(debug: bool = False, prefix: str = "/v1") -> FastAPI:
<h3>Multiple Mounts</h3>
<p>Mount multiple applications at different paths:</p>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">asgi</span>
<span class="directive">config:</span>
<span class="directive">mounts:</span>
<span class="comment"># FastAPI for REST API</span>
- <span class="directive">path:</span> <span class="value">"/api"</span>
<span class="directive">app_path:</span> <span class="value">"apps.api:app"</span>
<span class="directive">app_type:</span> <span class="value">asgi</span>
<pre><code class="language-yaml">extensions:
- type: asgi
config:
mounts:
# FastAPI for REST API
- path: "/api"
app_path: "apps.api:app"
app_type: asgi
<span class="comment"># Flask admin panel</span>
- <span class="directive">path:</span> <span class="value">"/admin"</span>
<span class="directive">app_path:</span> <span class="value">"apps.admin:app"</span>
<span class="directive">app_type:</span> <span class="value">wsgi</span>
# Flask admin panel
- path: "/admin"
app_path: "apps.admin:app"
app_type: wsgi
<span class="comment"># Starlette websocket handler</span>
- <span class="directive">path:</span> <span class="value">"/ws"</span>
<span class="directive">app_path:</span> <span class="value">"apps.websocket:app"</span>
<span class="directive">app_type:</span> <span class="value">asgi</span>
# Starlette websocket handler
- path: "/ws"
app_path: "apps.websocket:app"
app_type: asgi
<span class="comment"># Standard routing for static files</span>
- <span class="directive">type:</span> <span class="value">routing</span>
<span class="directive">config:</span>
<span class="directive">regex_locations:</span>
<span class="value">"__default__"</span>:
<span class="directive">root:</span> <span class="value">"./static"</span></pre>
# 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
@@ -236,24 +236,24 @@ def create_app(debug: bool = False, prefix: str = "/v1") -> FastAPI:
<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><span class="directive">extensions:</span>
<span class="comment"># ASGI apps handle /api/* and /admin/*</span>
- <span class="directive">type:</span> <span class="value">asgi</span>
<span class="directive">config:</span>
<span class="directive">mounts:</span>
- <span class="directive">path:</span> <span class="value">"/api"</span>
<span class="directive">app_path:</span> <span class="value">"myapp:api"</span>
<span class="directive">app_type:</span> <span class="value">asgi</span>
<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
<span class="comment"># Routing handles everything else</span>
- <span class="directive">type:</span> <span class="value">routing</span>
<span class="directive">config:</span>
<span class="directive">regex_locations:</span>
<span class="value">"=/health"</span>:
<span class="directive">return:</span> <span class="value">"200 OK"</span>
<span class="value">"__default__"</span>:
<span class="directive">spa_fallback:</span> <span class="value">true</span>
<span class="directive">root:</span> <span class="value">"./dist"</span></pre>
# 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>
+32 -32
View File
@@ -131,42 +131,42 @@
<p>List of extension modules to load. See <a href="../reference/extensions.html">Extensions Reference</a>.</p>
<h3>Complete Example</h3>
<pre><span class="directive">http:</span>
<span class="directive">static_dir:</span> <span class="value">./static</span>
<span class="directive">templates_dir:</span> <span class="value">./templates</span>
<pre><code class="language-yaml">http:
static_dir: ./static
templates_dir: ./templates
<span class="directive">server:</span>
<span class="directive">host:</span> <span class="value">0.0.0.0</span>
<span class="directive">port:</span> <span class="value">8080</span>
<span class="directive">backlog:</span> <span class="value">5</span>
<span class="directive">default_root:</span> <span class="value">false</span>
<span class="directive">proxy_timeout:</span> <span class="value">30.0</span>
server:
host: 0.0.0.0
port: 8080
backlog: 5
default_root: false
proxy_timeout: 30.0
<span class="directive">ssl:</span>
<span class="directive">enabled:</span> <span class="value">false</span>
<span class="directive">cert_file:</span> <span class="value">./ssl/cert.pem</span>
<span class="directive">key_file:</span> <span class="value">./ssl/key.pem</span>
ssl:
enabled: false
cert_file: ./ssl/cert.pem
key_file: ./ssl/key.pem
<span class="directive">logging:</span>
<span class="directive">level:</span> <span class="value">INFO</span>
<span class="directive">console_output:</span> <span class="value">true</span>
<span class="directive">format:</span>
<span class="directive">type:</span> <span class="value">standard</span>
<span class="directive">use_colors:</span> <span class="value">true</span>
<span class="directive">timestamp_format:</span> <span class="value">"%Y-%m-%d %H:%M:%S"</span>
<span class="directive">files:</span>
- <span class="directive">path:</span> <span class="value">./logs/pyserve.log</span>
<span class="directive">level:</span> <span class="value">DEBUG</span>
<span class="directive">max_bytes:</span> <span class="value">10485760</span>
<span class="directive">backup_count:</span> <span class="value">5</span>
logging:
level: INFO
console_output: true
format:
type: standard
use_colors: true
timestamp_format: "%Y-%m-%d %H:%M:%S"
files:
- path: ./logs/pyserve.log
level: DEBUG
max_bytes: 10485760
backup_count: 5
<span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">routing</span>
<span class="directive">config:</span>
<span class="directive">regex_locations:</span>
<span class="value">"__default__"</span>:
<span class="directive">root:</span> <span class="value">"./static"</span>
<span class="directive">index_file:</span> <span class="value">"index.html"</span></pre>
extensions:
- type: routing
config:
regex_locations:
"__default__":
root: "./static"
index_file: "index.html"</code></pre>
<div class="warning">
<strong>Warning:</strong> When running in production, always use SSL
+85 -85
View File
@@ -41,7 +41,7 @@
</ul>
<h3>Architecture</h3>
<pre>
<pre><code class="language-bash">
PyServe Gateway (:8000)
┌────────────────┼────────────────┐
@@ -49,25 +49,25 @@
FastAPI Flask Starlette
:9001 :9002 :9003
/api/* /admin/* /ws/*
</pre>
</code></pre>
<p>PyServe acts as a gateway, routing requests to the appropriate subprocess based on URL path.</p>
<h3>Basic Configuration</h3>
<pre><span class="directive">server:</span>
<span class="directive">host:</span> <span class="value">0.0.0.0</span>
<span class="directive">port:</span> <span class="value">8000</span>
<pre><code class="language-yaml">server:
host: 0.0.0.0
port: 8000
<span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">process_orchestration</span>
<span class="directive">config:</span>
<span class="directive">apps:</span>
- <span class="directive">name:</span> <span class="value">api</span>
<span class="directive">path:</span> <span class="value">/api</span>
<span class="directive">app_path:</span> <span class="value">myapp.api:app</span>
extensions:
- type: process_orchestration
config:
apps:
- name: api
path: /api
app_path: myapp.api:app
- <span class="directive">name:</span> <span class="value">admin</span>
<span class="directive">path:</span> <span class="value">/admin</span>
<span class="directive">app_path:</span> <span class="value">myapp.admin:app</span></pre>
- name: admin
path: /admin
app_path: myapp.admin:app</code></pre>
<h3>App Configuration Options</h3>
<dl>
@@ -130,18 +130,18 @@
</dl>
<h3>Global Configuration</h3>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">process_orchestration</span>
<span class="directive">config:</span>
<span class="directive">port_range:</span> <span class="value">[9000, 9999]</span>
<span class="directive">health_check_enabled:</span> <span class="value">true</span>
<span class="directive">proxy_timeout:</span> <span class="value">60.0</span>
<span class="directive">logging:</span>
<span class="directive">httpx_level:</span> <span class="value">warning</span>
<span class="directive">proxy_logs:</span> <span class="value">true</span>
<span class="directive">health_check_logs:</span> <span class="value">false</span>
<span class="directive">apps:</span>
<span class="comment"># ...</span></pre>
<pre><code class="language-yaml">extensions:
- type: process_orchestration
config:
port_range: [9000, 9999]
health_check_enabled: true
proxy_timeout: 60.0
logging:
httpx_level: warning
proxy_logs: true
health_check_logs: false
apps:
# ...</code></pre>
<dl>
<dt>port_range</dt>
@@ -161,7 +161,7 @@
</dl>
<h3>FastAPI Example</h3>
<pre><span class="comment"># myapp/api.py</span>
<pre><code class="language-python"># myapp/api.py
from fastapi import FastAPI
app = FastAPI()
@@ -172,22 +172,22 @@ async def health():
@app.get("/users")
async def get_users():
return [{"id": 1, "name": "Alice"}]</pre>
return [{"id": 1, "name": "Alice"}]</code></pre>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">process_orchestration</span>
<span class="directive">config:</span>
<span class="directive">apps:</span>
- <span class="directive">name:</span> <span class="value">api</span>
<span class="directive">path:</span> <span class="value">/api</span>
<span class="directive">app_path:</span> <span class="value">myapp.api:app</span>
<span class="directive">workers:</span> <span class="value">4</span>
<span class="directive">health_check_path:</span> <span class="value">/health</span></pre>
<pre><code class="language-yaml">extensions:
- type: process_orchestration
config:
apps:
- name: api
path: /api
app_path: myapp.api:app
workers: 4
health_check_path: /health</code></pre>
<p>Requests to <code>/api/users</code> are proxied to the FastAPI process as <code>/users</code>.</p>
<h3>Flask Example (WSGI)</h3>
<pre><span class="comment"># myapp/admin.py</span>
<pre><code class="language-python"># myapp/admin.py
from flask import Flask
app = Flask(__name__)
@@ -198,17 +198,17 @@ def health():
@app.route("/dashboard")
def dashboard():
return {"page": "dashboard"}</pre>
return {"page": "dashboard"}</code></pre>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">process_orchestration</span>
<span class="directive">config:</span>
<span class="directive">apps:</span>
- <span class="directive">name:</span> <span class="value">admin</span>
<span class="directive">path:</span> <span class="value">/admin</span>
<span class="directive">app_path:</span> <span class="value">myapp.admin:app</span>
<span class="directive">app_type:</span> <span class="value">wsgi</span>
<span class="directive">workers:</span> <span class="value">2</span></pre>
<pre><code class="language-yaml">extensions:
- type: process_orchestration
config:
apps:
- name: admin
path: /admin
app_path: myapp.admin:app
app_type: wsgi
workers: 2</code></pre>
<div class="note">
<strong>Note:</strong> WSGI support requires <code>a2wsgi</code> package:
@@ -216,7 +216,7 @@ def dashboard():
</div>
<h3>Factory Pattern</h3>
<pre><span class="comment"># myapp/api.py</span>
<pre><code class="language-python"># myapp/api.py
from fastapi import FastAPI
def create_app(debug: bool = False) -> FastAPI:
@@ -226,49 +226,49 @@ def create_app(debug: bool = False) -> FastAPI:
async def health():
return {"status": "ok", "debug": debug}
return app</pre>
return app</code></pre>
<pre><span class="directive">apps:</span>
- <span class="directive">name:</span> <span class="value">api</span>
<span class="directive">path:</span> <span class="value">/api</span>
<span class="directive">app_path:</span> <span class="value">myapp.api:create_app</span>
<span class="directive">factory:</span> <span class="value">true</span></pre>
<pre><code class="language-bash">apps:
- name: api
path: /api
app_path: myapp.api:create_app
factory: true</code></pre>
<h3>Environment Variables</h3>
<p>Pass environment variables to subprocesses:</p>
<pre><span class="directive">apps:</span>
- <span class="directive">name:</span> <span class="value">api</span>
<span class="directive">path:</span> <span class="value">/api</span>
<span class="directive">app_path:</span> <span class="value">myapp.api:app</span>
<span class="directive">env:</span>
<span class="directive">DATABASE_URL:</span> <span class="value">"postgresql://localhost/mydb"</span>
<span class="directive">REDIS_URL:</span> <span class="value">"redis://localhost:6379"</span>
<span class="directive">DEBUG:</span> <span class="value">"false"</span></pre>
<pre><code class="language-bash">apps:
- name: api
path: /api
app_path: myapp.api:app
env:
DATABASE_URL: "postgresql://localhost/mydb"
REDIS_URL: "redis://localhost:6379"
DEBUG: "false"</code></pre>
<h3>Multiple Applications</h3>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">process_orchestration</span>
<span class="directive">config:</span>
<span class="directive">port_range:</span> <span class="value">[9000, 9999]</span>
<span class="directive">apps:</span>
<span class="comment"># FastAPI REST API</span>
- <span class="directive">name:</span> <span class="value">api</span>
<span class="directive">path:</span> <span class="value">/api</span>
<span class="directive">app_path:</span> <span class="value">apps.api:app</span>
<span class="directive">workers:</span> <span class="value">4</span>
<pre><code class="language-yaml">extensions:
- type: process_orchestration
config:
port_range: [9000, 9999]
apps:
# FastAPI REST API
- name: api
path: /api
app_path: apps.api:app
workers: 4
<span class="comment"># Flask Admin Panel</span>
- <span class="directive">name:</span> <span class="value">admin</span>
<span class="directive">path:</span> <span class="value">/admin</span>
<span class="directive">app_path:</span> <span class="value">apps.admin:app</span>
<span class="directive">app_type:</span> <span class="value">wsgi</span>
<span class="directive">workers:</span> <span class="value">2</span>
# Flask Admin Panel
- name: admin
path: /admin
app_path: apps.admin:app
app_type: wsgi
workers: 2
<span class="comment"># Starlette WebSocket Handler</span>
- <span class="directive">name:</span> <span class="value">websocket</span>
<span class="directive">path:</span> <span class="value">/ws</span>
<span class="directive">app_path:</span> <span class="value">apps.websocket:app</span>
<span class="directive">workers:</span> <span class="value">1</span></pre>
# Starlette WebSocket Handler
- name: websocket
path: /ws
app_path: apps.websocket:app
workers: 1</code></pre>
<h3>Request Tracing</h3>
<p>PyServe automatically generates and propagates <code>X-Request-ID</code> headers:</p>
+46 -46
View File
@@ -28,12 +28,12 @@
<h3>Basic Proxy Configuration</h3>
<p>Use the <code>proxy_pass</code> directive in routing:</p>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">routing</span>
<span class="directive">config:</span>
<span class="directive">regex_locations:</span>
<span class="value">"~^/api/"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://localhost:9001"</span></pre>
<pre><code class="language-yaml">extensions:
- type: routing
config:
regex_locations:
"~^/api/":
proxy_pass: "http://localhost:9001"</code></pre>
<p>All requests to <code>/api/*</code> will be forwarded to <code>http://localhost:9001/api/*</code>.</p>
@@ -62,21 +62,21 @@
<h3>Custom Headers</h3>
<p>Add custom headers to proxied requests:</p>
<pre><span class="value">"~^/api/"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://localhost:9001"</span>
<span class="directive">headers:</span>
- <span class="value">"X-Custom-Header: my-value"</span>
- <span class="value">"Authorization: Bearer token123"</span></pre>
<pre><code class="language-bash">"~^/api/":
proxy_pass: "http://localhost:9001"
headers:
- "X-Custom-Header: my-value"
- "Authorization: Bearer token123"</code></pre>
<h3>Dynamic Headers with Captures</h3>
<p>Use regex capture groups to build dynamic headers:</p>
<pre><span class="value">"~^/api/v(?P&lt;version&gt;\\d+)/(?P&lt;service&gt;\\w+)"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://localhost:9001"</span>
<span class="directive">headers:</span>
- <span class="value">"X-API-Version: {version}"</span>
- <span class="value">"X-Service: {service}"</span>
- <span class="value">"X-Client-IP: $remote_addr"</span></pre>
<pre><code class="language-bash">"~^/api/v(?P&lt;version&gt;\\d+)/(?P&lt;service&gt;\\w+)":
proxy_pass: "http://localhost:9001"
headers:
- "X-API-Version: {version}"
- "X-Service: {service}"
- "X-Client-IP: $remote_addr"</code></pre>
<p>Special variables:</p>
<ul class="indent">
@@ -87,49 +87,49 @@
<h3>Proxy Timeout</h3>
<p>Configure timeout for proxy requests:</p>
<pre><span class="comment"># Global default timeout</span>
<span class="directive">server:</span>
<span class="directive">proxy_timeout:</span> <span class="value">30.0</span>
<pre><code class="language-yaml"># Global default timeout
server:
proxy_timeout: 30.0
<span class="comment"># Per-route timeout</span>
<span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">routing</span>
<span class="directive">config:</span>
<span class="directive">regex_locations:</span>
<span class="value">"~^/api/slow"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://localhost:9001"</span>
<span class="directive">timeout:</span> <span class="value">120</span> <span class="comment"># 2 minutes for slow endpoints</span></pre>
# Per-route timeout
extensions:
- type: routing
config:
regex_locations:
"~^/api/slow":
proxy_pass: "http://localhost:9001"
timeout: 120 # 2 minutes for slow endpoints</code></pre>
<h3>URL Rewriting</h3>
<p>The proxy preserves the original request path by default:</p>
<pre><span class="comment"># Request: GET /api/users/123</span>
<span class="comment"># Proxied: GET http://backend:9001/api/users/123</span>
<span class="value">"~^/api/"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://backend:9001"</span></pre>
<pre><code class="language-bash"># Request: GET /api/users/123
# Proxied: GET http://backend:9001/api/users/123
"~^/api/":
proxy_pass: "http://backend:9001"</code></pre>
<p>To proxy to a specific path:</p>
<pre><span class="comment"># Request: GET /api/users/123</span>
<span class="comment"># Proxied: GET http://backend:9001/v2/users/123 (path preserved)</span>
<span class="value">"~^/api/"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://backend:9001/v2"</span></pre>
<pre><code class="language-bash"># Request: GET /api/users/123
# Proxied: GET http://backend:9001/v2/users/123 (path preserved)
"~^/api/":
proxy_pass: "http://backend:9001/v2"</code></pre>
<h3>Load Balancing Example</h3>
<p>Route different services to different backends:</p>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">routing</span>
<span class="directive">config:</span>
<span class="directive">regex_locations:</span>
<span class="value">"~^/api/users"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://user-service:8001"</span>
<pre><code class="language-yaml">extensions:
- type: routing
config:
regex_locations:
"~^/api/users":
proxy_pass: "http://user-service:8001"
<span class="value">"~^/api/orders"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://order-service:8002"</span>
"~^/api/orders":
proxy_pass: "http://order-service:8002"
<span class="value">"~^/api/products"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://product-service:8003"</span></pre>
"~^/api/products":
proxy_pass: "http://product-service:8003"</code></pre>
<h3>Error Handling</h3>
<p>pyserve returns appropriate error codes for proxy failures:</p>
+38 -38
View File
@@ -62,29 +62,29 @@
<h3>Routing Configuration</h3>
<p>Routing is configured via the <code>routing</code> extension:</p>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">routing</span>
<span class="directive">config:</span>
<span class="directive">regex_locations:</span>
<span class="comment"># Exact match for health check</span>
<span class="value">"=/health"</span>:
<span class="directive">return:</span> <span class="value">"200 OK"</span>
<span class="directive">content_type:</span> <span class="value">"text/plain"</span>
<pre><code class="language-yaml">extensions:
- type: routing
config:
regex_locations:
# Exact match for health check
"=/health":
return: "200 OK"
content_type: "text/plain"
<span class="comment"># Static files with caching</span>
<span class="value">"~*\\.(js|css|png|jpg|gif|ico)$"</span>:
<span class="directive">root:</span> <span class="value">"./static"</span>
<span class="directive">cache_control:</span> <span class="value">"public, max-age=31536000"</span>
# Static files with caching
"~*\\.(js|css|png|jpg|gif|ico)$":
root: "./static"
cache_control: "public, max-age=31536000"
<span class="comment"># HTML files without caching</span>
<span class="value">"~*\\.html$"</span>:
<span class="directive">root:</span> <span class="value">"./static"</span>
<span class="directive">cache_control:</span> <span class="value">"no-cache"</span>
# HTML files without caching
"~*\\.html$":
root: "./static"
cache_control: "no-cache"
<span class="comment"># Default fallback</span>
<span class="value">"__default__"</span>:
<span class="directive">root:</span> <span class="value">"./static"</span>
<span class="directive">index_file:</span> <span class="value">"index.html"</span></pre>
# Default fallback
"__default__":
root: "./static"
index_file: "index.html"</code></pre>
<h3>Location Directives</h3>
@@ -120,11 +120,11 @@
<h3>Named Capture Groups</h3>
<p>Regex locations support named capture groups that can be used in headers and proxy URLs:</p>
<pre><span class="value">"~^/api/v(?P&lt;version&gt;\\d+)/(?P&lt;resource&gt;\\w+)"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://backend:9001"</span>
<span class="directive">headers:</span>
- <span class="value">"X-API-Version: {version}"</span>
- <span class="value">"X-Resource: {resource}"</span></pre>
<pre><code class="language-bash">"~^/api/v(?P&lt;version&gt;\\d+)/(?P&lt;resource&gt;\\w+)":
proxy_pass: "http://backend:9001"
headers:
- "X-API-Version: {version}"
- "X-Resource: {resource}"</code></pre>
<p>Request to <code>/api/v2/users</code> will have headers:</p>
<ul class="indent">
@@ -135,14 +135,14 @@
<h3>SPA Configuration</h3>
<p>For Single Page Applications, use <code>spa_fallback</code> with <code>exclude_patterns</code>:</p>
<pre><span class="value">"__default__"</span>:
<span class="directive">spa_fallback:</span> <span class="value">true</span>
<span class="directive">root:</span> <span class="value">"./dist"</span>
<span class="directive">index_file:</span> <span class="value">"index.html"</span>
<span class="directive">exclude_patterns:</span>
- <span class="value">"/api/"</span>
- <span class="value">"/assets/"</span>
- <span class="value">"/static/"</span></pre>
<pre><code class="language-python">"__default__":
spa_fallback: true
root: "./dist"
index_file: "index.html"
exclude_patterns:
- "/api/"
- "/assets/"
- "/static/"</code></pre>
<p>This will:</p>
<ul class="indent">
@@ -153,11 +153,11 @@
<h3>Static File Serving</h3>
<p>Basic static file configuration:</p>
<pre><span class="value">"~*\\.(css|js|png|jpg|gif|svg|woff2?)$"</span>:
<span class="directive">root:</span> <span class="value">"./static"</span>
<span class="directive">cache_control:</span> <span class="value">"public, max-age=86400"</span>
<span class="directive">headers:</span>
- <span class="value">"X-Content-Type-Options: nosniff"</span></pre>
<pre><code class="language-bash">"~*\\.(css|js|png|jpg|gif|svg|woff2?)$":
root: "./static"
cache_control: "public, max-age=86400"
headers:
- "X-Content-Type-Options: nosniff"</code></pre>
<div class="note">
<strong>Note:</strong> pyserve automatically detects MIME types based on file extensions.