Server Actions (Remote Functions)
Áttekintés
Szekció neve “Áttekintés”Az ElyOS a SvelteKit command és query függvényeit használja szerver oldali logikához. Ezek a *.remote.ts fájlokban élnek, és közvetlenül hívhatók a kliens oldalról — nincs szükség külön API route-ra.
src/apps/[app-name]/└── [feature].remote.ts # Szerver akciókcommand vs query
Szekció neve “command vs query”| Típus | Mikor használd | Mutáció? |
|---|---|---|
command | Adatmódosítás, mentés, törlés | Igen |
query | Adatolvasás, listázás | Nem |
Alapstruktúra
Szekció neve “Alapstruktúra”import { command, query, getRequestEvent } from '$app/server';import * as v from 'valibot';
// Validációs sémaconst updateSettingsSchema = v.object({ theme: v.optional(v.object({ mode: v.optional(v.picklist(['light', 'dark', 'auto'])) }))});
// Mutáció – commandexport const updateSettings = command(updateSettingsSchema, async (input) => { const { locals } = getRequestEvent();
if (!locals.user?.id) { return { success: false, error: 'Nincs bejelentkezve' }; }
// ... adatbázis művelet
return { success: true };});
// Olvasás – queryexport const getSettings = query(async () => { const { locals } = getRequestEvent(); // ... adatok lekérése return { success: true, data: locals.settings };});Visszatérési érték konvenció
Szekció neve “Visszatérési érték konvenció”Minden remote function { success: boolean, error?: string, ...data } alakú objektumot ad vissza:
// Sikeres válaszreturn { success: true, data: result };
// Hibás válaszreturn { success: false, error: 'Leíró hibaüzenet' };Validáció
Szekció neve “Validáció”A command első paramétere mindig egy Valibot séma. Az input automatikusan validálódik — ha a validáció sikertelen, a handler nem fut le.
import * as v from 'valibot';
const createUserSchema = v.object({ name: v.pipe(v.string(), v.minLength(2), v.maxLength(100)), email: v.pipe(v.string(), v.email()), role: v.picklist(['admin', 'user']), age: v.optional(v.pipe(v.number(), v.minValue(0), v.maxValue(150)))});
export const createUser = command(createUserSchema, async (input) => { // input típusa automatikusan v.InferOutput<typeof createUserSchema> console.log(input.name, input.email); // ...});A Valibot részletes dokumentációját — beleértve a kliensoldali használatot, típuskövetkeztetést és egyéni hibaüzeneteket — a Validáció oldalon találod.
Session és jogosultság ellenőrzés
Szekció neve “Session és jogosultság ellenőrzés”A getRequestEvent() adja vissza az aktuális request kontextust, benne a locals-szal:
import { command, getRequestEvent } from '$app/server';
export const deleteItem = command(deleteSchema, async (input) => { const { locals } = getRequestEvent();
// Autentikáció ellenőrzése if (!locals.user?.id) { return { success: false, error: 'Hitelesítés szükséges' }; }
// Admin jogosultság ellenőrzése // (a jogosultságkezelés a permissionStore-on keresztül történik) const userId = parseInt(locals.user.id);
// ...});locals tartalma
Szekció neve “locals tartalma”Az app.d.ts-ben definiált App.Locals interfész:
interface Locals { user: import('better-auth').User | null; session: import('better-auth').Session | null; settings: UserSettings; locale: string;}Kliens oldali hívás
Szekció neve “Kliens oldali hívás”A remote functionök közvetlenül importálhatók és hívhatók a Svelte komponensekből:
<script lang="ts"> import { updateSettings } from './settings.remote'; import { toast } from 'svelte-sonner';
async function save() { const result = await updateSettings({ theme: { mode: 'dark' } });
if (result.success) { toast.success('Beállítások mentve'); } else { toast.error(result.error ?? 'Hiba történt'); } }</script>
<button onclick={save}>Mentés</button>Timeout kezelés
Szekció neve “Timeout kezelés”Ha a szerver nem válaszol, a withTimeout segédfüggvény megakadályozza a UI lefagyását:
import { withTimeout, RemoteTimeoutError } from '$lib/utils/remote';
try { const result = await withTimeout(fetchData({}), 8000);} catch (error) { if (error instanceof RemoteTimeoutError) { toast.error('A szerver nem válaszolt időben'); }}Paginált lekérdezések
Szekció neve “Paginált lekérdezések”Paginált adatok visszaadásának konvenciója:
const fetchItemsSchema = v.object({ page: v.optional(v.pipe(v.number(), v.minValue(1)), 1), pageSize: v.optional(v.pipe(v.number(), v.minValue(1), v.maxValue(100)), 20), search: v.optional(v.string())});
export const fetchItems = command(fetchItemsSchema, async (input) => { const limit = input.pageSize ?? 20; const offset = ((input.page ?? 1) - 1) * limit;
const [rows, totalCount] = await Promise.all([ itemRepository.findMany({ limit, offset, search: input.search }), itemRepository.count({ search: input.search }) ]);
return { success: true, data: rows, pagination: { page: input.page ?? 1, pageSize: limit, totalCount, totalPages: Math.ceil(totalCount / limit) } };});Típus exportálás
Szekció neve “Típus exportálás”A séma output típusát érdemes exportálni a kliens oldali használathoz:
export type CreateUserInput = v.InferOutput<typeof createUserSchema>;