Skip to main content
Use .effect(...) when a procedure implementation should run as an Effect.
effect-procedure.ts
const getUser = effectProcedure
  .input(z.object({ id: z.string() }))
  .effect(function* ({ input }) {
    const user = yield* UsersRepo.findById(input.id);
    return user;
  });
The handler receives the normal oRPC handler options, including input, context, errors, path, signal, and metadata.

Handler shape

handler-options.ts
const procedure = effectProcedure.effect(function* ({
  input,
  context,
  errors,
  path,
  signal,
}) {
  yield* Effect.logDebug("handling procedure", { path });

  if (signal?.aborted) {
    yield* Effect.logDebug("request was already aborted");
  }

  return { input, context, path, errorKeys: Object.keys(errors) };
});

Mix with standard oRPC handlers

You can put standard oRPC procedures and Effect procedures in the same router.
mixed-router.ts
import { os } from "@orpc/server";

export const router = {
  health: os.handler(() => "ok"),

  users: {
    get: effectProcedure.effect(function* ({ input }) {
      return yield* UsersRepo.findById(input.id);
    }),
  },
};
Use .handler(...) for plain synchronous or async procedures. Use .effect(...) when the handler needs Effect services, typed Effect failures, tracing, or fiber context.

Next steps

Last modified on June 15, 2026