Server Runtime
Remote functions always execute their Effect callback on the server. Query, Command, Form, and Prerender all eventually run through the server runtime.
import { RequestEvent, ServerRuntime } from "svelte-effect-runtime";This page explains what the server runtime is responsible for, when you need to configure it, and how it interacts with request-scoped data.
What the server runtime does
When a remote function executes on the server, svelte-effect-runtime:
- Resolves the current server runtime.
- Injects SvelteKit's
RequestEventinto the Effect environment. - Runs the remote Effect and converts failures into the correct remote response shape.
That means the server runtime is the place where you provide server-side dependencies such as:
- database pools
- server-only API clients
- loggers
- caches
- configuration and secrets
The callback you pass to Query(...), Command(...), Form(...), or Prerender(...) can then access those services with normal Effect patterns:
import { Effect, Schema } from "effect";
import { Query } from "svelte-effect-runtime";
import { Database } from "$lib/server/database";
export const get_post = Query(
Schema.Struct({
slug: Schema.String,
}),
({ slug }) =>
Effect.gen(function* () {
const db = yield* Database;
return yield* db.find_post_by_slug(slug);
}),
);Default behavior
ServerRuntime.make(...) is optional.
If no server runtime has been registered yet, the first remote Effect that runs will create a default ManagedRuntime backed by an empty layer. That is enough for remote functions that only use plain Effect operators and do not depend on custom Context services.
This works:
import { Effect } from "effect";
import { Query } from "svelte-effect-runtime";
export const get_version = Query(() => Effect.succeed("1.0.0"));No hooks.server.ts setup is required for that case.
When to call ServerRuntime.make(...)
Call ServerRuntime.make(...) when your remote functions need services that are not provided automatically by the runtime.
Typical examples:
- a database service
- an authenticated upstream API client
- a structured logger
- a configuration layer
The usual place to do that is src/hooks.server.ts:
import { Layer } from "effect";
import { ServerRuntime } from "svelte-effect-runtime";
import { Database } from "$lib/server/database";
import { Logger } from "$lib/server/logger";
export const init = () => {
ServerRuntime.make(Layer.provide(Database.Live), Layer.provide(Logger.Live));
};If ServerRuntime.make(...) is called again later, the new runtime replaces the previous one.
RequestEvent is provided automatically
Each remote execution receives the current SvelteKit RequestEvent as a service:
import { Effect } from "effect";
import { Query, RequestEvent } from "svelte-effect-runtime";
export const get_session = Query(() =>
Effect.gen(function* () {
const event = yield* RequestEvent;
return {
user_id: event.cookies.get("user_id"),
pathname: event.url.pathname,
};
}),
);This matters because RequestEvent is request-scoped, while the server runtime itself is global. You should not try to bake per-request values into ServerRuntime.make(...). Put long-lived services in the runtime, and read request-specific data from RequestEvent inside the Effect.
Global services vs request-scoped values
The server runtime is a good place for long-lived infrastructure:
- pooled database connections
- reusable HTTP clients
- telemetry
- configuration
It is not the right place for data that changes per request:
- cookies
- headers
- route params
- the current user
For request-scoped values, read RequestEvent and derive what you need from it inside the Effect:
import { Effect } from "effect";
import { Query, RequestEvent } from "svelte-effect-runtime";
import { UserRepository } from "$lib/server/user-repository";
export const get_current_user = Query(() =>
Effect.gen(function* () {
const event = yield* RequestEvent;
const users = yield* UserRepository;
const session_id = event.cookies.get("session_id");
return yield* users.find_by_session(session_id);
}),
);That separation keeps the runtime stable while still giving each invocation access to the current request.
Prerender runs through the same server runtime
Prerender(...) uses the same server runtime model as Query(...), Command(...), and Form(...). The difference is when it runs:
Query,Command, andFormrun during request handling.Prerenderruns during prerendering and build-time remote evaluation.
If a prerendered remote function needs custom services, those services still need to be installed into ServerRuntime.make(...).
Failure handling
The server runtime does more than just provide services. It is also where remote Effect failures are interpreted:
- form validation failures become SvelteKit invalid responses
- domain failures are serialized for the client
- defects are surfaced as server errors
That lets you keep remote function bodies as plain Effect programs while the runtime handles the transport boundary.