First protected app / Step 5 of 6

Add a route policy

Source and package versions

One AIWAF repository. Python: py/aiwaf/ · Node.js: js/ · Rust/WASM: rust/ · Java: java/. Source and canonical README. Versions shown describe the source checkout; package releases are independent. Current Python/Node source targets are aiwaf 1.1.8 (Python) and aiwaf 1.1.5 (npm), with Rust/Python 0.2.7 and WASM 0.2.5 and Java 1.3.5. Sandbox guides retain their separately pinned builds.

A route policy lets you change one check on one route. Here you will add a preview endpoint that bypasses rate limiting while the existing message endpoint retains its limit.

1. Import the selective exemption helper

Change the AIWAF import at the top of app.py:

from aiwaf.flask import AIWAF, aiwaf_exempt, aiwaf_exempt_from

2. Add the preview route

Insert this handler before the final AIWAF(app, ...) registration:

@app.get("/api/preview")
@aiwaf_exempt_from("rate_limit")
def preview():
    return {"message": "Preview has no rate limit"}

Also add this explicit preview budget before the final AIWAF registration. It keeps the baseline at three requests even after manifest inference, so five successful requests demonstrate the decorator exemption:

# Pin the preview budget too; its decorator selectively bypasses that check.
app.config["AIWAF_PATH_RULES"].append(
    {"PREFIX": "/api/preview", "RATE_LIMIT": {"WINDOW": 60, "MAX": 3, "FLOOD": 100}}
)

The Flask route decorator is outside the AIWAF decorator. aiwaf_exempt_from disables the named check on this handler, while aiwaf_exempt on health marks a full exemption. The example selects rate limiting and logging; adding other checks later requires reviewing the preview route's intended policy.

3. Regenerate and restart

aiwaf init --framework flask --app app:app
python -m flask --app app:app run --port 5000

Stop the earlier server before starting another one. Run the four-request message test again: its fourth request should still be 429. Then request preview five times:

python -c "from urllib.request import urlopen; print([urlopen('http://127.0.0.1:5000/api/preview').status for _ in range(5)])"
[200, 200, 200, 200, 200]

Using a different path gives a different default rate bucket; five successes exceed the three-request threshold and demonstrate the selective exemption.

Complete app after this change

Show the final app.py

app.py

"""Local learning app. Its small limits are for the tutorial, not production."""

from flask import Flask
from aiwaf.flask import AIWAF, aiwaf_exempt, aiwaf_exempt_from

app = Flask(__name__)
app.config.update(
    AIWAF_USE_CSV=True,
    AIWAF_DATA_DIR="aiwaf_data",
    AIWAF_RATE_WINDOW=60,
    AIWAF_RATE_MAX=3,
    AIWAF_RATE_FLOOD=100,
    AIWAF_PATH_RULES=[
        {"PREFIX": "/api/message", "RATE_LIMIT": {"WINDOW": 60, "MAX": 3, "FLOOD": 100}},
    ],
    AIWAF_LOG_DIR="aiwaf_logs",
    AIWAF_LOG_FORMAT="json",
)


@app.get("/api/message")
def message():
    return {"message": "Hello from a protected route"}


@app.get("/health")
@aiwaf_exempt
def health():
    return {"ok": True}


# Added in the tutorial's route-policy chapter.
@app.get("/api/preview")
@aiwaf_exempt_from("rate_limit")
def preview():
    return {"message": "Preview has no rate limit"}


# Pin the preview budget too; its decorator selectively bypasses that check.
app.config["AIWAF_PATH_RULES"].append(
    {"PREFIX": "/api/preview", "RATE_LIMIT": {"WINDOW": 60, "MAX": 3, "FLOOD": 100}}
)


AIWAF(app, middlewares=["rate_limit", "logging"])
Checkpoint Message remains limited, preview skips rate limiting, and health keeps its deliberate full exemption.