Skip to content

Frameworks and ORMs

Knocker intentionally does not ship framework adapters. Your app owns HTTP routing and ORM choices; Knocker owns durable webhook storage, verification, dedupe, and later handler dispatch.

A framework adapter would still need to read your framework’s request object, preserve the raw signed body, pass headers through, and return a plain status response. That code is short, app-specific, and easier to audit when it lives next to the route it serves.

Keeping this glue in docs also keeps Knocker’s packages small: no web framework dependency choices leak into the durable webhook core.

The route glue is small in every framework:

  1. read the raw request body bytes exactly as the provider sent them
  2. pass headers and query params through to receive(...)
  3. return result.status_code

Use receive(...) from your route for normal verified ingress. Use ingest(...) only when another trusted layer has already decided the verification outcome.

from fastapi import Request, Response
@api.post("/webhooks/provider")
async def provider_webhook(request: Request):
result = webhooks.receive(
endpoint="provider",
body=await request.body(),
headers=dict(request.headers),
query=dict(request.query_params),
)
return Response(status_code=result.status_code)

The handler transaction is the important integration point for ORMs. If your handler writes app state, do it through Knocker’s transaction handle or through an ORM session/connection bound to that same SQLite transaction.

Do not open a second independent SQLite connection inside the handler and expect it to be part of the webhook acknowledgement transaction. That second connection may commit even if the handler later fails.

In the examples below, helper names like insertInvoiceUsingKnockerTx(...) stand in for your ORM/repository code bound to Knocker’s transaction handle. The point is the ownership boundary: app writes and Knocker’s handled/ack transition share one SQLite transaction.

import json
@automation.handle("invoice.created")
def handle_invoice(event, tx):
payload = json.loads(event.body)
tx.execute(
"INSERT INTO invoices(provider_id, total_cents) VALUES (?, ?)",
[payload["id"], payload["data"]["object"]["amount_due"]],
)