Skip to content

Instantly share code, notes, and snippets.

@STBoyden
Last active January 13, 2026 01:14
Show Gist options
  • Select an option

  • Save STBoyden/fd55dfe4eeea8a3bb5d0af177d0da6b1 to your computer and use it in GitHub Desktop.

Select an option

Save STBoyden/fd55dfe4eeea8a3bb5d0af177d0da6b1 to your computer and use it in GitHub Desktop.
Effect.ts + SvelteKit remote functions
import type { RemoteFormInput } from "@sveltejs/kit";
import { command, form, query } from "$app/server";
import { Effect, Schema } from "effect";
/**
* Creates a remote query defined with `Effect`. Shares the same behaviour as
* SvelteKit's `query`.
*
* @param schema The `Schema` to validate arguments against.
* @param handler The `Effect` to run when this function is ran.
* @template A success value for the given `Effect`.
* @template E possible error for the given `Effect`.
* @see query
*/
export const effectfulQuery = <A, E, ASchema, ISchema>(
schema: Schema.Schema<ASchema, ISchema, never>,
handler: (args: ASchema) => Effect.Effect<A, E, never>
) => query(Schema.standardSchemaV1(schema), async args => Effect.runPromiseExit(handler(args)));
/**
* Creates a remote batch query defined with `Effect`. Shares the same
* behaviour as SvelteKit's `query.batch`.
*
* @param schema The `Schema` to validate arguments against.
* @param handler The `Effect` to run when this function is ran.
* @template A success value for the given `Effect`.
* @template E possible error for the given `Effect`.
* @see query.batch
*/
export const effectfulBatchQuery = <A, E, ASchema, ISchema>(
schema: Schema.Schema<ASchema, ISchema, never>,
handler: (
args: ASchema[]
) => Effect.Effect<(args: ASchema, index?: number) => Effect.Effect<A, E, never>>
) =>
query.batch(Schema.standardSchemaV1(schema), async args => {
return (arg, index) =>
Effect.runSyncExit(handler(args).pipe(Effect.andThen(fn => fn(arg, index))));
});
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Creates a remote form function defined with `Effect`. Shares the same
* behaviour as SvelteKit's `form`.
*
* @param schema The `Schema` to validate arguments and fields against.
* @param handler The `Effect` to run when this function is ran.
* @template A success value for the given `Effect`.
* @template E possible error for the given `Effect`.
* @see form
*/
export const effectfulForm = <
A,
E,
ASchema extends Record<string, any>,
ISchema extends RemoteFormInput
>(
schema: Schema.Schema<ASchema, ISchema, never>,
handler: (args: ASchema) => Effect.Effect<A, E, never>
) => form(Schema.standardSchemaV1(schema), async args => Effect.runPromiseExit(handler(args)));
/* eslint-enable @typescript-eslint/no-explicit-any */
/**
* Creates a remote command function defined with `Effect`. Shares the same
* behaviour as SvelteKit's `command`.
*
* @param schema The `Schema` to validate arguments and fields against.
* @param handler The `Effect` to run when this function is ran.
* @template A success value for the given `Effect`.
* @template E possible error for the given `Effect`.
* @see command
*/
export const effectfulCommand = <A, E, ASchema, ISchema>(
schema: Schema.Schema<ASchema, ISchema, never>,
handler: (args: ASchema) => Effect.Effect<A, E, never>
) => command(Schema.standardSchemaV1(schema), async args => Effect.runPromiseExit(handler(args)));
import type {
RemoteCommand,
RemoteForm,
RemoteFormInput,
RemoteQueryFunction
} from "@sveltejs/kit";
import { command, form, query } from "$app/server";
import { Effect, Schema } from "effect";
/**
* Creates a remote query defined with `Effect`. Shares the same behaviour as
* SvelteKit's `query`.
*
* @param schema The `Schema` to validate arguments against.
* @param handler The `Effect` to run when this function is ran. When `E` is `never`, then the return value will be non-nullish, otherwise the result can be `undefined`.
* @template A success value for the given `Effect`.
* @template E possible error for the given `Effect`.
* @template QueryReturn `A` when `E == never`, otherwise `A | undefined`.
* @see query
*/
export const effectfulQuery = <
A,
E,
ASchema,
ISchema,
QueryReturn = E extends never ? A : A | undefined
>(
schema: Schema.Schema<ASchema, ISchema, never>,
handler: (args: ASchema) => Effect.Effect<A, E, never>
): RemoteQueryFunction<ISchema, QueryReturn> =>
query(Schema.standardSchemaV1(schema), async args =>
Effect.runPromise(
Effect.gen(function* () {
const result = handler(args);
if (yield* Effect.isSuccess(result)) {
return yield* result;
} else {
yield* Effect.tapError(result, error =>
Effect.logError(`An error occurred in remote query function: ${error}`)
);
return undefined;
}
})
)
) as RemoteQueryFunction<ISchema, QueryReturn>;
/**
* Creates a remote batch query defined with `Effect`. Shares the same
* behaviour as SvelteKit's `query.batch`.
*
* @param schema The `Schema` to validate arguments against.
* @param handler The `Effect` to run when this function is ran. When `E` is `never`, then the return value will be non-nullish, otherwise the result can be `undefined`.
* @template A success value for the given `Effect`.
* @template E possible error for the given `Effect`.
* @template BatchQueryReturn `A` when `E == never`, otherwise `A | undefined`.
* @see query.batch
*/
export const effectfulBatchQuery = <
A,
E,
ASchema,
ISchema,
BatchQueryReturn = E extends never ? A : A | undefined
>(
schema: Schema.Schema<ASchema, ISchema, never>,
handler: (
args: ASchema[]
) => Effect.Effect<(args: ASchema, index?: number) => Effect.Effect<BatchQueryReturn, E, never>>
): RemoteQueryFunction<ISchema, BatchQueryReturn> =>
query.batch(Schema.standardSchemaV1(schema), async args => {
const lookup = handler(args);
return (arg, index) =>
Effect.runSync(
Effect.gen(function* () {
const fn = yield* lookup;
const result = fn(arg, index);
if (yield* Effect.isSuccess(result)) {
return yield* result;
} else {
yield* Effect.tapError(result, error =>
Effect.logError(
`An error occurred in remote query batch function [index: ${index}, arg: ${arg}]: ${error}`
)
);
return undefined;
}
})
);
}) as RemoteQueryFunction<ISchema, BatchQueryReturn>;
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Creates a remote form function defined with `Effect`. Shares the same
* behaviour as SvelteKit's `form`.
*
* @param schema The `Schema` to validate arguments and fields against.
* @param handler The `Effect` to run when this function is ran. When `E` is `never`, then the return value will be non-nullish, otherwise the result can be `undefined`.
* @template A success value for the given `Effect`.
* @template E possible error for the given `Effect`.
* @template FormReturn `A` when `E == never`, otherwise `A | undefined`.
* @see form
*/
export const effectfulForm = <
A,
E,
ASchema extends Record<string, any>,
ISchema extends RemoteFormInput,
FormReturn = E extends never ? A : A | undefined
>(
schema: Schema.Schema<ASchema, ISchema, never>,
handler: (args: ASchema) => Effect.Effect<A, E, never>
): RemoteForm<ISchema, FormReturn> =>
form(Schema.standardSchemaV1(schema), async args =>
Effect.runPromise(
Effect.gen(function* () {
const result = handler(args);
if (yield* Effect.isSuccess(result)) {
return yield* result;
} else {
yield* Effect.tapError(result, error =>
Effect.logError(`An error occurred in remote form function: ${error}`)
);
return undefined;
}
})
)
) as RemoteForm<ISchema, FormReturn>;
/* eslint-enable @typescript-eslint/no-explicit-any */
/**
* Creates a remote command function defined with `Effect`. Shares the same
* behaviour as SvelteKit's `command`.
*
* @param schema The `Schema` to validate arguments and fields against.
* @param handler The `Effect` to run when this function is ran. When `E` is `never`, then the return value will be non-nullish, otherwise the result can be `undefined`.
* @template A success value for the given `Effect`.
* @template E possible error for the given `Effect`.
* @template CommandReturn `A` when `E == never`, otherwise `A | undefined`.
* @see command
*/
export const effectfulCommand = <
A,
E,
ASchema,
ISchema,
CommandReturn = E extends never ? A : A | undefined
>(
schema: Schema.Schema<ASchema, ISchema, never>,
handler: (args: ASchema) => Effect.Effect<A, E, never>
): RemoteCommand<ISchema, CommandReturn> =>
command(Schema.standardSchemaV1(schema), async args =>
Effect.runPromise(
Effect.gen(function* () {
const result = handler(args);
if (yield* Effect.isSuccess(result)) {
return yield* result;
} else {
yield* Effect.tapError(result, error =>
Effect.logError(`An error occurred in remote command function: ${error}`)
);
return undefined;
}
})
)
) as RemoteCommand<ISchema, CommandReturn>;
@sjunepark
Copy link

Thanks

@STBoyden
Copy link
Author

STBoyden commented Jan 7, 2026

Thanks

No problem!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment