Query
Read server data with schemas, typed failures, and client usage.
Use Query when the client needs server data and the operation should not mutate anything. A query is easiest to reason about when identical input can safely return cached or repeated output.
When to use this
Use Query for dashboards, profile data, search results, permissions, and detail pages. Reach for Command or Form when a user action changes state.
Minimal working example
import { Query } from "svelte-effect-runtime/server";
import { Effect, Schema } from "effect";
const UserInput = Schema.Struct({
id: Schema.String,
});
export const get_user = Query(UserInput, ({ id }) =>
Effect.succeed({
id,
name: "Ada Lovelace",
}),
);<script lang="ts" effect>
import { get_user } from "./user.remote";
const user = get_user({ id: "42" });
</script>
{#await yield* user}
<p>Loading user...</p>
{:then value}
<h2>{value.name}</h2>
{/await}Realistic variant
Model expected failures as data:
import { Query } from "svelte-effect-runtime/server";
import { Effect, Schema } from "effect";
type UserNotFound = {
readonly _tag: "UserNotFound";
id: string;
};
export const get_user = Query(Schema.Struct({ id: Schema.String }), ({ id }) =>
Effect.fail({ _tag: "UserNotFound", id } satisfies UserNotFound),
);The client can handle the failure in an await block or inside an Effect pipeline.
Query variants
Use the base Query helper for one read per input. Reach for a variant when the access pattern changes:
Query.batch
Collect many related reads into one server handler.
Query.live
Keep server data connected with an Effect Stream.
Common mistakes
- Mutating data in a query handler.
- Accepting unvalidated input because TypeScript already knows the caller.
- Throwing plain
Errorfor expected business failures. - Calling a query repeatedly with unstable object inputs created from noisy state.