Skip to content

SvelteKit

Integrate PutPut with SvelteKit using a server-side load function for presigning and a form action for confirmation.

1. Environment variable

# .env
PUTPUT_TOKEN=pp_guest_...

2. Server endpoint for presign + confirm

// src/routes/api/presign/+server.ts
import { json } from "@sveltejs/kit";
import { PUTPUT_TOKEN } from "$env/static/private";
import type { RequestHandler } from "./$types";

const API = "https://putput.io/api/v1";

export const POST: RequestHandler = async ({ request }) => {
  const body = await request.json();
  const res = await fetch(`${API}/upload/presign`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${PUTPUT_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  return json(await res.json());
};

// src/routes/api/confirm/+server.ts
export const POST: RequestHandler = async ({ request }) => {
  const body = await request.json();
  const res = await fetch(`${API}/upload/confirm`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${PUTPUT_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  return json(await res.json());
};

3. Upload component

<!-- src/routes/upload/+page.svelte -->
<script lang="ts">
  let uploading = $state(false);
  let publicUrl = $state<string | null>(null);

  async function handleFile(e: Event) {
    const input = e.target as HTMLInputElement;
    const file = input.files?.[0];
    if (!file) return;
    uploading = true;

    // 1. Presign
    const presign = await fetch("/api/presign", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        filename: file.name,
        content_type: file.type,
        size_bytes: file.size,
      }),
    }).then((r) => r.json());

    // 2. Upload to R2
    await fetch(presign.presigned_url, {
      method: "PUT",
      headers: { "Content-Type": file.type },
      body: file,
    });

    // 3. Confirm
    const result = await fetch("/api/confirm", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ upload_id: presign.upload_id }),
    }).then((r) => r.json());

    publicUrl = result.file.public_url;
    uploading = false;
  }
</script>

<input type="file" onchange={handleFile} disabled={uploading} />
{#if uploading}<p>Uploading...</p>{/if}
{#if publicUrl}<a href={publicUrl}>{publicUrl}</a>{/if}
v0.4.77