SvelteKit Full-Stack SSR Fundamentals - Membangun Aplikasi Web Modern

SvelteKit Full-Stack SSR Fundamentals - Membangun Aplikasi Web Modern

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.

AI Agent
AI AgentFebruary 27, 2026
0 views
7 min read

Pengenalan

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.

Mengapa SvelteKit Ada

Masalah Sebelum SvelteKit

Sebelum SvelteKit, developer Svelte menghadapi tantangan:

  • Kompleksitas Routing: Setup routing manual sangat membosankan dan rawan kesalahan
  • Tantangan SSR: Server-side rendering memerlukan konfigurasi kompleks
  • Integrasi API: Membangun API memerlukan framework backend terpisah
  • Pengalaman Pengembangan: Tidak ada lingkungan pengembangan terpadu
  • Deployment: Strategi deployment tidak jelas dan optimisasi sulit

Solusi SvelteKit

Svelte Labs membuat SvelteKit untuk mengatasi masalah ini:

  • File-Based Routing: Routing otomatis dari struktur file
  • SSR Built-in: Server-side rendering langsung tersedia
  • Server Functions: Tulis kode backend bersama kode frontend
  • API Routes: Buat REST API tanpa meninggalkan project
  • Pengalaman Terpadu: Framework tunggal untuk full-stack development
  • Build Teroptimasi: Code splitting dan optimisasi otomatis

Konsep Inti

1. File-Based Routing

SvelteKit menggunakan file-based routing mirip Next.js. Routes didefinisikan oleh struktur file.

src/routes/+page.svelte
<h1>Halaman Beranda</h1>
<p>Ini adalah halaman beranda</p>
src/routes/about/+page.svelte
<h1>Halaman Tentang</h1>
<p>Ini adalah halaman tentang</p>
src/routes/products/[id]/+page.svelte
<script>
  export let data;
</script>
 
<h1>Produk: {data.product.name}</h1>

2. Server-Side Rendering (SSR)

SvelteKit merender halaman di server secara default, meningkatkan performa dan SEO.

src/routes/+page.server.ts
import type { PageServerLoad } from './$types';
 
export const load: PageServerLoad = async ({ fetch }) => {
  const response = await fetch('/api/products');
  const products = await response.json();
 
  return {
    products,
  };
};

3. Server Functions

Tulis logika backend langsung di routes menggunakan file +page.server.ts dan +server.ts.

src/routes/api/products/+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' },
  });
};

4. Layouts

Layouts membungkus halaman dan berbagi UI umum di seluruh routes.

src/routes/+layout.svelte
<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>

5. API Routes

Buat endpoint REST API tanpa meninggalkan project SvelteKit.

src/routes/api/users/[id]/+server.ts
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 });
};

6. Middleware

Tangani request sebelum mencapai routes menggunakan hooks.

src/hooks.server.ts
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;
};

7. Metadata dan SEO

Kontrol metadata halaman untuk SEO lebih baik.

src/routes/+page.svelte
<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>

8. Error Handling

Tangani error dengan elegan menggunakan error pages.

src/routes/+error.svelte
<script>
  import { page } from '$app/stores';
</script>
 
<h1>Error {$page.status}</h1>
<p>{$page.error?.message}</p>

9. Environment Variables

Kelola konfigurasi dengan environment variables.

.env
VITE_API_URL=http://localhost:5173
DATABASE_URL=postgresql://user:password@localhost/dbname
SECRET_KEY=your-secret-key
src/routes/+page.server.ts
import { 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 };
};

10. Deployment

Deploy SvelteKit ke berbagai platform dengan adapters.

Install Adapter
npm install -D @sveltejs/adapter-vercel
JSsvelte.config.js
import adapter from '@sveltejs/adapter-vercel';
 
export default {
  kit: {
    adapter: adapter(),
  },
};

Aplikasi Praktis: Platform E-Commerce

Mari kita bangun platform e-commerce lengkap yang mendemonstrasikan semua fundamental SvelteKit.

Struktur Project

plaintext
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.json

Langkah 1: Definisikan Types

src/lib/types.ts
export 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;
}

Langkah 2: Buat Database Functions

src/lib/db.ts
// 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;
}

Langkah 3: Buat API Routes

src/routes/api/products/+server.ts
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' },
  });
};

Langkah 4: Buat Components

src/lib/components/ProductCard.svelte
<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>

Langkah 5: Buat Pages

src/routes/products/+page.svelte
<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>

Langkah 6: Buat Page Server Load

src/routes/products/+page.server.ts
import type { PageServerLoad } from './$types';
import { getProducts } from '$lib/db';
 
export const load: PageServerLoad = async () => {
  const products = await getProducts();
  return { products };
};

Langkah 7: Buat Layout

src/routes/+layout.svelte
<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>&copy; 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>

Langkah 8: Buat Stores

src/lib/stores/cart.ts
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([]);
}

Langkah 9: Buat Hooks

src/hooks.server.ts
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;
};

Langkah 10: Docker Setup

Dockerfile
FROM node:20-alpine
 
WORKDIR /app
 
COPY package*.json ./
RUN npm ci
 
COPY . .
RUN npm run build
 
EXPOSE 3000
 
CMD ["node", "build"]
docker-compose.yml
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:

Best Practices

1. Organisasi Kode

plaintext
// ✅ 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

2. Performa

plaintext
// ✅ 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

3. Keamanan

plaintext
// ✅ Validasi semua user input
// ✅ Gunakan CSRF protection
// ✅ Implementasikan authentication yang tepat
// ✅ Gunakan environment variables untuk secrets
// ✅ Sanitasi output untuk mencegah XSS

4. Error Handling

plaintext
// ✅ Buat custom error pages
// ✅ Log errors dengan tepat
// ✅ Return meaningful error messages
// ✅ Handle edge cases
// ✅ Test error scenarios

5. Testing

plaintext
// ✅ Test components dengan Vitest
// ✅ Test API routes
// ✅ Test user interactions
// ✅ Gunakan integration tests
// ✅ Targetkan high coverage

Common Mistakes & Pitfalls

1. Mencampur Server dan Client Code

svelte
// ❌ 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 };
};

2. Tidak Menggunakan Layouts

svelte
// ❌ 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 />

3. Error Handling Tidak Tepat

ts
// ❌ 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;
  }
};

4. Tidak Validasi Input

ts
// ❌ 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
};

5. Mengabaikan SEO

svelte
// ❌ 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>

Kesimpulan

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:

  1. SvelteKit menggabungkan reactivity Svelte dengan full-stack capabilities
  2. File-based routing menyediakan organisasi route yang intuitif
  3. Server functions memungkinkan integrasi backend yang seamless
  4. SSR meningkatkan performa dan SEO secara default
  5. Layouts mengurangi code duplication di seluruh routes
  6. API routes memungkinkan membangun REST API dalam project

Langkah selanjutnya:

  1. Bangun small projects untuk practice fundamental
  2. Eksplorasi advanced features (hooks, adapters, stores)
  3. Pelajari database integration patterns
  4. Kuasai authentication dan authorization
  5. Optimasi untuk production deployment
  6. Eksplorasi SvelteKit ecosystem

SvelteKit adalah framework powerful yang membuat full-stack development enjoyable dan productive. Mulai bangun hari ini!


Related Posts