@allservices/sdk-sveltekit
v1.0.0
Published
SvelteKit 2 SSR companion for @allservices/sdk-svelte: load() prefetch, hydration boundary, form-action helper, hooks.server middleware, and __Host-session cookie helpers.
Maintainers
Readme
@allservices/sdk-sveltekit
SvelteKit 2 SSR companion for @allservices/sdk-svelte. Provides hooks.server.ts middleware, +page.server.ts prefetch + dehydration helpers, form-action error mapping, and cookie helpers for the AllServices session cookie.
Status: alpha. Public API may change between minors until
1.0.0. Requires SvelteKit 2, Svelte 5, and@tanstack/svelte-queryv6.
Install
pnpm add @allservices/sdk-sveltekit @allservices/sdk-svelte @allservices/sdk-server \
@sveltejs/kit svelte @tanstack/svelte-querySubpath exports
This package ships two entries:
| Import | Runs in | Purpose |
|---|---|---|
| @allservices/sdk-sveltekit | browser + server | prefetchAllServices, dehydrate, HydrationBoundary, queryKeys |
| @allservices/sdk-sveltekit/server | Node only ("browser": null) | createAllServicesHandle, createKitServerClient, formAction, cookie helpers |
Never import from the /server subpath in a .svelte file or any module that may end up in a browser bundle. The build-time "browser": null guard will fail the bundle if you do.
Server middleware (hooks.server.ts)
createAllServicesHandle creates a per-request AllServicesClient, seeds it with the __Host-session cookie token, optionally fetches the current user, and attaches everything to event.locals.allservices.
// src/hooks.server.ts
import { createAllServicesHandle } from '@allservices/sdk-sveltekit/server';
import { env } from '$env/dynamic/private';
export const handle = createAllServicesHandle({
config: {
region: 'eu',
baseUrl: env.ALLSERVICES_API_URL,
},
loadUser: async (client) => {
try {
return await client.users.getMe();
} catch {
return null;
}
},
});Cookie rotation is automatic: if the SDK's TokenManager refreshes the access token during the request, the middleware writes the new token back to the cookie before the response is sent.
app.d.ts augmentation
SvelteKit requires you to declare the shape of event.locals. Add this once to src/app.d.ts:
import type { AllServicesLocals } from '@allservices/sdk-sveltekit/server';
declare global {
namespace App {
interface Locals {
allservices: AllServicesLocals;
}
}
}
export {};AllServicesLocals exposes client (the per-request AllServicesClient), user (AuthResponseUser | null), and accessToken (string | null).
Server-side prefetch with hydration (+page.server.ts)
Use createKitServerClient + prefetchAllServices + dehydrate to pre-populate the TanStack cache on the server and hand the dehydrated state to the client.
// src/routes/dashboard/+page.server.ts
import { QueryClient } from '@tanstack/svelte-query';
import { prefetchAllServices, dehydrate } from '@allservices/sdk-sveltekit';
import { createKitServerClient } from '@allservices/sdk-sveltekit/server';
import { queryKeys } from '@allservices/sdk-svelte';
import { env } from '$env/dynamic/private';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async (event) => {
const client = createKitServerClient(event, {
config: { region: 'eu', baseUrl: env.ALLSERVICES_API_URL },
});
const qc = new QueryClient();
await prefetchAllServices(qc, queryKeys.users.me(), () => client.users.getMe());
return { dehydratedState: dehydrate(qc) };
};Then on the page, wrap your hooks in <HydrationBoundary> using the dehydrated state from data:
<!-- src/routes/dashboard/+page.svelte -->
<script lang="ts">
import { HydrationBoundary } from '@allservices/sdk-sveltekit';
import { createCurrentUser } from '@allservices/sdk-svelte';
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
const user = createCurrentUser();
</script>
<HydrationBoundary state={data.dehydratedState}>
{#if user.data}
<h1>Welcome, {user.data.firstName}</h1>
{/if}
</HydrationBoundary>The hook is a warm cache hit on mount — no loading spinner, no second network request.
Form actions (+page.server.ts)
formAction wraps a SvelteKit action handler and maps AllServices error classes to fail() / redirect() responses without boilerplate. Error mapping:
| Error class | fail() status | Payload |
|---|---|---|
| ValidationError | 422 | { fieldErrors: error.details } |
| AuthenticationError | 401 | { message } |
| SudoRequiredError | 403 | { sudoRequired: true, message } |
| RateLimitedError | 429 | { retryAfter, message } |
| Other AllServicesApiError | error.statusCode | { message } |
SvelteKit redirect() and other Response objects are re-thrown transparently.
// src/routes/login/+page.server.ts
import { redirect } from '@sveltejs/kit';
import { formAction, writeSessionToCookies } from '@allservices/sdk-sveltekit/server';
import type { Actions } from './$types';
export const actions: Actions = {
default: formAction(async (event) => {
const data = Object.fromEntries(await event.request.formData());
const result = await event.locals.allservices.client.auth.login({
email: String(data.email),
password: String(data.password),
});
writeSessionToCookies(event.cookies, result.accessToken);
throw redirect(303, '/dashboard');
}),
};Consume the fail result in the .svelte file:
<!-- src/routes/login/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { ActionData } from './$types';
let { form }: { form: ActionData } = $props();
</script>
<form method="POST" use:enhance>
<input name="email" type="email" />
<input name="password" type="password" />
{#if form?.fieldErrors?.email}
<span role="alert">{form.fieldErrors.email[0].message}</span>
{/if}
{#if form?.message}
<p role="alert">{form.message}</p>
{/if}
<button type="submit">Sign in</button>
</form>Cookie helpers
Three helpers manage the __Host-session access-token cookie. They use strict flags (Secure; HttpOnly; SameSite=Lax; Path=/) suitable for production.
import {
readSessionFromCookies,
writeSessionToCookies,
clearSessionCookies,
} from '@allservices/sdk-sveltekit/server';
// Read access token from the cookie jar (returns null if absent)
const token = readSessionFromCookies(event.cookies);
// Write after login or token refresh (maxAge defaults to 30 minutes)
writeSessionToCookies(event.cookies, accessToken);
// Clear on logout
clearSessionCookies(event.cookies);Webhook receiver
Webhook signature verification is provided by @allservices/sdk-server, not this package. See @allservices/sdk-server for verifyWebhookSignature and createWebhookHandler. The reference app wires this up in src/routes/api/webhooks/.
Reference app
The SvelteKit reference application at examples/sveltekit-app/ demonstrates every pattern above end-to-end, including the hooks.server.ts middleware, the formAction login flow, <HydrationBoundary>, and webhook verification.
Documentation
See the main README.
License
UNLICENSED — internal AllServices use only.
