> ## Documentation Index
> Fetch the complete documentation index at: https://docs.markpdf.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Framework Guide: SvelteKit

> Public environment variables vs protecting the API key with a +server.ts endpoint.

# 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`.

```bash .env theme={null}
# Ends up in browser bundle — anyone can see it
PUBLIC_MARKPDF_API_KEY=tu_key_publica

# Only accessible from server-side code (+page.server.ts, +server.ts, hooks.server.ts)
MARKPDF_API_KEY=tu_key_privada
```

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)

```svelte theme={null}
<script lang="ts">
  import { PUBLIC_MARKPDF_API_KEY } from "$env/static/public";
  import { MarkpdfClient, createConvertStore } from "@markpdf/svelte";

  const client = new MarkpdfClient({ apiKey: PUBLIC_MARKPDF_API_KEY });
  const convertStore = createConvertStore(client);
</script>
```

<Warning>
  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](/docs/sdks/react/framework-guide#la-api-key-en-el-navegador), which apply the same here).
</Warning>

## Pattern B (recommended): proxy with `+server.ts`

Keep the key on the server and expose your own endpoint:

```ts src/routes/api/convert/+server.ts theme={null}
import { MARKPDF_API_KEY } from "$env/static/private";
import { MarkpdfClient } from "@markpdf/svelte";
import type { RequestHandler } from "./$types";

const client = new MarkpdfClient({ apiKey: MARKPDF_API_KEY });

export const POST: RequestHandler = async ({ request }) => {
  const form = await request.formData();
  const file = form.get("file") as File;

  const markdown = await client.convertFile(file, { filename: file.name, mode: "fast" });

  return new Response(markdown, { headers: { "content-type": "text/markdown" } });
};
```

And from the client component, upload the file to your own endpoint instead of directly to markpdf:

```svelte theme={null}
<script lang="ts">
  let status = "idle";
  let markdown = "";

  async function onFile(e: Event) {
    const file = (e.target as { files?: FileList | null }).files?.[0];
    if (!file) return;

    status = "uploading";
    const form = new FormData();
    form.append("file", file);

    const res = await fetch("/api/convert", { method: "POST", body: form });
    markdown = await res.text();
    status = "success";
  }
</script>

<input type="file" on:change={onFile} />
```

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:

```ts src/routes/convert/+page.server.ts theme={null}
import { MARKPDF_API_KEY } from "$env/static/private";
import { MarkpdfClient } from "@markpdf/svelte";
import type { Actions } from "./$types";

const client = new MarkpdfClient({ apiKey: MARKPDF_API_KEY });

export const actions: Actions = {
  default: async ({ request }) => {
    const form = await request.formData();
    const file = form.get("file") as File;
    const markdown = await client.convertFile(file, { filename: file.name, mode: "fast" });
    return { markdown };
  },
};
```

<Tip>
  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.
</Tip>
