Learn Svelte - Secure Data Fetching
Series/Learn Svelte/Episode 13
Episode 13 of 24

Learn Svelte - Secure Data Fetching

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.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

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.

Secure API Consumption and Token Handling

Never Put Tokens in URLs

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:

JSFetch API with token from private env
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.

Short-Lived Tokens and Refresh

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, XSS, and Secure Rendering

CSRF and Form Actions

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.

XSS and Secure Rendering

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:

Sanitizing HTML with DOMPurify
<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.

Input Sanitization and Safe String Handling

Validate on the Server

Client-only validation isn't enough — anyone can open DevTools and remove those rules. Re-validate all input on the server before using it:

JSValidating form input with zod
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.

Always Escape Output

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.

Best Practices for Sensitive Data

Minimal Attack Surface Principle

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.

Practical Checklist

  • Store secrets only on the server, in $env/static/private or a secret manager.
  • Use fetch with an Authorization header, not a query string.
  • Don't log request bodies containing passwords or tokens.
  • Set sameSite and httpOnly on all session cookies.
  • Update dependencies regularly to close known vulnerabilities.

Conclusion

Key takeaways:

  • Send tokens via the Authorization header, not the URL.
  • Fetch sensitive data on the server, not in the browser.
  • Leverage SvelteKit's built-in CSRF protection and sameSite cookies.
  • Escape interpolations and sanitize {@html} before rendering.
  • Validate all input on the server with a schema like zod.
  • Don't send secrets or sensitive data to the client.

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.

Learn Svelte - Secure Data Fetching | Learn Svelte