Skip to main content

Framework guide: SvelteKit

Public vs private environment variables

SvelteKit explicitly distinguishes between public (accessible on the client) and private (server only) variables via $env/static/public / $env/dynamic/public vs $env/static/private / $env/dynamic/private.
.env
If you use createConvertStore/MarkpdfClient in a .svelte component (client code), you can only pass it a PUBLIC_* variable — and that key is exposed in the bundle, just like in any SPA.

Pattern A: public key on the client (prototypes, internal tools)

As with any SPA, this key is visible to anyone inspecting the bundle or network traffic. Use it only if you accept that risk (see key exposure considerations, which apply the same here).
Keep the key on the server and expose your own endpoint:
src/routes/api/convert/+server.ts
And from the client component, upload the file to your own endpoint instead of directly to markpdf:
With this pattern, createConvertStore does not apply on the client side (there is no point in calling it against your own endpoint unless you also implement the same progress contract). If you need actual upload progress to your own endpoint, implement the same XMLHttpRequest pattern that createConvertStore uses internally, pointing to /api/convert instead of markpdf’s API.

Form Actions as an alternative to +server.ts

If the upload form lives on the same page, a Form Action (+page.server.ts) is an alternative without needing a separate endpoint:
src/routes/convert/+page.server.ts
Use Form Actions when the form is traditional (full page submission, progressive enhancement with use:enhance). Use a +server.ts endpoint when you need upload progress via XMLHttpRequest/fetch from client JS, or when another service needs to call your proxy directly.