This episode covers security when fetching data: secure API consumption and token handling, protection against CSRF and XSS and secure rendering, input sanitization and safe string handling, and best practices for sensitive data.

Fetching data is only half the job; the other half is making sure the data coming in and going out doesn't open security holes. Leaked tokens, unvalidated input, or HTML rendered without sanitization are attackers' favorite entry points.
This episode covers secure API consumption and token handling, protection against CSRF and XSS, input sanitization and safe string handling, and best practices for sensitive data.
When you're done, you can fetch data from external services without worrying about leaked secrets or opening the door to injection. The patterns you learn here become the foundation of the broader security policy covered in episode 14.
Tokens in query strings leak through server logs, browser history, and the referer header. Send tokens through the Authorization header, and fetch server-side whenever possible:
import { API_TOKEN } from "$env/static/private"
export async function load({ fetch }) {
const res = await fetch("https://api.example.com/v1/users", {
headers: {
Authorization: `Bearer ${API_TOKEN}`,
},
})
if (!res.ok) {
throw new Error("Gagal mengambil data dari API")
}
return { users: await res.json() }
}API_TOKEN is imported from $env/static/private, so it only exists on the server and never reaches the client bundle. The template literal `Bearer ${API_TOKEN}` inserts the token without touching the query string.
Keep tokens as short-lived as possible. A leaked long-lived token is a disaster. A common pattern: short access tokens for each request and a longer-lived refresh token exchanged only on the server. If you suspect a leak, revoke tokens as soon as possible.
CSRF makes a logged-in user's browser perform actions on their behalf without the owner's knowledge. SvelteKit offers built-in protection: form actions validate the request origin, and cookies with sameSite: "lax" prevent most cross-site attacks.
Svelte escapes all interpolations like {nama} by default. Risk appears when you force Svelte to render raw HTML with {@html}. Any content you don't fully trust must be sanitized first:
<script>
import DOMPurify from "dompurify"
let { konten } = $props()
const bersih = DOMPurify.sanitize(konten)
</script>
<div>{@html bersih}</div>DOMPurify.sanitize(konten) strips scripts and dangerous attributes from HTML before rendering. Remember the golden rule: sanitize on the server, not in the browser, because the client can be manipulated.
Client-only validation isn't enough — anyone can open DevTools and remove those rules. Re-validate all input on the server before using it:
import { z } from "zod"
import { fail } from "@sveltejs/kit"
const skemaForm = z.object({
nama: z.string().trim().min(3).max(80),
email: z.string().trim().email(),
umur: z.coerce.number().int().min(18),
})
export const actions = {
async daftar({ request }) {
const data = Object.fromEntries(await request.formData())
const hasil = skemaForm.safeParse(data)
if (!hasil.success) {
return fail(400, { pesan: "Data tidak valid" })
}
return { sukses: true, nama: hasil.data.nama }
},
}z.string().email() ensures the email format is correct and z.coerce.number() converts the string from the form into a number. safeParse returns a result you can check without throwing.
The second rule after validation is escaping. Never concatenate user input directly into SQL, HTML, or URLs. Use parameterized queries for the database and let Svelte handle markup escaping. The same principle applies to JSON sent to other endpoints.
Reduce the sensitive data you move around. Send only the columns the UI needs, never send password hashes or tokens from server to client. Shape a safe user object on the server before it reaches a load function.
$env/static/private or a secret manager.Authorization header, not a query string.sameSite and httpOnly on all session cookies.Key takeaways:
sameSite cookies.{@html} before rendering.Next, in episode 14 we will discuss content security & performance — Content Security Policy and secure headers, caching strategies and resource optimization, image optimization with lazy loading, and network performance monitoring. The sanitization patterns from this episode will form the basis of a broader security policy.