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}