Skip to content

Instantly share code, notes, and snippets.

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
Copy Markdown

Thanks

@STBoyden

STBoyden commented Jan 7, 2026

Copy link
Copy Markdown
Author

Thanks

No problem!

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