Advanced patterns and edge cases for SvelteKit applications.
// Prevent race conditions with AbortController
export async function load({ fetch, url }) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch(`/api/data?q=${url.searchParams.get('q')}`, {
signal: controller.signal
});
return { data: await response.json() };
} catch (e) {
if (e.name === 'AbortError') {
return { data: null, timeout: true };
}
throw e;
} finally {
clearTimeout(timeoutId);
}
}// Only load data if certain conditions are met
export async function load({ url, depends }) {
const shouldLoad = url.searchParams.get('load') === 'true';
if (!shouldLoad) {
depends('app:data');
return {};
}
const data = await fetchData();
return { data };
}// Prevent specific reads from triggering reruns
export async function load({ url, untrack }) => {
const filter = url.searchParams.get('filter');
// This won't trigger rerun when stable param changes
const stable = untrack(() => url.searchParams.get('stable'));
return { filter, stable };
}// src/routes/upload/+page.server.ts
import { fail } from '@sveltejs/kit';
export const actions = {
upload: async ({ request }) => {
const data = await request.formData();
const file = data.get('file') as File;
// Stream large files
if (file.size > 100 * 1024 * 1024) {
const stream = file.stream();
const url = await streamToStorage(stream);
return { url };
}
// Handle smaller files in memory
const buffer = await file.arrayBuffer();
const url = await uploadBuffer(buffer);
return { url };
}
};// Complex validation logic
export const actions = {
register: async ({ request }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
const confirmPassword = data.get('confirmPassword');
const issues = [];
if (!email || !isValidEmail(email)) {
issues.push({ path: ['email'], message: 'Invalid email' });
}
if (password !== confirmPassword) {
issues.push({ path: ['password'], message: 'Passwords do not match' });
issues.push({ path: ['confirmPassword'], message: 'Passwords do not match' });
}
if (issues.length > 0) {
invalid(...issues);
}
// Continue with registration
}
};import { beforeNavigate } from '$app/navigation';
let hasUnsavedChanges = false;
beforeNavigate(({ cancel }) => {
if (hasUnsavedChanges) {
if (!confirm('You have unsaved changes. Leave anyway?')) {
cancel();
}
}
});// Restore application state from URL
export async function load({ url, parent }) {
const stateParam = url.searchParams.get('state');
if (stateParam) {
const state = JSON.parse(decodeURIComponent(stateParam));
return { restoredState: state };
}
return {};
}// Retry failed requests
async function loadWithRetry(fetch: typeof fetch, url: string, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url);
if (response.ok) return await response.json();
throw new Error(`HTTP ${response.status}`);
} catch (e) {
if (i === maxRetries - 1) throw e;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
export async function load({ fetch }) {
const data = await loadWithRetry(fetch, '/api/unreliable');
return { data };
}// Fallback when primary data source fails
export async function load({ fetch }) {
try {
const data = await fetch('/api/primary').then(r => r.json());
return { data, source: 'primary' };
} catch (e) {
const fallback = await fetch('/api/fallback').then(r => r.json());
return { data: fallback, source: 'fallback' };
}
}// src/hooks.server.ts
import { sequence } from '@sveltejs/kit/hooks';
import type { Handle } from '@sveltejs/kit';
const auth: Handle = async ({ event, resolve }) => {
if (event.url.pathname.startsWith('/api')) {
// Skip auth for public API routes
if (event.url.pathname.startsWith('/api/public')) {
return resolve(event);
}
}
// Apply auth for protected routes
const token = event.cookies.get('token');
if (!token) {
return new Response('Unauthorized', { status: 401 });
}
return resolve(event);
};
export const handle = sequence(auth);// Transform requests before processing
export const handle: Handle = async ({ event, resolve }) => {
// Rewrite old API paths
if (event.url.pathname.startsWith('/api/v1')) {
event.url.pathname = event.url.pathname.replace('/api/v1', '/api/v2');
}
return resolve(event);
};// Handle different environments
import { env } from '$env/dynamic/private';
import { dev } from '$app/environment';
const config = {
apiUrl: dev
? 'http://localhost:3000'
: env.PRODUCTION_API_URL || 'https://api.example.com',
features: {
analytics: env.ENABLE_ANALYTICS === 'true',
debug: dev
}
};// Server: src/lib/server/commands.ts
import { command } from '$app/server';
import { getPost } from './queries';
export const updatePost = command('unchecked', async (data: { id: string; title: string }) => {
await db.posts.update(data.id, { title: data.title });
return { success: true };
});
// Client: Use with optimistic update
import { updatePost } from '$lib/server/commands';
import { getPost } from '$lib/server/queries';
const post = getPost('post-123');
await updatePost({ id: 'post-123', title: 'New Title' })
.updates(post.withOverride((current) => ({ ...current, title: 'New Title' })));// Batch multiple operations
import { query } from '$app/server';
export const getUsers = query.batch('unchecked', async (userIds: string[]) => {
const users = await db.users.findMany(userIds);
return (userId, idx) => users[idx];
});
// Client automatically batches concurrent calls
const [user1, user2, user3] = await Promise.all([
getUsers('id-1'),
getUsers('id-2'),
getUsers('id-3')
]);
// Only one server request is made!// Lazy load heavy components
<script>
import { onMount } from 'svelte';
let HeavyComponent;
onMount(async () => {
HeavyComponent = (await import('./HeavyComponent.svelte')).default;
});
</script>
{#if HeavyComponent}
<svelte:component this={HeavyComponent} />
{/if}// Prefetch data on hover
import { preloadData } from '$app/navigation';
function handleMouseEnter(href: string) {
preloadData(href);
}