Svelte x Node

A Svelte frontend talking to a Node/Express backend that records audit events via sdk-node and serves the Svelte bundle on the same port. Comes in single-tenant and multi-tenant variants.

Source: https://github.com/everscribe/examples/tree/main/svelte-fe-with-node-be

Stack

Layer What it is
Frontend Svelte 4, Vite, @everscribe/components-element
Backend Node 20+, Express, tsx, @everscribe/sdk-node
Domain In-memory secrets vault
Audit panel <audit-trail> custom element

Run it

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

cp .env.example .env   # EVERSCRIBE_PROJECT_ID + EVERSCRIBE_API_KEY
npm install
cd frontend && npm install && npm run build && cd ..

npm start

Server on :3000 serves the Svelte bundle and the API.

Backend integration

Identical to the React + Node backend — the SDK doesn't know which frontend is on the other side:

import { create } from "@everscribe/sdk-node";
import * as minter from "@everscribe/sdk-node/minter";

const es = create(process.env.EVERSCRIBE_PROJECT_ID!, process.env.EVERSCRIBE_API_KEY!);
const rec = es.newRecorder({ flushInterval: 2_000 });
const m = new minter.Client(process.env.EVERSCRIBE_PROJECT_ID!, process.env.EVERSCRIBE_API_KEY!);

Recording from a route handler:

await rec.record({
    action: "secret.reveal",
    actor: actorFor(req),
    target: { type: "secret", id: req.params.id },
    result: { status: "ok" },
});

See React + Node → Backend integration for the full mint endpoint.

Frontend integration

Custom elements are first-class in Svelte:

<script>
    import "@everscribe/components-element";
    import "@everscribe/components-styles/default.css";
</script>

<audit-trail token-endpoint="/api/embed-token"
             on:audit-trail-error={(e) => console.error(e.detail.error)} />

For the multi-tenant customer view, attach the X-Demo-Actor header via the JS-only onTokenExpired property:

<script>
    let el;
    $: if (el) {
        el.onTokenExpired = async () => {
            const res = await fetch("/api/embed-token/customer", {
                credentials: "include",
                headers: { "X-Demo-Actor": currentUser.id },
            });
            return (await res.json()).token;
        };
    }
</script>

<audit-trail bind:this={el} />

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

  • The Express server and the Svelte build share one port. express.static('frontend/dist') is everything.
  • Custom elements work without a wrapper. Svelte's attribute / event-listener syntax flows through cleanly.
  • Backend code is identical to the React variant. Frontend choice is local.

What next