Vanilla JS x Python

A vanilla-JS frontend (no framework) talking to a Python/FastAPI backend that records audit events via sdk-python and serves the bundle on the same port. Comes in single-tenant and multi-tenant variants.

Source: https://github.com/everscribe/examples/tree/main/vanilla-js-fe-with-python-be

Stack

Layer What it is
Frontend Plain HTML + ES modules + CSS, Vite for bundling, @everscribe/components-element
Backend Python 3.10+, FastAPI, uvicorn, everscribe
Domain In-memory secrets vault
Audit panel <audit-trail> custom element

Run it

git clone https://github.com/everscribe/examples
cd examples/vanilla-js-fe-with-python-be/single-tenant   # or .../multi-tenant

cp .env.example .env   # EVERSCRIBE_PROJECT_ID + EVERSCRIBE_API_KEY
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
(cd frontend && npm install && npm run build)

python backend/app.py

Server on :9000 serves the bundle and the API.

Backend integration

Identical to the other Python variants:

import everscribe
from everscribe.asgi import EverscribeMiddleware

es = everscribe.new(project_id, api_key)
rec = es.new_recorder(flush_interval=2.0)
minter = es.new_minter()

app.add_middleware(EverscribeMiddleware, recorder=rec, resolve_actor=resolve_actor)

Handlers enrich current_event(); the middleware records on response finish. See React + Python → Backend integration for the full reveal + mint handlers.

Frontend integration

Plain HTML + a tiny module script:

<!doctype html>
<link rel="stylesheet" href="/style.css">
<link rel="stylesheet" href="https://unpkg.com/@everscribe/components-styles/default.css">

<main id="vault"></main>
<audit-trail token-endpoint="/api/embed-token"></audit-trail>

<script type="module">
    import "@everscribe/components-element";
    // ... vault UI code
</script>

For multi-tenant customer scoping, attach the demo auth header via onTokenExpired:

const el = document.querySelector("audit-trail");
el.onTokenExpired = async () => {
    const res = await fetch("/api/embed-token/customer", {
        method: "POST",
        credentials: "include",
        headers: { "X-Demo-Actor": currentUser.id },
    });
    return (await res.json()).token;
};

Real apps swap X-Demo-Actor for whatever auth your backend expects.

Single-Tenant vs Multi-Tenant

Single-tenant Multi-tenant
Token endpoint One. /api/embed-token Two. /api/embed-token/customer, /api/embed-token/admin
UI One vault view Tab strip: Customer view · Admin view
Seeded data One implicit tenant Acme, Initech
Audit-panel scope Whole project Customer: tenant-scoped · Admin: unscoped

What to take away

  • Smallest stack of the matrix. No framework on the frontend, a single uvicorn process on the backend.
  • The audit-trail tag is the entire embed. No wrapper component, no manual polling, no manual filter UI.
  • Same backend code as the React and Svelte variants. Frontend choice is local.

What next