SvelteKit Samples
SvelteKit framework examples - The fastest way to build Svelte apps with SSR, routing, and modern web features
Key Facts
- Category
- Web Frameworks
- Items
- 3
- Format Families
- sample
Sample Overview
SvelteKit framework examples - The fastest way to build Svelte apps with SSR, routing, and modern web features This sample set belongs to Web Frameworks and can be used to test related workflows inside Elysia Tools.
💻 SvelteKit Basics and Components svelte
🟢 simple
⭐⭐
Fundamental SvelteKit concepts including components, pages, routing, and basic functionality
⏱️ 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 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>
💻 SvelteKit API Routes and Data Handling javascript
🟡 intermediate
⭐⭐⭐⭐
Building RESTful APIs with SvelteKit server routes, form handling, and data management
⏱️ 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.'
});
}
}
};
💻 SvelteKit Advanced Features javascript
🔴 complex
⭐⭐⭐⭐⭐
Advanced SvelteKit concepts including SSR, SSG, form actions, authentication, middleware, and deployment
⏱️ 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"