Exemples SvelteKit

Exemples de framework SvelteKit - Le moyen le plus rapide de construire des apps Svelte avec SSR, routing et fonctionnalités web modernes

💻 SvelteKit Bases et Composants svelte

🟢 simple ⭐⭐

Concepts de base SvelteKit incluant composants, pages, routing et fonctionnalité de base

⏱️ 25 min 🏷️ sveltekit, svelte, frontend, components, routing
Prerequisites: HTML/CSS basics, JavaScript ES6+, Basic web concepts
<!-- SvelteKit Basics and Components -->

<!-- 1. Basic SvelteKit App Structure -->
<!--
my-sveltekit-app/
├── src/
│   ├── lib/
│   │   └── components/
│   │       ├── Header.svelte
│   │       └── Footer.svelte
│   ├── routes/
│   │   ├── +page.svelte          # Home page
│   │   ├── about/
│   │   │   └── +page.svelte      # About page
│   │   ├── api/
│   │   │   └── users/
│   │   │       └── +server.js    # API route
│   │   └── blog/
│   │       ├── [slug]/
│   │       │   └── +page.svelte  # Dynamic route
├── static/                       # Static assets
├── svelte.config.js
└── package.json
-->

<!-- 2. Main Layout (src/routes/+layout.svelte) -->
<script>
	import '../app.css';
	import Header from '$lib/components/Header.svelte';
	import Footer from '$lib/components/Footer.svelte';
	import { page } from '$app/stores';
</script>

<div class="app">
	<Header />

	<main>
		<slot />
	</main>

	<Footer />
</div>

<style>
	.app {
		min-height: 100vh;
		display: flex;
		flex-direction: column;
		font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
	}

	main {
		flex: 1;
		padding: 2rem;
		max-width: 1200px;
		margin: 0 auto;
		width: 100%;
	}
</style>

<!-- 3. Home Page (src/routes/+page.svelte) -->
<script>
	import { onMount } from 'svelte';
	import Link from 'svelte-routing';
	import Counter from '$lib/components/Counter.svelte';

	let featuredPosts = [];
	let loading = true;

	onMount(async () => {
		// Simulate API call
		await new Promise(resolve => setTimeout(resolve, 1000));
		featuredPosts = [
			{
				id: 1,
				title: 'Getting Started with SvelteKit',
				excerpt: 'Learn the basics of SvelteKit and how to build your first application.',
				date: '2024-01-15',
				tags: ['SvelteKit', 'Tutorial', 'Frontend']
			},
			{
				id: 2,
				title: 'Server-Side Rendering with SvelteKit',
				excerpt: 'Understanding SSR and how it improves performance and SEO.',
				date: '2024-01-10',
				tags: ['SSR', 'Performance', 'SEO']
			},
			{
				id: 3,
				title: 'Building API Routes in SvelteKit',
				excerpt: 'Create powerful backend APIs using SvelteKit server routes.',
				date: '2024-01-05',
				tags: ['API', 'Backend', 'Full-stack']
			}
		];
		loading = false;
	});
</script>

<svelte:head>
	<title>Welcome to SvelteKit</title>
	<meta name="description" content="Get started with SvelteKit - the fastest way to build web apps" />
</svelte:head>

<section class="hero">
	<h1>Welcome to SvelteKit</h1>
	<p>The fastest way to build Svelte apps with SSR, routing, and modern web features</p>

	<div class="hero-actions">
		<a href="/about" class="btn btn-primary">Learn More</a>
		<a href="/docs" class="btn btn-secondary">Documentation</a>
	</div>
</section>

<section class="features">
	<h2>Why SvelteKit?</h2>
	<div class="features-grid">
		<div class="feature-card">
			<h3>⚡ Lightning Fast</h3>
			<p>Compile-time optimizations make your apps incredibly fast and small</p>
		</div>
		<div class="feature-card">
			<h3>🔄 Server-Side Rendering</h3>
			<p>Built-in SSR for better performance and SEO out of the box</p>
		</div>
		<div class="feature-card">
			<h3>🎯 File-based Routing</h3>
			<p>Intuitive routing system based on your file structure</p>
		</div>
		<div class="feature-card">
			<h3>📱 Progressive Enhancement</h3>
			<p>Apps work everywhere, with or without JavaScript</p>
		</div>
	</div>
</section>

<section class="interactive">
	<h2>Try Svelte Reactivity</h2>
	<Counter />
</section>

