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()
The audit adapter wraps the routes, so handlers never call Record themselves:
auditMW := event.Middleware(event.Options{
ActorResolver: actorResolver(users),
Recorder: rec,
})
handler := withSession(auditMW(apiMux))
Recording per request handler. The audit adapter records the event once the handler returns:
e := event.Current(r.Context())
e.Action = "secret.reveal"
e.Actor = actor
e.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", {
method: "POST",
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.
onTokenExpiredis the escape hatch for any auth pattern the built-intokenEndpointfetcher can't express.