Kuasai SvelteKit, framework full-stack untuk Svelte. Pelajari SSR, file-based routing, server functions, API routes, dan bangun platform e-commerce production-ready dengan fitur real-time.

SvelteKit adalah framework full-stack resmi untuk Svelte, dirancang untuk membuat pembuatan aplikasi web lebih cepat dan mudah. Ini menggabungkan compiler reaktif Svelte dengan server-side rendering, file-based routing, dan API routes untuk menciptakan pengalaman pengembangan yang lengkap.
Dalam artikel ini, kami akan mengeksplorasi konsep inti SvelteKit, memahami mengapa framework ini ada, dan membangun platform e-commerce production-ready yang mendemonstrasikan semua pola fundamental SvelteKit.
Sebelum SvelteKit, developer Svelte menghadapi tantangan:
Svelte Labs membuat SvelteKit untuk mengatasi masalah ini:
SvelteKit menggunakan file-based routing mirip Next.js. Routes didefinisikan oleh struktur file.
<h1>Halaman Beranda</h1>
<p>Ini adalah halaman beranda</p><h1>Halaman Tentang</h1>
<p>Ini adalah halaman tentang</p><script>
export let data;
</script>
<h1>Produk: {data.product.name}</h1>SvelteKit merender halaman di server secara default, meningkatkan performa dan SEO.
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ fetch }) => {
const response = await fetch('/api/products');
const products = await response.json();
return {
products,
};
};Tulis logika backend langsung di routes menggunakan file +page.server.ts dan +server.ts.
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async () => {
const products = [
{ id: 1, name: 'Produk 1', price: 99.99 },
{ id: 2, name: 'Produk 2', price: 149.99 },
];
return new Response(JSON.stringify(products), {
headers: { 'Content-Type': 'application/json' },
});
};
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
// Simpan ke database
return new Response(JSON.stringify({ success: true }), {
status: 201,
headers: { 'Content-Type': 'application/json' },
});
};Layouts membungkus halaman dan berbagi UI umum di seluruh routes.
<script>
import Header from '$lib/components/Header.svelte';
import Footer from '$lib/components/Footer.svelte';
</script>
<Header />
<main>
<slot />
</main>
<Footer />
<style>
main {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
</style>Buat endpoint REST API tanpa meninggalkan project SvelteKit.
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ params }) => {
const userId = params.id;
// Ambil dari database
const user = { id: userId, name: 'John Doe', email: 'john@example.com' };
return new Response(JSON.stringify(user), {
headers: { 'Content-Type': 'application/json' },
});
};
export const PUT: RequestHandler = async ({ params, request }) => {
const userId = params.id;
const data = await request.json();
// Update di database
return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' },
});
};
export const DELETE: RequestHandler = async ({ params }) => {
const userId = params.id;
// Hapus dari database
return new Response(null, { status: 204 });
};Tangani request sebelum mencapai routes menggunakan hooks.
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
// Tambah custom headers
const response = await resolve(event);
response.headers.set('X-Custom-Header', 'value');
return response;
};Kontrol metadata halaman untuk SEO lebih baik.
<script>
import { page } from '$app/stores';
</script>
<svelte:head>
<title>Beranda - Toko Saya</title>
<meta name="description" content="Selamat datang di toko online kami" />
<meta property="og:title" content="Beranda - Toko Saya" />
<meta property="og:description" content="Selamat datang di toko online kami" />
</svelte:head>
<h1>Selamat Datang</h1>Tangani error dengan elegan menggunakan error pages.
<script>
import { page } from '$app/stores';
</script>
<h1>Error {$page.status}</h1>
<p>{$page.error?.message}</p>Kelola konfigurasi dengan environment variables.
VITE_API_URL=http://localhost:5173
DATABASE_URL=postgresql://user:password@localhost/dbname
SECRET_KEY=your-secret-keyimport { env } from '$env/dynamic/private';
import { PUBLIC_API_URL } from '$env/static/public';
export const load = async () => {
const apiUrl = PUBLIC_API_URL;
const dbUrl = env.DATABASE_URL;
return { apiUrl };
};Deploy SvelteKit ke berbagai platform dengan adapters.
npm install -D @sveltejs/adapter-vercelimport adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter(),
},
};Mari kita bangun platform e-commerce lengkap yang mendemonstrasikan semua fundamental SvelteKit.
ecommerce-app/
├── src/
│ ├── routes/
│ │ ├── +page.svelte
│ │ ├── +page.server.ts
│ │ ├── +layout.svelte
│ │ ├── products/
│ │ │ ├── +page.svelte
│ │ │ ├── +page.server.ts
│ │ │ └── [id]/
│ │ │ ├── +page.svelte
│ │ │ └── +page.server.ts
│ │ ├── cart/
│ │ │ ├── +page.svelte
│ │ │ └── +page.server.ts
│ │ ├── checkout/
│ │ │ ├── +page.svelte
│ │ │ └── +page.server.ts
│ │ └── api/
│ │ ├── products/
│ │ │ └── +server.ts
│ │ ├── cart/
│ │ │ └── +server.ts
│ │ └── orders/
│ │ └── +server.ts
│ ├── lib/
│ │ ├── components/
│ │ ├── stores/
│ │ └── db.ts
│ └── hooks.server.ts
├── svelte.config.js
├── vite.config.ts
└── package.jsonexport interface Product {
id: string;
name: string;
description: string;
price: number;
image: string;
stock: number;
}
export interface CartItem {
productId: string;
quantity: number;
price: number;
}
export interface Order {
id: string;
items: CartItem[];
total: number;
status: 'pending' | 'processing' | 'shipped' | 'delivered';
createdAt: Date;
}// Mock database - ganti dengan database nyata
const products: Product[] = [
{
id: '1',
name: 'Laptop',
description: 'Laptop performa tinggi',
price: 999.99,
image: '/products/laptop.jpg',
stock: 10,
},
{
id: '2',
name: 'Mouse',
description: 'Mouse wireless',
price: 29.99,
image: '/products/mouse.jpg',
stock: 50,
},
];
export async function getProducts() {
return products;
}
export async function getProductById(id: string) {
return products.find((p) => p.id === id);
}
export async function createOrder(items: CartItem[]) {
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const order = {
id: Date.now().toString(),
items,
total,
status: 'pending' as const,
createdAt: new Date(),
};
return order;
}import type { RequestHandler } from './$types';
import { getProducts } from '$lib/db';
export const GET: RequestHandler = async () => {
const products = await getProducts();
return new Response(JSON.stringify(products), {
headers: { 'Content-Type': 'application/json' },
});
};<script lang="ts">
import type { Product } from '$lib/types';
export let product: Product;
function addToCart() {
// Handle add to cart
}
</script>
<div class="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>{product.description}</p>
<div class="price">${product.price}</div>
<button on:click={addToCart}>Tambah ke Keranjang</button>
</div>
<style>
.product-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
text-align: center;
}
img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 4px;
}
.price {
font-size: 1.5rem;
font-weight: bold;
color: #333;
margin: 10px 0;
}
button {
background: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
</style><script lang="ts">
import ProductCard from '$lib/components/ProductCard.svelte';
import type { PageData } from './$types';
export let data: PageData;
</script>
<svelte:head>
<title>Produk - Toko</title>
</svelte:head>
<h1>Produk</h1>
<div class="products-grid">
{#each data.products as product (product.id)}
<ProductCard {product} />
{/each}
</div>
<style>
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-top: 20px;
}
</style>import type { PageServerLoad } from './$types';
import { getProducts } from '$lib/db';
export const load: PageServerLoad = async () => {
const products = await getProducts();
return { products };
};<script>
import '../app.css';
</script>
<header>
<nav>
<a href="/">Beranda</a>
<a href="/products">Produk</a>
<a href="/cart">Keranjang</a>
</nav>
</header>
<main>
<slot />
</main>
<footer>
<p>© 2026 Toko E-Commerce</p>
</footer>
<style>
:global(body) {
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
}
header {
background: #333;
color: white;
padding: 20px;
}
nav {
display: flex;
gap: 20px;
}
nav a {
color: white;
text-decoration: none;
}
nav a:hover {
text-decoration: underline;
}
main {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
footer {
background: #f5f5f5;
padding: 20px;
text-align: center;
margin-top: 40px;
}
</style>import { writable } from 'svelte/store';
import type { CartItem } from '$lib/types';
export const cart = writable<CartItem[]>([]);
export function addToCart(productId: string, quantity: number, price: number) {
cart.update((items) => {
const existing = items.find((item) => item.productId === productId);
if (existing) {
existing.quantity += quantity;
return items;
}
return [...items, { productId, quantity, price }];
});
}
export function removeFromCart(productId: string) {
cart.update((items) => items.filter((item) => item.productId !== productId));
}
export function clearCart() {
cart.set([]);
}import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
// Tambah authentication check
const token = event.cookies.get('auth_token');
if (token) {
event.locals.user = { authenticated: true };
}
const response = await resolve(event);
return response;
};FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "build"]version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/ecommerce
depends_on:
- db
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=ecommerce
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:// ✅ Jaga routes terorganisir per fitur
// ✅ Gunakan $lib untuk kode bersama
// ✅ Pisahkan kode server dan client
// ✅ Gunakan TypeScript untuk type safety
// ✅ Jaga components kecil dan fokus// ✅ Gunakan server-side rendering untuk SEO
// ✅ Implementasikan strategi caching yang tepat
// ✅ Lazy load components jika mungkin
// ✅ Optimasi images dan assets
// ✅ Gunakan database indexing yang tepat// ✅ Validasi semua user input
// ✅ Gunakan CSRF protection
// ✅ Implementasikan authentication yang tepat
// ✅ Gunakan environment variables untuk secrets
// ✅ Sanitasi output untuk mencegah XSS// ✅ Buat custom error pages
// ✅ Log errors dengan tepat
// ✅ Return meaningful error messages
// ✅ Handle edge cases
// ✅ Test error scenarios// ✅ Test components dengan Vitest
// ✅ Test API routes
// ✅ Test user interactions
// ✅ Gunakan integration tests
// ✅ Targetkan high coverage// ❌ Salah - database code di component
<script>
import { db } from '$lib/db';
let products = [];
onMount(async () => {
products = await db.getProducts();
});
</script>
// ✅ Benar - gunakan +page.server.ts
// src/routes/+page.server.ts
export const load = async () => {
const products = await db.getProducts();
return { products };
};// ❌ Salah - mengulang header/footer
<script>
import Header from '$lib/Header.svelte';
import Footer from '$lib/Footer.svelte';
</script>
<Header />
<main>Content</main>
<Footer />
// ✅ Benar - gunakan layout
// src/routes/+layout.svelte
<Header />
<slot />
<Footer />// ❌ Salah - silent failures
export const load = async () => {
const data = await fetch('/api/data');
return { data };
};
// ✅ Benar - handle errors
export const load = async ({ fetch }) => {
try {
const response = await fetch('/api/data');
if (!response.ok) throw new Error('Failed to fetch');
const data = await response.json();
return { data };
} catch (error) {
throw error;
}
};// ❌ Salah - tidak ada validasi
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
// Gunakan data langsung
};
// ✅ Benar - validasi input
import { z } from 'zod';
const schema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
const validated = schema.parse(data);
// Gunakan validated data
};// ❌ Salah - tidak ada metadata
<h1>Produk</h1>
// ✅ Benar - tambah metadata
<svelte:head>
<title>{product.name} - Toko</title>
<meta name="description" content={product.description} />
<meta property="og:title" content={product.name} />
</svelte:head>
<h1>{product.name}</h1>SvelteKit menyediakan framework lengkap dan modern untuk membangun aplikasi web full-stack dengan Svelte. File-based routing, SSR built-in, dan server functions membuat ini pilihan excellent untuk developer yang menginginkan pengalaman pengembangan terpadu.
Platform e-commerce yang kami bangun mendemonstrasikan semua konsep inti SvelteKit dalam aksi. Dengan menguasai fundamental ini dan mengikuti best practices, Anda akan mampu membangun aplikasi web yang scalable dan performant.
Key takeaways:
Langkah selanjutnya:
SvelteKit adalah framework powerful yang membuat full-stack development enjoyable dan productive. Mulai bangun hari ini!