Memahami locals.auth() dan event.locals.auth() di SvelteKit: Beserta Contoh Penerapannya -> Svelte Clerk

SandikodevSandikodev
5 min read

Pendahuluan

Dalam pengembangan aplikasi web modern dengan SvelteKit dan Svelte Clerk, kita sering menemukan dua cara untuk mengakses fungsi autentikasi: locals.auth() dan event.locals.auth(). Meskipun keduanya terlihat mirip, mereka memiliki konteks penggunaan yang berbeda dan penting untuk dipahami agar dapat mengimplementasikan autentikasi dengan benar.

Asal-usul Fungsi auth()

Svelte Clerk sebagai Provider

Fungsi auth() berasal dari library Svelte Clerk, yang merupakan wrapper untuk Clerk.com - layanan autentikasi modern. Svelte Clerk menyediakan integrasi seamless antara Clerk dan SvelteKit.

// hooks.server.ts
import { withClerkHandler } from 'svelte-clerk/server';

export const handle: Handle = sequence(
  withClerkHandler(), // ← Ini yang menambahkan auth() ke locals
  // ... middleware lainnya
);

Type Definition

Svelte Clerk menambahkan type definition untuk auth() di interface Locals:

// app.d.ts
interface Locals {
  auth: import('svelte-clerk/server').RequestAuth;
}

// RequestAuth adalah function yang mengembalikan session
type RequestAuth = () => Promise<{
  userId?: string;
  sessionId?: string;
  // ... data session lainnya
}>;

Perbedaan locals.auth() vs event.locals.auth()

1. event.locals.auth() - Konteks Hooks/Middleware

Digunakan di: Handle functions dalam hooks.server.ts Konteks: Server-side middleware dan hooks Parameter: event object dari SvelteKit

// hooks.server.ts
const authMiddleware: Handle = async ({ event, resolve }) => {
  const session = await event.locals.auth(); // ← event.locals
  if (session?.userId) {
    const user = await checkUserStatus(session.userId);
    event.locals.user = user;
  }
  return resolve(event);
};

2. locals.auth() - Konteks Page/Layout

Digunakan di: load functions dalam page.server.ts atau layout.server.ts Konteks: Page/Layout server load functions Parameter: locals object yang sudah di-extract dari event

// page.server.ts
export async function load({ locals }) {
  const session = await locals.auth(); // ← locals (sudah di-extract)
  const user = locals.user;
  return { user };
}

Alur Data dan Eksekusi

1. Request Flow

// 1. Request masuk ke SvelteKit
Request → hooks.server.ts

// 2. withClerkHandler() menambahkan auth() ke event.locals
withClerkHandler() → event.locals.auth()

// 3. Middleware kita akses via event.locals
const session = await event.locals.auth();

// 4. SvelteKit extract locals untuk page.server.ts
export async function load({ locals }) {
  // locals adalah event.locals yang sudah di-extract
  const session = await locals.auth();
}

2. Sequence Execution

export const handle: Handle = sequence(
  dbErrorMiddleware,
  withClerkHandler(),     // ← Menambahkan auth() ke event.locals
  authMiddleware,         // ← Menggunakan event.locals.auth()
  handleParaglide
);

Implementasi Praktis

1. Hooks Server dengan Authentication Middleware

// hooks.server.ts
import { withClerkHandler } from 'svelte-clerk/server';
import { checkUserStatus } from '$lib/server/middleware/auth';

const authMiddleware: Handle = async ({ event, resolve }) => {
  const session = await event.locals.auth();

  if (session?.userId) {
    try {
      const user = await checkUserStatus(session.userId);
      event.locals.user = user; // ← Set user data untuk digunakan di page
    } catch (error) {
      console.error('Error loading user data:', error);
    }
  }

  return resolve(event);
};

export const handle: Handle = sequence(
  withClerkHandler(),
  authMiddleware,
  // ... middleware lainnya
);

2. Page Server Load Function

// page.server.ts
export async function load({ locals }) {
  const session = await locals.auth();
  const user = locals.user; // ← Data yang diset di hooks

  if (!user) {
    throw redirect(302, '/login');
  }

  return { user };
}

3. Type Safety dengan TypeScript

// app.d.ts
import type { AuthUser } from '$lib/server/middleware/auth';

interface Locals {
  auth: import('svelte-clerk/server').RequestAuth;
  user?: AuthUser; // ← Custom user data
}

// TypeScript memberikan autocomplete dan type checking
const session = await locals.auth(); // ← Type: Promise<Session>
const user = locals.user;           // ← Type: AuthUser | undefined

Best Practices

1. Gunakan Hooks untuk Data Loading

// ✅ Baik: Load user data sekali di hooks
const authMiddleware: Handle = async ({ event, resolve }) => {
  const session = await event.locals.auth();
  if (session?.userId) {
    const user = await checkUserStatus(session.userId);
    event.locals.user = user;
  }
  return resolve(event);
};

// ❌ Buruk: Load user data di setiap page
export async function load({ locals }) {
  const session = await locals.auth();
  const user = await checkUserStatus(session.userId); // ← Duplikasi
  return { user };
}

2. Validasi di Page Server

// ✅ Baik: Validasi role/status di page server
export async function load({ locals }) {
  const user = requireAdmin(locals); // ← Helper function
  return { user };
}

// Helper function untuk validasi
export function requireAdmin(locals: any): AuthUser {
  if (!locals.user) {
    throw redirect(302, '/login');
  }

  if (locals.user.role !== 'admin') {
    throw redirect(302, '/dashboard');
  }

  return locals.user;
}

3. Error Handling

// hooks.server.ts
const authMiddleware: Handle = async ({ event, resolve }) => {
  try {
    const session = await event.locals.auth();
    if (session?.userId) {
      const user = await checkUserStatus(session.userId);
      event.locals.user = user;
    }
  } catch (error) {
    console.error('Auth middleware error:', error);
    // Jangan fail request, lanjutkan tanpa user data
  }

  return resolve(event);
};

Alur Kerja

// 1. Request masuk ke SvelteKit
Request → hooks.server.ts

// 2. withClerkHandler() menambahkan auth() ke event.locals
withClerkHandler() → event.locals.auth()

// 3. Middleware kita akses via event.locals
const session = await event.locals.auth();

// 4. SvelteKit extract locals untuk page.server.ts
export async function load({ locals }) {
  // locals adalah event.locals yang sudah di-extract
  const session = await locals.auth();
}

Kesimpulan

  • event.locals.auth(): Digunakan di hooks/middleware untuk mengakses session dan memuat data user

  • locals.auth(): Digunakan di page.server.ts untuk mengakses session yang sudah dimuat

  • Keduanya sama: Mengakses function yang sama dari Svelte Clerk

  • Perbedaan konteks: Berbeda cara akses berdasarkan tempat penggunaannya

Pattern ini memungkinkan kita untuk:

  1. Performance: Load user data sekali di hooks, bukan di setiap page

  2. Consistency: User data tersedia di semua request

  3. Type Safety: TypeScript support dengan proper type definitions

  4. Maintainability: Logic terpusat dan mudah dikelola

Dengan memahami perbedaan ini, kita dapat mengimplementasikan sistem autentikasi yang efisien dan maintainable di aplikasi SvelteKit kita.

0
Subscribe to my newsletter

Read articles from Sandikodev directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sandikodev
Sandikodev