Svelte x Go

A Svelte frontend talking to a Go backend that records audit events via sdk-go and serves the Svelte bundle from a single self-contained binary. Comes in single-tenant and multi-tenant variants.

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

Stack

Layer What it is
Frontend Svelte 4, Vite, @everscribe/components-element (custom-element)
Backend Go 1.25+, net/http, sdk-go (Recorder + Minter), //go:embed all:web/dist
Domain In-memory secrets vault
Audit panel <audit-trail> custom element mounted under the vault UI

Run it

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

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

make build && make run

Binary on :8080 serves the Svelte bundle and the API.

Backend integration

Identical to the React + Go backend — the SDK doesn't know or care which frontend is on the other side. Construction at boot:

import (
    everscribe "github.com/everscribe/sdk-go"
    "github.com/everscribe/sdk-go/pkg/event"
    "github.com/everscribe/sdk-go/pkg/recorder"
)

es, _ := everscribe.New(cfg.ProjectID, cfg.APIKey)
rec := es.NewRecorder(recorder.WithFlushInterval(2*time.Second))
defer rec.Close()
m := es.NewMinter()

Recording per request handler:

defer func() { _ = rec.Record(r.Context(), event.Event{
    Action: "secret.reveal",
    Actor:  actor,
    Target: event.Target{Type: "secret", ID: r.PathValue("id")},
}) }()

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

Frontend integration

The custom element registers on import:

<script type="module">
    import "@everscribe/components-element";
</script>

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

Inside a Svelte component:

<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 replace X-Demo-Actor with their own auth header / cookie.

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

  • Custom elements are first-class in Svelte. Attributes and event listeners just work — no wrapper component needed.
  • The backend is unchanged from the React variant. Swapping the frontend doesn't touch any Go code.
  • onTokenExpired is the escape hatch for any auth pattern the built-in tokenEndpoint fetcher can't express.

What next