Integration & Extensibility (ASGI & Python hooks)
So far we have run PageQL from the command line. In real projects you often need to embed it in a larger Python stack—add authentication, custom SQL functions, or inject data before a template executes. This chapter shows how.
The code below relies only on
uvicorn
,pageql
, and the Python std‑lib.
« Part 6: Enhancing Interactivity with HTMX
1 A minimal programmable server
import pageql, argparse, uvicorn, base64 parser = argparse.ArgumentParser(description="PageQL programmable server") parser.add_argument('--db', default='data.db') parser.add_argument('--dir', default='templates') parser.add_argument('--port', type=int, default=8000) parser.add_argument('--create', action='store_true') parser.add_argument('--no-reload', action='store_true') args = parser.parse_args() app = pageql.PageQLApp( args.db, args.dir, create_db=args.create, should_reload=not args.no_reload, ) # 1️⃣ Add a custom SQLite function app.conn.create_function('base64_encode', 1, lambda blob: base64.b64encode(blob).decode()) # 2️⃣ Inject data before rendering any template that matches /before @app.before('/before') async def inject(params): params['title'] = 'Horse Power 🐴' with open('horse.jpg', 'rb') as f: params['image'] = f.read() # raw bytes return params # merged into template namespace print(f"Visit http://localhost:{args.port}/before to test the hook") uvicorn.run(app, host='0.0.0.0', port=args.port)
import pageql, argparse, uvicorn, base64 parser = argparse.ArgumentParser(description="PageQL programmable server") parser.add_argument('--db', default='data.db') parser.add_argument('--dir', default='templates') parser.add_argument('--port', type=int, default=8000) parser.add_argument('--create', action='store_true') parser.add_argument('--no-reload', action='store_true') args = parser.parse_args() app = pageql.PageQLApp( args.db, args.dir, create_db=args.create, should_reload=not args.no_reload, ) # 1️⃣ Add a custom SQLite function app.conn.create_function('base64_encode', 1, lambda blob: base64.b64encode(blob).decode()) # 2️⃣ Inject data before rendering any template that matches /before @app.before('/before') async def inject(params): params['title'] = 'Horse Power 🐴' with open('horse.jpg', 'rb') as f: params['image'] = f.read() # raw bytes return params # merged into template namespace print(f"Visit http://localhost:{args.port}/before to test the hook") uvicorn.run(app, host='0.0.0.0', port=args.port)
How it works
Line | What happens |
---|---|
PageQLApp(...) | Embeds the template engine in an ASGI app compatible with any framework/server. |
create_function | Exposes pure‑Python helpers to every SQL statement in your templates. |
@app.before('/before') | Registers an async function that runs before the template. You can also use @app.after() for post‑processing. |
2 Tiny template that consumes the hook
Create templates/before.pageql:
<h1>{{title}}</h1> <img src="data:image/jpeg;base64,{{{ base64_encode(image) }}}" width="320"> <p>This page was populated entirely from the <code>@before</code> hook.</p>
<h1>{{title}}</h1> <img src="data:image/jpeg;base64,{{{ base64_encode(image) }}}" width="320"> <p>This page was populated entirely from the <code>@before</code> hook.</p>
Reload the browser and you should see your local horse.jpg embedded via Data‑URI. Switch images at runtime or add extra parameters—no template change required.
Tip: If you don't have a
horse.jpg
, drop any JPEG into the project root or modify the hook to fetch from the internet.
3 Recap
- Programmable hooks let you keep business logic in Python while PageQL handles rendering.
- Custom SQL functions turn the database into your number‑cruncher or encoder.
- The engine is an ASGI component—compose it with your favourite web stack.
Because PageQLApp is just ASGI, you can:
- Mount it under FastAPI or Starlette and add JWT auth middleware.
- Pipe it through
asgiref.wsgi.WsgiToAsgi
to reuse Flask blueprints. - Deploy on any host that understands ASGI (Uvicorn, Hypercorn, Daphne, AWS Lambda via
mangum
).
That concludes the tutorial series—happy hacking! 🎉