<section class="blog">
	<h2>Latest Posts</h2>

	{#if loading}
		<div class="loading">Loading posts...</div>
	{:else if featuredPosts.length > 0}
		<div class="posts-grid">
			{#each featuredPosts as post (post.id)}
				<article class="post-card">
					<a href="/blog/{post.id}">
						<div class="post-meta">
							<time datetime={post.date}>
								{new Date(post.date).toLocaleDateString()}
							</time>
						</div>
						<h3>{post.title}</h3>
						<p>{post.excerpt}</p>
						<div class="post-tags">
							{#each post.tags as tag}
								<span class="tag">{tag}</span>
							{/each}
						</div>
					</a>
				</article>
			{/each}
		</div>
	{:else}
		<p>No posts found.</p>
	{/if}
</section>

<style>
	.hero {
		text-align: center;
		padding: 4rem 0;
		background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
		color: white;
		margin: -2rem -2rem 2rem -2rem;
	}

	.hero h1 {
		font-size: 3rem;
		margin-bottom: 1rem;
		font-weight: 700;
	}

	.hero p {
		font-size: 1.2rem;
		margin-bottom: 2rem;
		opacity: 0.9;
	}

	.hero-actions {
		display: flex;
		gap: 1rem;
		justify-content: center;
		flex-wrap: wrap;
	}

	.btn {
		padding: 0.75rem 1.5rem;
		border-radius: 0.5rem;
		text-decoration: none;
		font-weight: 500;
		transition: all 0.3s ease;
		display: inline-block;
	}

	.btn-primary {
		background-color: white;
		color: #667eea;
	}

	.btn-primary:hover {
		transform: translateY(-2px);
		box-shadow: 0 4px 12px rgba(0,0,0,0.15);
	}

	.btn-secondary {
		background-color: transparent;
		color: white;
		border: 2px solid white;
	}

	.btn-secondary:hover {
		background-color: white;
		color: #667eea;
	}

	.features {
		margin: 4rem 0;
	}

	.features h2 {
		text-align: center;
		margin-bottom: 3rem;
		font-size: 2rem;
		color: #333;
	}

	.features-grid {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
		gap: 2rem;
	}

	.feature-card {
		text-align: center;
		padding: 2rem;
		background: #f8f9fa;
		border-radius: 1rem;
		transition: transform 0.3s ease;
	}

	.feature-card:hover {
		transform: translateY(-5px);
	}

	.feature-card h3 {
		margin-bottom: 1rem;
		font-size: 1.5rem;
		color: #333;
	}

	.interactive {
		margin: 4rem 0;
		text-align: center;
		padding: 2rem;
		background: #f0f4ff;
		border-radius: 1rem;
	}

	.interactive h2 {
		margin-bottom: 2rem;
		color: #333;
	}

	.blog {
		margin: 4rem 0;
	}

	.blog h2 {
		margin-bottom: 2rem;
		font-size: 2rem;
		color: #333;
	}

	.loading {
		text-align: center;
		padding: 2rem;
		font-size: 1.1rem;
		color: #666;
	}

	.posts-grid {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
		gap: 2rem;
	}

	.post-card {
		background: white;
		border-radius: 0.75rem;
		overflow: hidden;
		box-shadow: 0 2px 10px rgba(0,0,0,0.1);
		transition: transform 0.3s ease, box-shadow 0.3s ease;
	}

	.post-card:hover {
		transform: translateY(-5px);
		box-shadow: 0 4px 20px rgba(0,0,0,0.15);
	}

	.post-card a {
		display: block;
		padding: 1.5rem;
		text-decoration: none;
		color: inherit;
	}

	.post-meta {
		margin-bottom: 1rem;
		color: #666;
		font-size: 0.9rem;
	}

	.post-card h3 {
		margin-bottom: 0.75rem;
		color: #333;
		font-size: 1.25rem;
		line-height: 1.3;
	}

	.post-card p {
		color: #666;
		line-height: 1.6;
		margin-bottom: 1rem;
	}

	.post-tags {
		display: flex;
		gap: 0.5rem;
		flex-wrap: wrap;
	}

	.tag {
		background: #e3f2fd;
		color: #1976d2;
		padding: 0.25rem 0.75rem;
		border-radius: 1rem;
		font-size: 0.8rem;
		font-weight: 500;
	}

	@media (max-width: 768px) {
		.hero {
			padding: 2rem 1rem;
			margin: -1rem -1rem 1rem -1rem;
		}

		.hero h1 {
			font-size: 2rem;
		}

		.hero-actions {
			flex-direction: column;
			align-items: center;
		}

		.features-grid,
		.posts-grid {
			grid-template-columns: 1fr;
		}
	}
</style>

<!-- 4. Reusable Counter Component (src/lib/components/Counter.svelte) -->
<script>
	let count = 0;

	function increment() {
		count += 1;
	}

	function decrement() {
		count -= 1;
	}

	function reset() {
		count = 0;
	}
</script>

<div class="counter">
	<h3>Interactive Counter: {count}</h3>

	<div class="counter-controls">
		<button on:click={decrement} class="btn btn-decrement">−</button>
		<span class="count-display">{count}</span>
		<button on:click={increment} class="btn btn-increment">+</button>
	</div>

	<button on:click={reset} class="btn btn-reset">Reset</button>

	<p>
		This is a simple Svelte component demonstrating reactivity.
		Click the buttons to see the count update in real-time!
	</p>
</div>

<style>
	.counter {
		text-align: center;
		padding: 2rem;
		background: white;
		border-radius: 1rem;
		box-shadow: 0 2px 10px rgba(0,0,0,0.1);
		max-width: 400px;
		margin: 0 auto;
	}

	.counter h3 {
		margin-bottom: 1.5rem;
		color: #333;
	}

	.counter-controls {
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 1rem;
		margin-bottom: 1rem;
	}

	.btn {
		padding: 0.5rem 1rem;
		border: none;
		border-radius: 0.5rem;
		font-size: 1rem;
		font-weight: 500;
		cursor: pointer;
		transition: all 0.3s ease;
	}

	.btn-increment {
		background-color: #4caf50;
		color: white;
	}

	.btn-increment:hover {
		background-color: #45a049;
	}

	.btn-decrement {
		background-color: #f44336;
		color: white;
	}

	.btn-decrement:hover {
		background-color: #da190b;
	}

	.btn-reset {
		background-color: #ff9800;
		color: white;
	}

	.btn-reset:hover {
		background-color: #e68900;
	}

	.count-display {
		font-size: 1.5rem;
		font-weight: bold;
		min-width: 3rem;
		text-align: center;
		color: #333;
	}
</style>

<!-- 5. Header Component (src/lib/components/Header.svelte) -->
<script>
	import { page } from '$app/stores';
</script>

<header class="header">
	<nav class="nav">
		<div class="nav-brand">
			<a href="/" class="logo">SvelteKit</a>
		</div>

		<ul class="nav-links">
			<li>
				<a href="/" class:active={$page.url.pathname === '/'}>
					Home
				</a>
			</li>
			<li>
				<a href="/about" class:active={$page.url.pathname === '/about'}>
					About
				</a>
			</li>
			<li>
				<a href="/blog" class:active={$page.url.pathname.startsWith('/blog')}>
					Blog
				</a>
			</li>
			<li>
				<a href="/api/users" class:active={$page.url.pathname === '/api/users'}>
					API
				</a>
			</li>
		</ul>

		<div class="nav-toggle">
			<input type="checkbox" id="nav-checkbox" class="nav-checkbox">
			<label for="nav-checkbox" class="nav-toggle-label">
				<span></span>
			</label>
		</div>
	</nav>
</header>

<style>
	.header {
		background-color: white;
		box-shadow: 0 2px 4px rgba(0,0,0,0.1);
		position: sticky;
		top: 0;
		z-index: 1000;
	}

	.nav {
		display: flex;
		justify-content: space-between;
		align-items: center;
		padding: 1rem 2rem;
		max-width: 1200px;
		margin: 0 auto;
	}

	.nav-brand .logo {
		font-size: 1.5rem;
		font-weight: bold;
		color: #ff3e00;
		text-decoration: none;
		transition: color 0.3s ease;
	}

	.nav-brand .logo:hover {
		color: #e63900;
	}

	.nav-links {
		display: flex;
		list-style: none;
		margin: 0;
		padding: 0;
		gap: 2rem;
	}

	.nav-links a {
		color: #333;
		text-decoration: none;
		font-weight: 500;
		padding: 0.5rem 1rem;
		border-radius: 0.5rem;
		transition: all 0.3s ease;
	}

	.nav-links a:hover {
		color: #ff3e00;
		background-color: #fff3f0;
	}

	.nav-links a.active {
		color: #ff3e00;
		background-color: #fff3f0;
	}

	.nav-checkbox {
		display: none;
	}

	.nav-toggle-label {
		display: none;
		flex-direction: column;
		cursor: pointer;
	}

	.nav-toggle-label span {
		width: 25px;
		height: 3px;
		background-color: #333;
		margin: 3px 0;
		transition: 0.3s;
	}

	@media (max-width: 768px) {
		.nav-links {
			position: fixed;
			top: 0;
			right: -100%;
			width: 80%;
			max-width: 300px;
			height: 100vh;
			background: white;
			flex-direction: column;
			justify-content: center;
			align-items: center;
			transition: right 0.3s ease;
			box-shadow: -2px 0 5px rgba(0,0,0,0.1);
			gap: 0;
		}

		.nav-links li {
			width: 100%;
			text-align: center;
			padding: 1rem 0;
		}

		.nav-links a {
			display: block;
			padding: 1rem;
			width: 100%;
		}

		.nav-toggle-label {
			display: flex;
		}

		.nav-checkbox:checked ~ .nav-links {
			right: 0;
		}

		.nav-checkbox:checked ~ .nav-toggle-label span:nth-child(1) {
			transform: rotate(-45deg) translate(-5px, 6px);
		}

		.nav-checkbox:checked ~ .nav-toggle-label span:nth-child(2) {
			opacity: 0;
		}

		.nav-checkbox:checked ~ .nav-toggle-label span:nth-child(3) {
			transform: rotate(45deg) translate(-5px, -6px);
		}
	}
</style>

<!-- 6. About Page (src/routes/about/+page.svelte) -->
<script>
	import { onMount } from 'svelte';

	let team = [];
	let loading = true;
	let stats = {
		projects: 0,
		clients: 0,
		years: 0,
		coffee: 0
	};

	onMount(async () => {
		// Simulate API calls
		await Promise.all([
			loadTeam(),
			animateStats()
		]);
		loading = false;
	});

	async function loadTeam() {
		await new Promise(resolve => setTimeout(resolve, 800));
		team = [
			{
				id: 1,
				name: 'Sarah Chen',
				role: 'Lead Developer',
				bio: 'Full-stack developer with expertise in SvelteKit and modern web technologies.',
				avatar: 'SC',
				skills: ['SvelteKit', 'TypeScript', 'Node.js']
			},
			{
				id: 2,
				name: 'Marcus Johnson',
				role: 'UI/UX Designer',
				bio: 'Creative designer passionate about user experience and interface design.',
				avatar: 'MJ',
				skills: ['Design Systems', 'Prototyping', 'User Research']
			},
			{
				id: 3,
				name: 'Elena Rodriguez',
				role: 'Backend Engineer',
				bio: 'Backend specialist focusing on APIs, databases, and cloud architecture.',
				avatar: 'ER',
				skills: ['API Design', 'PostgreSQL', 'AWS']
			}
		];
	}

	async function animateStats() {
		const targets = { projects: 127, clients: 89, years: 7, coffee: 2847 };

		for (const [key, target] of Object.entries(targets)) {
			let current = 0;
			const increment = target / 50;

			const timer = setInterval(() => {
				current += increment;
				if (current >= target) {
					current = target;
					clearInterval(timer);
				}
				stats[key] = Math.floor(current);
			}, 30);
		}
	}
</script>

<svelte:head>
	<title>About Us - SvelteKit App</title>
	<meta name="description" content="Learn more about our team and what we do with SvelteKit" />
</svelte:head>

<section class="hero">
	<div class="hero-content">
		<h1>About Our Company</h1>
		<p>We build amazing web experiences with SvelteKit, combining performance, developer experience, and modern best practices.</p>
	</div>
</section>

<section class="stats">
	<div class="stats-grid">
		<div class="stat-card">
			<div class="stat-number">{stats.projects}</div>
			<div class="stat-label">Projects Completed</div>
		</div>
		<div class="stat-card">
			<div class="stat-number">{stats.clients}</div>
			<div class="stat-label">Happy Clients</div>
		</div>
		<div class="stat-card">
			<div class="stat-number">{stats.years}</div>
			<div class="stat-label">Years Experience</div>
		</div>
		<div class="stat-card">
			<div class="stat-number">{stats.coffee}</div>
			<div class="stat-label">Cups of Coffee</div>
		</div>
	</div>
</section>

<section class="mission">
	<div class="mission-content">
		<h2>Our Mission</h2>
		<p>To create fast, accessible, and beautiful web applications that provide exceptional user experiences while maintaining developer productivity and joy.</p>

		<div class="values">
			<div class="value">
				<h3>🚀 Performance</h3>
				<p>We prioritize speed and efficiency in everything we build.</p>
			</div>
			<div class="value">
				<h3>♿ Accessibility</h3>
				<p>We believe the web should be usable by everyone, everywhere.</p>
			</div>
			<div class="value">
				<h3>🎨 Design Excellence</h3>
				<p>Beautiful interfaces that are both functional and delightful.</p>
			</div>
			<div class="value">
				<h3>🔧 Developer Experience</h3>
				<p>Tools and processes that make development a joy, not a chore.</p>
			</div>
		</div>
	</div>
</section>

<section class="team">
	<div class="team-content">
		<h2>Meet Our Team</h2>

		{#if loading}
			<div class="loading">Loading team members...</div>
		{:else if team.length > 0}
			<div class="team-grid">
				{#each team as member (member.id)}
					<div class="team-member">
						<div class="member-avatar">
							{member.avatar}
						</div>
						<h3>{member.name}</h3>
						<p class="member-role">{member.role}</p>
						<p class="member-bio">{member.bio}</p>
						<div class="member-skills">
							{#each member.skills as skill}
								<span class="skill-tag">{skill}</span>
							{/each}
						</div>
					</div>
				{/each}
			</div>
		{:else}
			<p>No team members found.</p>
		{/if}
	</div>
</section>

<style>
	.hero {
		text-align: center;
		padding: 4rem 2rem;
		background: linear-gradient(135deg, #ff3e00 0%, #ff9e00 100%);
		color: white;
		margin: -2rem -2rem 0 -2rem;
	}

	.hero-content h1 {
		font-size: 2.5rem;
		margin-bottom: 1rem;
		font-weight: 700;
	}

	.hero-content p {
		font-size: 1.2rem;
		max-width: 600px;
		margin: 0 auto;
		opacity: 0.9;
	}

	.stats {
		padding: 4rem 2rem;
		background: #f8f9fa;
	}

	.stats-grid {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
		gap: 2rem;
		max-width: 800px;
		margin: 0 auto;
	}

	.stat-card {
		text-align: center;
		padding: 2rem;
		background: white;
		border-radius: 1rem;
		box-shadow: 0 2px 10px rgba(0,0,0,0.1);
		transition: transform 0.3s ease;
	}

	.stat-card:hover {
		transform: translateY(-5px);
	}

	.stat-number {
		font-size: 2.5rem;
		font-weight: bold;
		color: #ff3e00;
		margin-bottom: 0.5rem;
	}

	.stat-label {
		color: #666;
		font-weight: 500;
	}

	.mission {
		padding: 4rem 2rem;
	}

	.mission-content {
		max-width: 800px;
		margin: 0 auto;
		text-align: center;
	}

	.mission h2 {
		font-size: 2rem;
		color: #333;
		margin-bottom: 1rem;
	}

	.mission > p {
		font-size: 1.1rem;
		color: #666;
		line-height: 1.6;
		margin-bottom: 3rem;
	}

	.values {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
		gap: 2rem;
		margin-top: 2rem;
	}

	.value {
		text-align: center;
		padding: 1.5rem;
	}

	.value h3 {
		font-size: 1.5rem;
		margin-bottom: 1rem;
		color: #333;
	}

	.value p {
		color: #666;
		line-height: 1.6;
	}

	.team {
		padding: 4rem 2rem;
		background: #f8f9fa;
	}

	.team-content {
		max-width: 1000px;
		margin: 0 auto;
	}

	.team h2 {
		font-size: 2rem;
		color: #333;
		text-align: center;
		margin-bottom: 3rem;
	}

	.loading {
		text-align: center;
		padding: 2rem;
		font-size: 1.1rem;
		color: #666;
	}

	.team-grid {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
		gap: 2rem;
	}

	.team-member {
		background: white;
		padding: 2rem;
		border-radius: 1rem;
		text-align: center;
		box-shadow: 0 2px 10px rgba(0,0,0,0.1);
		transition: transform 0.3s ease;
	}

	.team-member:hover {
		transform: translateY(-5px);
	}

	.member-avatar {
		width: 80px;
		height: 80px;
		background: linear-gradient(135deg, #ff3e00, #ff9e00);
		color: white;
		border-radius: 50%;
		display: flex;
		align-items: center;
		justify-content: center;
		font-size: 1.5rem;
		font-weight: bold;
		margin: 0 auto 1.5rem;
	}

	.member-role {
		color: #ff3e00;
		font-weight: 500;
		margin-bottom: 1rem;
	}

	.member-bio {
		color: #666;
		line-height: 1.6;
		margin-bottom: 1.5rem;
	}

	.member-skills {
		display: flex;
		flex-wrap: wrap;
		gap: 0.5rem;
		justify-content: center;
	}

	.skill-tag {
		background: #e3f2fd;
		color: #1976d2;
		padding: 0.25rem 0.75rem;
		border-radius: 1rem;
		font-size: 0.8rem;
		font-weight: 500;
	}

	@media (max-width: 768px) {
		.hero {
			padding: 2rem 1rem;
			margin: -1rem -1rem 0 -1rem;
		}

		.hero-content h1 {
			font-size: 2rem;
		}

		.stats-grid,
		.values,
		.team-grid {
			grid-template-columns: 1fr;
		}

		.stats,
		.mission,
		.team {
			padding: 2rem 1rem;
		}
	}
</style>

💻 Routes API et Gestion de Données SvelteKit javascript

🟡 intermediate ⭐⭐⭐⭐

Construction d'APIs RESTful avec routes serveur SvelteKit, gestion de formulaires et gestion de données

⏱️ 30 min 🏷️ sveltekit, api, backend, database, authentication
Prerequisites: SvelteKit basics, REST APIs, JavaScript async/await, Database concepts
// SvelteKit API Routes and Data Handling

// 1. Simple API Route (src/routes/api/users/+server.js)
import { json } from '@sveltejs/kit';

// Mock user data (in real app, this would come from a database)
let users = [
  { id: 1, name: 'Sarah Chen', email: '[email protected]', age: 28, role: 'developer' },
  { id: 2, name: 'Marcus Johnson', email: '[email protected]', age: 32, role: 'designer' },
  { id: 3, name: 'Elena Rodriguez', email: '[email protected]', age: 30, role: 'manager' }
];

let nextId = 4;

// GET method - Fetch users
export async function GET({ url }) {
  const searchParams = url.searchParams;
  const id = searchParams.get('id');
  const role = searchParams.get('role');

  try {
    if (id) {
      // Get single user
      const user = users.find(u => u.id === parseInt(id));
      if (!user) {
        return json({ error: 'User not found' }, { status: 404 });
      }
      return json({ user });
    } else {
      // Get all users with optional filtering
      let filteredUsers = users;

      if (role) {
        filteredUsers = users.filter(u => u.role === role);
      }

      return json({
        users: filteredUsers,
        total: filteredUsers.length,
        timestamp: new Date().toISOString()
      });
    }
  } catch (error) {
    console.error('GET users error:', error);
    return json({ error: 'Internal server error' }, { status: 500 });
  }
}

// POST method - Create new user
export async function POST({ request }) {
  try {
    const data = await request.json();
    const { name, email, age, role = 'developer' } = data;

    // Validation
    if (!name || !email || !age) {
      return json({
        error: 'Missing required fields: name, email, age'
      }, { status: 400 });
    }

    if (typeof age !== 'number' || age < 18 || age > 100) {
      return json({
        error: 'Age must be a number between 18 and 100'
      }, { status: 400 });
    }

    // Check if email already exists
    if (users.some(u => u.email === email)) {
      return json({
        error: 'Email already exists'
      }, { status: 409 });
    }

    // Create new user
    const newUser = {
      id: nextId++,
      name,
      email,
      age,
      role,
      createdAt: new Date().toISOString()
    };

    users.push(newUser);

    return json({
      message: 'User created successfully',
      user: newUser
    }, { status: 201 });

  } catch (error) {
    console.error('POST users error:', error);
    return json({ error: 'Invalid JSON data' }, { status: 400 });
  }
}

// PUT method - Update user
export async function PUT({ request, url }) {
  try {
    const id = url.searchParams.get('id');
    const updateData = await request.json();

    if (!id) {
      return json({ error: 'User ID is required' }, { status: 400 });
    }

    const userIndex = users.findIndex(u => u.id === parseInt(id));
    if (userIndex === -1) {
      return json({ error: 'User not found' }, { status: 404 });
    }

    // Update user (merge with existing data)
    const updatedUser = {
      ...users[userIndex],
      ...updateData,
      id: users[userIndex].id,
      updatedAt: new Date().toISOString()
    };

    users[userIndex] = updatedUser;

    return json({
      message: 'User updated successfully',
      user: updatedUser
    });

  } catch (error) {
    console.error('PUT users error:', error);
    return json({ error: 'Invalid request data' }, { status: 400 });
  }
}

// DELETE method - Delete user
export async function DELETE({ url }) {
  try {
    const id = url.searchParams.get('id');

    if (!id) {
      return json({ error: 'User ID is required' }, { status: 400 });
    }

    const userIndex = users.findIndex(u => u.id === parseInt(id));
    if (userIndex === -1) {
      return json({ error: 'User not found' }, { status: 404 });
    }

    const deletedUser = users.splice(userIndex, 1)[0];

    return json({
      message: 'User deleted successfully',
      user: deletedUser
    });

  } catch (error) {
    console.error('DELETE users error:', error);
    return json({ error: 'Internal server error' }, { status: 500 });
  }
}

// 2. Advanced API Route with Database Integration (src/routes/api/products/+server.js)
import { json } from '@sveltejs/kit';
import { Client } from 'pg';

// Database connection
const client = new Client({
  connectionString: process.env.DATABASE_URL || 'postgresql://user:password@localhost:5432/myapp'
});

// Initialize database connection
async function initDB() {
  try {
    await client.connect();
    console.log('Connected to database');
  } catch (error) {
    console.error('Database connection failed:', error);
    throw error;
  }
}

// GET products with pagination and filtering
export async function GET({ url }) {
  try {
    await initDB();

    const searchParams = url.searchParams;
    const page = parseInt(searchParams.get('page')) || 1;
    const limit = parseInt(searchParams.get('limit')) || 10;
    const category = searchParams.get('category');
    const minPrice = searchParams.get('minPrice');
    const maxPrice = searchParams.get('maxPrice');
    const search = searchParams.get('search');

    const offset = (page - 1) * limit;

    // Build query
    let query = 'SELECT * FROM products WHERE 1=1';
    const queryParams = [];
    let paramCount = 0;

    if (category) {
      query += ` AND category = $${++paramCount}`;
      queryParams.push(category);
    }

    if (minPrice) {
      query += ` AND price >= $${++paramCount}`;
      queryParams.push(parseFloat(minPrice));
    }

    if (maxPrice) {
      query += ` AND price <= $${++paramCount}`;
      queryParams.push(parseFloat(maxPrice));
    }

    if (search) {
      query += ` AND (name ILIKE $${++paramCount} OR description ILIKE $${++paramCount})`;
      queryParams.push(`%${search}%`, `%${search}%`);
    }

    // Count total products
    const countQuery = query.replace('SELECT * FROM', 'SELECT COUNT(*) FROM');
    const countResult = await client.query(countQuery, queryParams);
    const total = parseInt(countResult.rows[0].count);

    // Get paginated products
    query += ` ORDER BY created_at DESC LIMIT $${++paramCount} OFFSET $${++paramCount}`;
    queryParams.push(limit, offset);

    const result = await client.query(query, queryParams);

    return json({
      products: result.rows,
      pagination: {
        currentPage: page,
        totalPages: Math.ceil(total / limit),
        totalItems: total,
        itemsPerPage: limit,
        hasNext: page < Math.ceil(total / limit),
        hasPrev: page > 1
      },
      filters: {
        category,
        minPrice,
        maxPrice,
        search
      }
    });

  } catch (error) {
    console.error('GET products error:', error);
    return json({ error: 'Failed to fetch products' }, { status: 500 });
  } finally {
    await client.end();
  }
}

// POST new product
export async function POST({ request }) {
  try {
    await initDB();

    const productData = await request.json();
    const { name, description, price, category, inStock = true, images = [] } = productData;

    // Validation
    if (!name || !price || !category) {
      return json({
        error: 'Missing required fields: name, price, category'
      }, { status: 400 });
    }

    if (typeof price !== 'number' || price <= 0) {
      return json({
        error: 'Price must be a positive number'
      }, { status: 400 });
    }

    // Insert product
    const query = `
      INSERT INTO products (name, description, price, category, in_stock, images, created_at, updated_at)
      VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW())
      RETURNING *
    `;

    const values = [name, description, price, category, inStock, JSON.stringify(images)];
    const result = await client.query(query, values);

    return json({
      message: 'Product created successfully',
      product: result.rows[0]
    }, { status: 201 });

  } catch (error) {
    console.error('POST products error:', error);
    return json({ error: 'Failed to create product' }, { status: 500 });
  } finally {
    await client.end();
  }
}

// 3. File Upload API (src/routes/api/upload/+server.js)
import { json } from '@sveltejs/kit';
import { writeFile, mkdir } from 'fs/promises';
import { join } from 'path';
import { v4 as uuidv4 } from 'uuid';

const UPLOAD_DIR = 'static/uploads';

// Ensure upload directory exists
async function ensureUploadDir() {
  try {
    await mkdir(UPLOAD_DIR, { recursive: true });
  } catch (error) {
    if (error.code !== 'EEXIST') {
      throw error;
    }
  }
}

export async function POST({ request }) {
  try {
    await ensureUploadDir();

    const formData = await request.formData();
    const file = formData.get('file');
    const description = formData.get('description') || '';

    if (!file) {
      return json({ error: 'No file provided' }, { status: 400 });
    }

    // Validate file type
    const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    if (!allowedTypes.includes(file.type)) {
      return json({
        error: 'Invalid file type. Only JPEG, PNG, GIF, and WebP are allowed.'
      }, { status: 400 });
    }

    // Validate file size (5MB limit)
    const maxSize = 5 * 1024 * 1024; // 5MB
    if (file.size > maxSize) {
      return json({
        error: 'File too large. Maximum size is 5MB.'
      }, { status: 400 });
    }

    // Generate unique filename
    const fileExtension = file.name.split('.').pop();
    const fileName = `${uuidv4()}.${fileExtension}`;
    const filePath = join(UPLOAD_DIR, fileName);

    // Save file
    const buffer = Buffer.from(await file.arrayBuffer());
    await writeFile(filePath, buffer);

    // Return file info
    return json({
      message: 'File uploaded successfully',
      file: {
        name: file.name,
        size: file.size,
        type: file.type,
        url: `/uploads/${fileName}`,
        description,
        uploadedAt: new Date().toISOString()
      }
    }, { status: 201 });

  } catch (error) {
    console.error('File upload error:', error);
    return json({ error: 'Failed to upload file' }, { status: 500 });
  }
}

// 4. Contact Form API (src/routes/api/contact/+server.js)
import { json } from '@sveltejs/kit';
import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST({ request }) {
  try {
    const { name, email, subject, message } = await request.json();

    // Validation
    if (!name || !email || !subject || !message) {
      return json({
        error: 'All fields are required: name, email, subject, message'
      }, { status: 400 });
    }

    // Email validation
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) {
      return json({
        error: 'Invalid email address'
      }, { status: 400 });
    }

    // Send email
    try {
      const { data, error } = await resend.emails.send({
        from: '[email protected]',
        to: '[email protected]',
        subject: `Contact Form: ${subject}`,
        html: `
          <h2>New Contact Form Submission</h2>
          <p><strong>Name:</strong> ${name}</p>
          <p><strong>Email:</strong> ${email}</p>
          <p><strong>Subject:</strong> ${subject}</p>
          <p><strong>Message:</strong></p>
          <p>${message.replace(/\n/g, '<br>')}</p>
        `
      });

      if (error) {
        console.error('Email send error:', error);
        return json({
          error: 'Failed to send message'
        }, { status: 500 });
      }

      // Send confirmation email to user
      await resend.emails.send({
        from: '[email protected]',
        to: email,
        subject: 'Thank you for contacting us',
        html: `
          <h2>Thank you for contacting us!</h2>
          <p>Dear ${name},</p>
          <p>We have received your message and will get back to you as soon as possible.</p>
          <p><strong>Your message:</strong></p>
          <p>${message.replace(/\n/g, '<br>')}</p>
          <p>Best regards,<br>The Team</p>
        `
      });

      return json({
        message: 'Message sent successfully',
        id: data.id
      });

    } catch (emailError) {
      console.error('Email service error:', emailError);
      return json({
        error: 'Failed to send message. Please try again later.'
      }, { status: 500 });
    }

  } catch (error) {
    console.error('Contact form error:', error);
    return json({
      error: 'Invalid request data'
    }, { status: 400 });
  }
}

// 5. Authentication API (src/routes/api/auth/login/+server.js)
import { json } from '@sveltejs/kit';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';

// Mock user database (in production, use a real database)
const users = [
  {
    id: 1,
    email: '[email protected]',
    password: '$2b$10$rJ8H8QzvGYHZJGZ8YwK2X.3Qh8h8h8h8h8h8h8h8h8h8h8h8h8h8h8h8h8', // 'password'
    name: 'Admin User',
    role: 'admin'
  },
  {
    id: 2,
    email: '[email protected]',
    password: '$2b$10$rJ8H8QzvGYHZJGZ8YwK2X.3Qh8h8h8h8h8h8h8h8h8h8h8h8h8h8h8h8h8', // 'password'
    name: 'Regular User',
    role: 'user'
  }
];

const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';

export async function POST({ request, cookies }) {
  try {
    const { email, password } = await request.json();

    // Validation
    if (!email || !password) {
      return json({
        error: 'Email and password are required'
      }, { status: 400 });
    }

    // Find user
    const user = users.find(u => u.email === email);
    if (!user) {
      return json({
        error: 'Invalid credentials'
      }, { status: 401 });
    }

    // Verify password
    const isValidPassword = await bcrypt.compare(password, user.password);
    if (!isValidPassword) {
      return json({
        error: 'Invalid credentials'
      }, { status: 401 });
    }

    // Generate JWT token
    const token = jwt.sign(
      {
        userId: user.id,
        email: user.email,
        role: user.role
      },
      JWT_SECRET,
      { expiresIn: '24h' }
    );

    // Set HTTP-only cookie
    cookies.set('auth-token', token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      maxAge: 60 * 60 * 24, // 24 hours
      path: '/'
    });

    return json({
      message: 'Login successful',
      user: {
        id: user.id,
        email: user.email,
        name: user.name,
        role: user.role
      },
      token
    });

  } catch (error) {
    console.error('Login error:', error);
    return json({
      error: 'Internal server error'
    }, { status: 500 });
  }
}

// 6. Data Loading with load function (src/routes/users/+page.server.js)
export async function load({ fetch }) {
  try {
    const response = await fetch('/api/users');
    if (!response.ok) {
      throw new Error('Failed to fetch users');
    }

    const data = await response.json();
    return {
      users: data.users || [],
      total: data.total || 0,
      timestamp: data.timestamp || new Date().toISOString()
    };
  } catch (error) {
    console.error('Load users error:', error);
    return {
      users: [],
      total: 0,
      error: error.message,
      timestamp: new Date().toISOString()
    };
  }
}

// 7. Form Actions (src/routes/users/+page.server.js)
import { fail, redirect } from '@sveltejs/kit';

export const actions = {
  create: async ({ request }) => {
    const data = await request.formData();
    const name = data.get('name');
    const email = data.get('email');
    const age = data.get('age');
    const role = data.get('role');

    // Validation
    if (!name || !email || !age) {
      return fail(400, {
        error: 'Missing required fields',
        values: { name, email, age, role }
      });
    }

    try {
      const response = await fetch('/api/users', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          name: name.toString(),
          email: email.toString(),
          age: parseInt(age.toString()),
          role: role?.toString() || 'developer'
        })
      });

      if (!response.ok) {
        const errorData = await response.json();
        return fail(response.status, {
          error: errorData.error || 'Failed to create user',
          values: { name, email, age, role }
        });
      }

      return { success: true };

    } catch (error) {
      return fail(500, {
        error: 'Network error. Please try again.',
        values: { name, email, age, role }
      });
    }
  },

  delete: async ({ request }) => {
    const data = await request.formData();
    const id = data.get('id');

    if (!id) {
      return fail(400, { error: 'User ID is required' });
    }

    try {
      const response = await fetch(`/api/users?id=${id}`, {
        method: 'DELETE'
      });

      if (!response.ok) {
        const errorData = await response.json();
        return fail(response.status, {
          error: errorData.error || 'Failed to delete user'
        });
      }

      return { success: true };

    } catch (error) {
      return fail(500, {
        error: 'Network error. Please try again.'
      });
    }
  }
};

💻 Fonctionnalités Avancées SvelteKit javascript

🔴 complex ⭐⭐⭐⭐⭐

Concepts avancés SvelteKit incluant SSR, SSG, actions de formulaire, authentification, middleware et déploiement

⏱️ 45 min 🏷️ sveltekit, ssr, ssg, auth, production, deployment
Prerequisites: Advanced SvelteKit, Database concepts, Authentication patterns, DevOps basics
// SvelteKit Advanced Features

// 1. Advanced Load Functions with Error Handling (src/routes/blog/[slug]/+page.server.js)
import { error } from '@sveltejs/kit';

export async function load({ params, fetch, parent }) {
  const { slug } = params;

  try {
    // Get parent data (from layout load function)
    const parentData = await parent();

    // Fetch blog post
    const postResponse = await fetch(`/api/blog/${slug}`);
    if (!postResponse.ok) {
      if (postResponse.status === 404) {
        throw error(404, 'Blog post not found');
      }
      throw error(500, 'Failed to fetch blog post');
    }

    const postData = await postResponse.json();
    const post = postData.post;

    // Fetch related posts
    const relatedResponse = await fetch(`/api/blog/related?category=${post.category}&exclude=${slug}&limit=3`);
    const relatedData = relatedResponse.ok ? await relatedResponse.json() : { posts: [] };

    // Fetch comments
    const commentsResponse = await fetch(`/api/blog/${slug}/comments`);
    const commentsData = commentsResponse.ok ? await commentsResponse.json() : { comments: [] };

    return {
      post,
      relatedPosts: relatedData.posts || [],
      comments: commentsData.comments || [],
      parentData
    };

  } catch (err) {
    // If it's already a SvelteKit error, re-throw it
    if (err.status) {
      throw err;
    }

    console.error('Load function error:', err);
    throw error(500, 'Internal server error');
  }
}

// 2. Universal Load Function (works on server and client) (src/routes/posts/[id]/+page.js)
import { goto } from '$app/navigation';

export async function load({ params, fetch }) {
  const { id } = params;

  // This function runs on both server and client
  const fetchPost = async () => {
    try {
      const response = await fetch(`/api/posts/${id}`);
      if (!response.ok) {
        if (response.status === 404) {
          // On client, redirect to 404 page
          if (typeof window !== 'undefined') {
            await goto('/404', { replaceState: true });
          }
          return { post: null, error: 'Post not found' };
        }
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const data = await response.json();
      return { post: data.post, error: null };
    } catch (err) {
      console.error('Failed to fetch post:', err);
      return { post: null, error: 'Failed to fetch post' };
    }
  };

  const result = await fetchPost();

  return {
    post: result.post,
    error: result.error,
    streamed: {
      // Stream data that's not critical for initial render
      comments: fetch(`/api/posts/${id}/comments`).then(res =>
        res.ok ? res.json() : { comments: [] }
      )
    }
  };
}

// 3. Advanced Form Actions with Validation (src/routes/contact/+page.server.js)
import { fail, redirect } from '@sveltejs/kit';
import { z } from 'zod';

// Validation schema using Zod
const contactSchema = z.object({
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Invalid email address'),
  subject: z.string().min(5, 'Subject must be at least 5 characters'),
  message: z.string().min(10, 'Message must be at least 10 characters'),
  newsletter: z.boolean().default(false),
  priority: z.enum(['low', 'medium', 'high']).default('medium')
});

export const actions = {
  default: async ({ request, getClientAddress }) => {
    const formData = await request.formData();
    const data = Object.fromEntries(formData);

    // Validate data
    try {
      const validatedData = contactSchema.parse(data);

      // Get client IP for rate limiting
      const clientIP = getClientAddress();

      // Check rate limiting (example: max 5 submissions per hour per IP)
      const rateLimitKey = `contact_rate_limit_${clientIP}`;
      // In production, you'd use Redis or similar for rate limiting

      // Send email
      await sendContactEmail(validatedData);

      // Send confirmation if requested
      if (validatedData.newsletter) {
        await addToNewsletter(validatedData.email);
      }

      // Log the contact submission
      await logContactSubmission({
        ...validatedData,
        ip: clientIP,
        timestamp: new Date(),
        userAgent: request.headers.get('user-agent')
      });

      return {
        success: true,
        message: 'Thank you for your message! We will get back to you soon.'
      };

    } catch (validationError) {
      if (validationError instanceof z.ZodError) {
        return fail(400, {
          error: 'Please fix the validation errors',
          errors: validationError.errors.reduce((acc, err) => {
            acc[err.path[0]] = err.message;
            return acc;
          }, {}),
          values: data
        });
      }

      console.error('Contact form error:', validationError);
      return fail(500, {
        error: 'Something went wrong. Please try again.',
        values: data
      });
    }
  }
};

async function sendContactEmail(data) {
  // Email sending logic here
  console.log('Sending email:', data);
  await new Promise(resolve => setTimeout(resolve, 100)); // Simulate email sending
}

async function addToNewsletter(email) {
  // Newsletter subscription logic
  console.log('Adding to newsletter:', email);
}

async function logContactSubmission(data) {
  // Logging logic
  console.log('Logging contact submission:', data);
}

// 4. Authentication Middleware (src/hooks.server.js)
import { redirect } from '@sveltejs/kit';
import jwt from 'jsonwebtoken';

const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';

const protectedRoutes = ['/dashboard', '/profile', '/admin'];
const adminRoutes = ['/admin'];

export async function handle({ event, resolve }) {
  const { url, request, cookies } = event;

  // Skip authentication for API routes and static files
  if (url.pathname.startsWith('/api/') ||
      url.pathname.startsWith('/_app/') ||
      url.pathname.startsWith('/static/')) {
    return resolve(event);
  }

  // Get auth token from cookie or Authorization header
  const token = cookies.get('auth-token') ||
                request.headers.get('authorization')?.replace('Bearer ', '');

  // Check if route requires authentication
  const isProtectedRoute = protectedRoutes.some(route =>
    url.pathname.startsWith(route)
  );

  const isAdminRoute = adminRoutes.some(route =>
    url.pathname.startsWith(route)
  );

  if (isProtectedRoute || isAdminRoute) {
    if (!token) {
      // Redirect to login with return URL
      const loginUrl = new URL('/login', url.origin);
      loginUrl.searchParams.set('returnUrl', url.pathname + url.search);
      throw redirect(302, loginUrl.toString());
    }

    try {
      // Verify JWT token
      const decoded = jwt.verify(token, JWT_SECRET);

      // Add user data to event.locals
      event.locals.user = {
        id: decoded.userId,
        email: decoded.email,
        role: decoded.role
      };

      // Check admin access
      if (isAdminRoute && decoded.role !== 'admin') {
        throw redirect(302, '/unauthorized');
      }

    } catch (tokenError) {
      console.error('Token verification error:', tokenError);

      // Clear invalid token
      cookies.delete('auth-token', { path: '/' });

      // Redirect to login
      const loginUrl = new URL('/login', url.origin);
      loginUrl.searchParams.set('returnUrl', url.pathname + url.search);
      throw redirect(302, loginUrl.toString());
    }
  }

  // Continue with the request
  const response = await resolve(event);

  // Add security headers
  response.headers.set('X-Frame-Options', 'DENY');
  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');

  return response;
}

// 5. Database Integration with Prisma (src/lib/server/database.js)
import { PrismaClient } from '@prisma/client';

// Singleton pattern for database connection
let prisma;

export function getPrismaClient() {
  if (!prisma) {
    prisma = new PrismaClient({
      log: ['query', 'info', 'warn', 'error'],
      errorFormat: 'pretty'
    });
  }

  // Handle graceful shutdown in development
  if (process.env.NODE_ENV === 'development') {
    globalThis.prisma = prisma;
  }

  return prisma;
}

// Database helper functions
export const db = {
  // User operations
  users: {
    async findById(id) {
      return await getPrismaClient().user.findUnique({
        where: { id: parseInt(id) },
        include: {
          posts: true,
          comments: true
        }
      });
    },

    async findByEmail(email) {
      return await getPrismaClient().user.findUnique({
        where: { email }
      });
    },

    async create(data) {
      return await getPrismaClient().user.create({
        data,
        select: {
          id: true,
          email: true,
          name: true,
          role: true,
          createdAt: true
        }
      });
    },

    async update(id, data) {
      return await getPrismaClient().user.update({
        where: { id: parseInt(id) },
        data
      });
    },

    async delete(id) {
      return await getPrismaClient().user.delete({
        where: { id: parseInt(id) }
      });
    }
  },

  // Post operations
  posts: {
    async findMany(options = {}) {
      const { page = 1, limit = 10, category, authorId } = options;
      const skip = (page - 1) * limit;

      const where = {};
      if (category) where.category = category;
      if (authorId) where.authorId = parseInt(authorId);

      const [posts, total] = await Promise.all([
        getPrismaClient().post.findMany({
          where,
          include: {
            author: {
              select: {
                id: true,
                name: true,
                email: true
              }
            },
            _count: {
              select: {
                comments: true,
                likes: true
              }
            }
          },
          orderBy: { createdAt: 'desc' },
          skip,
          take: limit
        }),
        getPrismaClient().post.count({ where })
      ]);

      return {
        posts,
        pagination: {
          page,
          limit,
          total,
          totalPages: Math.ceil(total / limit),
          hasNext: page < Math.ceil(total / limit),
          hasPrev: page > 1
        }
      };
    },

    async findBySlug(slug) {
      return await getPrismaClient().post.findUnique({
        where: { slug },
        include: {
          author: true,
          comments: {
            include: {
              author: true
            },
            orderBy: { createdAt: 'desc' }
          },
          tags: true
        }
      });
    },

    async create(data) {
      return await getPrismaClient().post.create({
        data,
        include: {
          author: {
            select: {
              id: true,
              name: true,
              email: true
            }
          }
        }
      });
    }
  }
};

// 6. Error Handling (src/hooks.client.js)
import { handleError } from '$lib/utils/errors';

// Global error handler for client-side errors
export function handleError({ error, event }) {
  console.error('Client error:', error, event);

  // Log to error reporting service
  handleError(error, {
    url: event.url.toString(),
    route: event.route.id,
    userAgent: navigator.userAgent,
    timestamp: new Date().toISOString()
  });
}

// 7. Custom App Configuration (src/app.html)
<!DOCTYPE html>
<html lang="en" %sveltekit.theme%>
	<head>
		<meta charset="utf-8" />
		<link rel="icon" href="%sveltekit.assets%/favicon.png" />
		<meta name="viewport" content="width=device-width" />
		<meta name="theme-color" content="#ff3e00" />

		<!-- Preload critical resources -->
		<link rel="preload" href="%sveltekit.assets%/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>

		<!-- SEO Meta Tags -->
		%sveltekit.head%

		<!-- Structured Data -->
		<script type="application/ld+json">
		{
			"@context": "https://schema.org",
			"@type": "WebSite",
			"name": "SvelteKit App",
			"description": "A modern web application built with SvelteKit",
			"url": "https://yourapp.com"
		}
		</script>
	</head>
	<body data-sveltekit-preload-data="hover" class="dark">
		<div style="display: contents">%sveltekit.body%</div>

		<!-- Service Worker Registration -->
		<script>
			if ('serviceWorker' in navigator) {
				navigator.serviceWorker.register('/sw.js');
			}
		</script>
	</body>
</html>

// 8. Service Worker (src/service-worker.ts)
import { build, files, version } from '$service-worker';

const CACHE_NAME = `cache-${version}`;

// Files to cache
const FILES_TO_CACHE = [
	...build,
	...files
];

// Install event - cache files
self.addEventListener('install', (event) => {
	console.log('Service Worker: Installing...');

	event.waitUntil(
		caches.open(CACHE_NAME)
			.then((cache) => {
				console.log('Service Worker: Caching files');
				return cache.addAll(FILES_TO_CACHE);
			})
			.then(() => self.skipWaiting())
	);
});

// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
	console.log('Service Worker: Activating...');

	event.waitUntil(
		caches.keys()
			.then((cacheNames) => {
				return Promise.all(
					cacheNames.map((cacheName) => {
						if (cacheName !== CACHE_NAME) {
							console.log('Service Worker: Deleting old cache:', cacheName);
							return caches.delete(cacheName);
						}
					})
				);
			})
			.then(() => self.clients.claim())
	);
});

// Fetch event - serve cached content when offline
self.addEventListener('fetch', (event) => {
	// Skip cross-origin requests
	if (!event.request.url.startsWith(self.location.origin)) {
		return;
	}

	event.respondWith(
		caches.match(event.request)
			.then((response) => {
				// Return cached version or fetch from network
				return response || fetch(event.request);
			})
			.catch(() => {
				// Return offline page for navigation requests
				if (event.request.mode === 'navigate') {
					return caches.match('/offline');
				}
			})
	);
});

// Background sync for offline actions
self.addEventListener('sync', (event) => {
	if (event.tag === 'background-sync') {
		event.waitUntil(doBackgroundSync());
	}
});

async function doBackgroundSync() {
	// Handle background synchronization
	console.log('Service Worker: Background sync');
}

// Push notifications
self.addEventListener('push', (event) => {
	const options = {
		body: event.data.text(),
		icon: '/icon-192.png',
		badge: '/badge.png',
		vibrate: [100, 50, 100],
		data: {
			dateOfArrival: Date.now(),
			primaryKey: 1
		},
		actions: [
			{
				action: 'explore',
				title: 'Explore',
				icon: '/images/checkmark.png'
			},
			{
				action: 'close',
				title: 'Close',
				icon: '/images/xmark.png'
			}
		]
	};

	event.waitUntil(
		self.registration.showNotification('SvelteKit App', options)
	);
});

// 9. Deployment Configuration (svelte.config.js)
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/kit/vite';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	// Consult https://kit.svelte.dev/docs/integrations#preprocessors
	// for more information about preprocessors
	preprocess: vitePreprocess(),

	kit: {
		// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
		// If your environment is not supported or you settled on a specific environment, switch out the adapter.
		// See https://kit.svelte.dev/docs/adapters for more information about adapters.
		adapter: adapter({
			// Node.js adapter options
			out: 'build',
			precompress: false,
			env: {
				host: 'HOST',
				port: 'PORT'
			}
		}),

		// Alias for cleaner imports
		alias: {
			'$components': 'src/lib/components',
			'$utils': 'src/lib/utils',
			'$stores': 'src/lib/stores',
			'$types': 'src/lib/types'
		},

		// CSRF protection
		csrf: {
			checkOrigin: process.env.NODE_ENV === 'production'
		},

		// Headers for security and performance
		headers: {
			'security': [
				{
					source: '/(.*)',
					headers: [
						{
							name: 'X-Frame-Options',
							value: 'DENY'
						},
						{
							name: 'X-Content-Type-Options',
							value: 'nosniff'
						},
						{
							name: 'Referrer-Policy',
							value: 'strict-origin-when-cross-origin'
						},
						{
							name: 'Permissions-Policy',
							value: 'camera=(), microphone=(), geolocation=()'
						}
					]
				}
			]
		}
	}
};

export default config;

// 10. Environment Configuration (.env.example)
# Database
DATABASE_URL="postgresql://username:password@localhost:5432/myapp"

# Authentication
JWT_SECRET="your-super-secret-jwt-key"
SESSION_SECRET="your-session-secret"

# Email Service (Resend)
RESEND_API_KEY="your-resend-api-key"
RESEND_FROM_EMAIL="[email protected]"

# OAuth Providers
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"

GITHUB_CLIENT_ID="your-github-client-id"
GITHUB_CLIENT_SECRET="your-github-client-secret"

# External APIs
STRIPE_SECRET_KEY="sk_test_..."
STRIPE_WEBHOOK_SECRET="whsec_..."

# Analytics
GOOGLE_ANALYTICS_ID="G-XXXXXXXXXX"
SENTRY_DSN="https://your-sentry-dsn"

# File Upload
UPLOAD_MAX_SIZE="5242880"  # 5MB in bytes
UPLOAD_ALLOWED_TYPES="image/jpeg,image/png,image/gif,image/webp"

# Rate Limiting
RATE_LIMIT_WINDOW="3600000"  # 1 hour in milliseconds
RATE_LIMIT_MAX_REQUESTS="100"

# Cache
REDIS_URL="redis://localhost:6379"
CACHE_TTL="3600"  # 1 hour in seconds

# Application
NODE_ENV="development"
PORT="5173"
HOST="localhost"

# Feature Flags
ENABLE_REGISTRATION="true"
ENABLE_NEWSLETTER="true"
ENABLE_COMMENTS="true"
MAINTENANCE_MODE="false"

# Security
CORS_ORIGIN="http://localhost:5173"
COOKIE_SECURE="false"  # Set to true in production
COOKIE_SAME_SITE="lax"