### Install effect-orpc Package Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Installs the effect-orpc package using npm, pnpm, or bun. This package is essential for integrating Effect with oRPC. ```bash npm install effect-orpc # or pnpm add effect-orpc # or bun add effect-orpc ``` -------------------------------- ### Apply Effect Builder to ORPC Router Source: https://context7.com/utopyin/effect-orpc/llms.txt This example demonstrates how to apply a pre-configured Effect ORPC builder to an entire router. This allows for consistent middleware (like authentication and rate limiting) and error handling across all defined routes. The configuration is applied using methods like `.errors()`, `.$context()`, `.use()`, and `.router()`. Dependencies include 'effect-orpc', '@orpc/server', 'effect', and 'zod'. ```typescript import { makeEffectORPC, ORPCTaggedError } from "effect-orpc"; import { os } from "@orpc/server"; import { Effect, ManagedRuntime } from "effect"; import z from "zod"; class AuthError extends ORPCTaggedError("AuthError", { status: 401 }) {} class RateLimitError extends ORPCTaggedError("RateLimitError", { status: 429 }) {} const runtime = ManagedRuntime.make(/* services */); // Create base builder with common config const protectedOs = makeEffectORPC(runtime) .errors({ AuthError, RateLimitError }) .$context<{ userId: string }>() .use(({ context, errors, next }) => { if (!context.userId) throw errors.AuthError(); return next({ context }); }) .use(({ next }) => { // Rate limiting middleware // ... rate limit check return next({}); }); // Define router const apiRouter = { users: { list: os.handler(() => [{ id: 1, name: "John" }]), me: os .input(z.object({ includeEmail: z.boolean().optional() })) .handler(({ context, input }) => ({ id: context.userId, name: "Current User", })), }, posts: { create: os .input(z.object({ title: z.string(), content: z.string() })) .handler(({ context }) => ({ id: 1, title: "Post", userId: context.userId })) }, }; // Apply Effect builder to entire router const protectedRouter = protectedOs .prefix("/api/v1") .tag("protected", "authenticated") .router(apiRouter); // All routes now have auth middleware, error handling, and OpenAPI config export type ProtectedRouter = typeof protectedRouter; ``` -------------------------------- ### EffectBuilder.traced - Add Custom Span Names for Telemetry Source: https://context7.com/utopyin/effect-orpc/llms.txt Demonstrates how to configure custom OpenTelemetry span names for Effect procedures using the `.traced()` method. This enhances observability by providing more meaningful names in tracing systems than the default procedure paths. Setup involves configuring the NodeSdk with an exporter and span processor. ```typescript import { makeEffectORPC } from "effect-orpc"; import { Effect, ManagedRuntime, Layer } from "effect"; import { NodeSdk } from "@effect/opentelemetry"; import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base"; import z from "zod"; // Setup OpenTelemetry const TracingLive = NodeSdk.layer( Effect.sync(() => ({ resource: { serviceName: "my-api" }, spanProcessor: [new SimpleSpanProcessor(new OTLPTraceExporter())], })) ); class UserService extends Effect.Service()("UserService", { accessors: true, sync: () => ({ findById: (id: string) => Effect.succeed({ id, name: "User" }), create: (name: string) => Effect.succeed({ id: "123", name }), }), }) {} const runtime = ManagedRuntime.make(Layer.mergeAll(UserService.Default, TracingLive)); const effectOs = makeEffectORPC(runtime); // Default span names use procedure path const router = { users: { // Span name: "users.get" get: effectOs .input(z.object({ id: z.string() })) .effect(function* ({ input }) { const service = yield* UserService; return yield* service.findById(input.id); }), // Custom span name create: effectOs .input(z.object({ name: z.string() })) .traced("user.creation.flow") // Override default "users.create" .effect(function* ({ input }) { const service = yield* UserService; return yield* service.create(input.name); }), // Nested operations with custom span updateProfile: effectOs .input(z.object({ id: z.string(), name: z.string() })) .traced("profile.update.complete") .effect(function* ({ input }) { const service = yield* UserService; const user = yield* service.findById(input.id); return yield* service.create(input.name); }), }, }; ``` -------------------------------- ### Callable Procedure with Custom Error Handling (TypeScript) Source: https://context7.com/utopyin/effect-orpc/llms.txt Illustrates creating a callable procedure that includes custom error handling using Effect-ORPC. This example defines specific errors (`NOT_FOUND`) that the procedure can throw, allowing clients to handle them gracefully. It demonstrates type-safe error mapping and invocation. ```typescript import { makeEffectORPC } from "effect-orpc"; import { Effect, ManagedRuntime } from "effect"; import z from "zod"; // Assume UserService is defined as in the previous example class UserService extends Effect.Service()("UserService", { accessors: true, sync: () => ({ findById: (id: number) => Effect.succeed({ id, name: "Alice" }), }), }) {} const runtime = ManagedRuntime.make(UserService.Default); const effectOs = makeEffectORPC(runtime); // Callable with error handling const deleteUser = effectOs .input(z.object({ id: z.number() })) .errors({ NOT_FOUND: { status: 404 } }) .effect(function* ({ input, errors }) { const service = yield* UserService; const user = yield* service.findById(input.id); if (!user) throw errors.NOT_FOUND(); return { success: true }; }) .callable({ context: {} }); await deleteUser({ id: 999 }); // Throws ORPCError with status 404 ``` -------------------------------- ### Creating Custom Tagged Errors with ORPCTaggedError Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Provides examples of creating various tagged error classes using the `ORPCTaggedError` utility. This includes basic errors, errors with explicit codes, default options, and errors with typed data schemas. ```typescript import { ORPCTaggedError } from "effect-orpc"; // Basic tagged error - code defaults to 'USER_NOT_FOUND' (CONSTANT_CASE of tag) class UserNotFound extends ORPCTaggedError("UserNotFound") {} // With explicit code class NotFound extends ORPCTaggedError("NotFound", { code: "NOT_FOUND" }) {} // With default options (code defaults to 'VALIDATION_ERROR') (CONSTANT_CASE of tag) class ValidationError extends ORPCTaggedError("ValidationError", { status: 400, message: "Validation failed", }) {} // With all options class ForbiddenError extends ORPCTaggedError("ForbiddenError", { code: "FORBIDDEN", status: 403, message: "Access denied", schema: z.object({ reason: z.string(), }), }) // With typed data using Standard Schema class UserNotFoundWithData extends ORPCTaggedError("UserNotFoundWithData", { schema: z.object({ userId: z.string() }), }) ``` -------------------------------- ### Create and Use Callable Procedure with Effect-ORPC (TypeScript) Source: https://context7.com/utopyin/effect-orpc/llms.txt Demonstrates creating a callable procedure for fetching user data using `effect-orpc`. It defines input and output schemas with Zod, implements the procedure using Effect, and shows how to invoke it both with and without custom context. This pattern is useful for direct server-side function calls. ```typescript import { makeEffectORPC } from "effect-orpc"; import { Effect, ManagedRuntime } from "effect"; import z from "zod"; class UserService extends Effect.Service()("UserService", { accessors: true, sync: () => ({ findById: (id: number) => Effect.succeed({ id, name: "Alice" }), }), }) {} const runtime = ManagedRuntime.make(UserService.Default); const effectOs = makeEffectORPC(runtime); // Create callable procedure const getUser = effectOs .input(z.object({ id: z.number() })) .output(z.object({ id: z.number(), name: z.string() })) .effect(function* ({ input }) { const service = yield* UserService; return yield* service.findById(input.id); }) .callable({ context: {}, // Provide initial context }); // Use as a regular function async function handler() { try { const user = await getUser({ id: 1 }); console.log(user); // { id: 1, name: "Alice" } // With custom context const userWithAuth = await getUser( { id: 2 }, { context: { userId: 123 } } ); return user; } catch (error) { console.error("Failed to get user:", error); throw error; } } ``` -------------------------------- ### EffectBuilder.router - Apply builder to router Source: https://context7.com/utopyin/effect-orpc/llms.txt Shows how to apply Effect builder configurations, including middleware and error handling, to an entire router for bulk configuration. ```APIDOC ## EffectBuilder.router - Apply builder to router ### Description Applies Effect builder configuration to an entire router, enabling bulk middleware and error handling. ### Method Not applicable for router configuration itself, but endpoints within the router will have methods like GET, POST. ### Endpoint `/api/v1` (as specified by `.prefix('/api/v1')`) ### Parameters #### Path Parameters None for the router configuration. #### Query Parameters None for the router configuration. #### Request Body None for the router configuration. ### Request Example ```typescript // Example of calling a protected route // const response = await fetch('/api/v1/users/list', { headers: { 'Authorization': 'Bearer YOUR_TOKEN' } }); ``` ### Response #### Success Response (200) Responses vary based on the specific route called within the `protectedRouter`. #### Response Example ```json // Example for users.list [ { "id": 1, "name": "John" } ] ``` #### Error Responses - **AuthError** (401) - Returned if authentication middleware fails. - **RateLimitError** (429) - Returned if rate limiting middleware is triggered. ``` -------------------------------- ### makeEffectORPC Function Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Documentation for the `makeEffectORPC` function, which creates an Effect-aware procedure builder. ```APIDOC ## `makeEffectORPC(runtime, builder?)` Creates an Effect-aware procedure builder. ### Parameters - `runtime` - A `ManagedRuntime` instance that provides services for Effect procedures - `builder` (optional) - An oRPC Builder instance to wrap. Defaults to `os` from `@orpc/server` ### Returns An `EffectBuilder` instance. ### Usage Example ```ts // With default builder const effectOs = makeEffectORPC(runtime); // With customized builder const effectAuthedOs = makeEffectORPC(runtime, authedBuilder); ``` ``` -------------------------------- ### Enabling OpenTelemetry Tracing in Effect-ORPC Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Demonstrates how to enable OpenTelemetry tracing for Effect-ORPC services by including the `NodeSdk` layer in the runtime configuration. This involves setting up the service name and span processors. ```typescript import { NodeSdk } from "@effect/opentelemetry"; import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base"; const TracingLive = NodeSdk.layer( Effect.sync(() => ({ resource: { serviceName: "my-service" }, spanProcessor: [new SimpleSpanProcessor(new OTLPTraceExporter())], })), ); const AppLive = Layer.mergeAll(UserServiceLive, TracingLive); const runtime = ManagedRuntime.make(AppLive); const effectOs = makeEffectORPC(runtime); ``` -------------------------------- ### Create Effect-aware oRPC builder with makeEffectORPC Source: https://context7.com/utopyin/effect-orpc/llms.txt The `makeEffectORPC` function creates an Effect-aware procedure builder. It wraps oRPC with Effect support, enabling service injection and automatic tracing. This function can be used with a default runtime or to wrap an existing oRPC builder, allowing for integration of Effect procedures within standard oRPC routers. ```typescript import { os } from "@orpc/server"; import { Effect, ManagedRuntime } from "effect"; import { makeEffectORPC } from "effect-orpc"; import z from "zod"; // Define services class DatabaseService extends Effect.Service()( "DatabaseService", { accessors: true, sync: () => ({ query: (sql: string) => Effect.succeed([{ id: 1, name: "John" }]), }), } ) {} // Create runtime with services const runtime = ManagedRuntime.make(DatabaseService.Default); // Create Effect builder from default os const effectOs = makeEffectORPC(runtime); // Or wrap existing oRPC builder const authedOs = os .errors({ UNAUTHORIZED: { status: 401 } }) .$context<{ userId: number }>() .use(({ context, errors, next }) => { if (!context.userId) throw errors.UNAUTHORIZED(); return next({ context }); }); const effectAuthedOs = makeEffectORPC(runtime, authedOs); // Create procedures const router = { getUsers: effectOs .input(z.object({ limit: z.number() })) .effect(function* ({ input }) { const db = yield* DatabaseService; return yield* db.query(`SELECT * FROM users LIMIT ${input.limit}`); }), getProfile: effectAuthedOs.effect(function* ({ context: { userId } }) { const db = yield* DatabaseService; return yield* db.query(`SELECT * FROM users WHERE id = ${userId}`); }), }; export type Router = typeof router; ``` -------------------------------- ### Define Effect-ORPC Router and Procedures Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Demonstrates defining an oRPC router with Effect-native procedures. It showcases service injection using ManagedRuntime, custom tagged errors, and mixed procedure types. ```typescript import { os } from "@orpc/server"; import { Effect, ManagedRuntime } from "effect"; import { makeEffectORPC, ORPCTaggedError } from "effect-orpc"; interface User { id: number; name: string; } let users: User[] = [ { id: 1, name: "John Doe" }, { id: 2, name: "Jane Doe" }, { id: 3, name: "James Dane" }, ]; // Authenticated os with initial context & errors set const authedOs = os .errors({ UNAUTHORIZED: { status: 401 } }) .$context<{ userId?: number }>() .use(({ context, errors, next }) => { if (context.userId === undefined) throw errors.UNAUTHORIZED(); return next({ context: { ...context, userId: context.userId } }); }); // Define your services class UsersRepo extends Effect.Service()("UsersRepo", { accessors: true, sync: () => ({ get: (id: number) => users.find((u) => u.id === id), }), }) {} // Special yieldable oRPC error class class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", { status: 404, }) {} // Create runtime with your services const runtime = ManagedRuntime.make(UsersRepo.Default); // Create Effect-aware oRPC builder from an other (optional) base oRPC builder and provide tagged errors const effectOs = makeEffectORPC(runtime, authedOs).errors({ UserNotFoundError, }); // Create the router with mixed procedures export const router = { health: os.handler(() => "ok"), users: { me: effectOs.effect(function* ({ context: { userId } }) { const user = yield* UsersRepo.get(userId); if (!user) { return yield* new UserNotFoundError(); } return user; }), }, }; export type Router = typeof router; ``` -------------------------------- ### Traceable Spans with Effect-ORPC Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Understand how Effect-ORPC automatically instruments procedures with traceable spans using Effect's `withSpan` and how to customize them. ```APIDOC ## Traceable Spans Effect procedures are automatically traced using `Effect.withSpan`. The default span name is the procedure path. ### Automatic Span Naming Router structure determines span names automatically. ```ts // Router structure determines span names automatically const router = { users: { // Span name: "users.get" get: effectOs.input(z.object({ id: z.string() })).effect(function* ({ input }) { const userService = yield* UserService; return yield* userService.findById(input.id); }), // Span name: "users.create" create: effectOs.input(z.object({ name: z.string() })).effect(function* ({ input }) { const userService = yield* UserService; return yield* userService.create(input.name); }), }, }; ``` ### Customizing Span Names Use `.traced()` to override the default span name. ```ts const getUser = effectOs .input(z.object({ id: z.string() })) .traced("custom.span.name") // Override the default path-based name .effect(function* ({ input }) { const userService = yield* UserService; return yield* userService.findById(input.id); }); ``` ### Enabling OpenTelemetry Include the OpenTelemetry layer in your runtime to enable tracing. ```ts import { NodeSdk } from "@effect/opentelemetry"; import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base"; const TracingLive = NodeSdk.layer( Effect.sync(() => ({ resource: { serviceName: "my-service" }, spanProcessor: [new SimpleSpanProcessor(new OTLPTraceExporter())], })), ); const AppLive = Layer.mergeAll(UserServiceLive, TracingLive); const runtime = ManagedRuntime.make(AppLive); const effectOs = makeEffectORPC(runtime); ``` ``` -------------------------------- ### EffectBuilder Methods Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Methods available on the EffectBuilder for configuring oRPC procedures with Effect support. ```APIDOC ## EffectBuilder Methods ### Description Methods available on the `EffectBuilder` to wrap an oRPC Builder with Effect support and configure various aspects of the procedure. ### Methods - **`.config(config)`** Set or override the builder config. - **`.context()`** Set or override the initial context type. - **`.meta(meta)`** Set or override the initial metadata. - **`.route(route)`** Set or override the initial route configuration. - **`.input(schema)`** Set or override the initial input schema. - **`.errors(map)`** Add type-safe custom errors. - **`.meta(meta)`** Set procedure metadata (merged with existing). - **`.route(route)`** Configure OpenAPI route (merged with existing). - **`.input(schema)`** Define input validation schema. - **`.output(schema)`** Define output validation schema. - **`.use(middleware)`** Add middleware. - **`.traced(name?)`** Add a traceable span for telemetry (optional, defaults to the procedure's path). - **`.handler(handler)`** Define a non-Effect handler (standard oRPC handler). - **`.effect(handler)`** Define the Effect handler. - **`.prefix(prefix)`** Prefix all procedures in the router (for OpenAPI). - **`.tag(...tags)`** Add tags to all procedures in the router (for OpenAPI). - **`.router(router)`** Apply all options to a router. - **`.lazy(loader)`** Create and apply options to a lazy-loaded router. ``` -------------------------------- ### Automatic Trace Spans in Effect-ORPC Procedures Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Explains how Effect-ORPC automatically generates traceable spans for procedures based on their router structure. It shows how the span names are derived and how to override them using the `.traced()` method. ```typescript // Router structure determines span names automatically const router = { users: { // Span name: "users.get" get: effectOs.input(z.object({ id: z.string() })).effect(function* ({ input }) { const userService = yield* UserService; return yield* userService.findById(input.id); }), // Span name: "users.create" create: effectOs.input(z.object({ name: z.string() })).effect(function* ({ input }) { const userService = yield* UserService; return yield* userService.create(input.name); }), }, }; const getUser = effectOs .input(z.object({ id: z.string() })) .traced("custom.span.name") // Override the default path-based name .effect(function* ({ input }) { const userService = yield* UserService; return yield* userService.findById(input.id); }); ``` -------------------------------- ### Effect-ORPC API: makeEffectORPC Function Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Details the `makeEffectORPC` function, which is used to create an Effect-aware procedure builder. It explains the parameters, including the `runtime` and optional `builder`, and the return type. ```typescript // With default builder const effectOs = makeEffectORPC(runtime); // With customized builder const effectAuthedOs = makeEffectORPC(runtime, authedBuilder); ``` -------------------------------- ### ORPCTaggedError Factory Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Factory function to create Effect-native tagged error classes. ```APIDOC ## ORPCTaggedError ### Description Factory function to create Effect-native tagged error classes. ### Parameters - **`tag`** (string) - The tag for the error. - **`options?`** (object) - Optional configuration for the error. - **`schema?`** (Schema) - Optional Standard Schema for the error's data payload (e.g., `z.object({ userId: z.string() })`). - **`code?`** (ORPCErrorCode) - Optional ORPCErrorCode, defaults to CONSTANT_CASE of the tag (e.g., `UserNotFoundError` → `USER_NOT_FOUND_ERROR`). - **`status?`** (number) - Sets the default HTTP status of the error. - **`message`** (string) - Sets the default message of the error. ``` -------------------------------- ### EffectDecoratedProcedure Methods Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Methods available on the EffectDecoratedProcedure for further customization. ```APIDOC ## EffectDecoratedProcedure Methods ### Description Methods available on the `EffectDecoratedProcedure` (the result of calling `.effect()`) for further customization and integration. ### Methods - **`.errors(map)`** Add more custom errors. - **`.meta(meta)`** Update metadata (merged with existing). - **`.route(route)`** Update route configuration (merged). - **`.use(middleware)`** Add middleware. - **`.callable(options?)`** Make procedure directly invocable. - **`.actionable(options?)`** Make procedure compatible with server actions. ``` -------------------------------- ### Enforce Type-Safe Service Injection with ManagedRuntime Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Illustrates how effect-orpc enforces compile-time safety by ensuring Effect procedures only access services provided within the ManagedRuntime. Attempts to use unprovided services result in compilation errors. ```typescript import { Context, Effect, Layer, ManagedRuntime } from "effect"; import { makeEffectORPC } from "effect-orpc"; class ProvidedService extends Context.Tag("ProvidedService")< ProvidedService, { doSomething: () => Effect.Effect } >() {} class MissingService extends Context.Tag("MissingService")< MissingService, { doSomething: () => Effect.Effect } >() {} const runtime = ManagedRuntime.make( Layer.succeed(ProvidedService, { doSomething: () => Effect.succeed("ok"), }), ); const effectOs = makeEffectORPC(runtime); // ✅ This compiles - ProvidedService is in the runtime const works = effectOs.effect(function* () { const service = yield* ProvidedService; return yield* service.doSomething(); }); // ❌ This fails to compile - MissingService is not in the runtime const fails = effectOs.effect(function* () { const service = yield* MissingService; // Type error! return yield* service.doSomething(); }); ``` -------------------------------- ### Error Handling with ORPCTaggedError Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Learn how to create and use Effect-native tagged errors with ORPCTaggedError for seamless integration with oRPC and Effect. ```APIDOC ## Error Handling with ORPCTaggedError `ORPCTaggedError` allows the creation of Effect-native error classes that integrate with oRPC. ### Creating Tagged Errors Tagged errors can be created with default or explicit options for code, status, message, and schema. ```ts import { ORPCTaggedError } from "effect-orpc"; import * as z from "zod"; // Basic tagged error - code defaults to 'USER_NOT_FOUND' class UserNotFound extends ORPCTaggedError("UserNotFound") {} // With explicit code class NotFound extends ORPCTaggedError("NotFound", { code: "NOT_FOUND" }) {} // With default options class ValidationError extends ORPCTaggedError("ValidationError", { status: 400, message: "Validation failed", }) {} // With all options class ForbiddenError extends ORPCTaggedError("ForbiddenError", { code: "FORBIDDEN", status: 403, message: "Access denied", schema: z.object({ reason: z.string(), }), }) // With typed data using Standard Schema class UserNotFoundWithData extends ORPCTaggedError("UserNotFoundWithData", { schema: z.object({ userId: z.string() }), }) ``` ### Usage in Effect Generators Tagged errors can be yielded directly or through the `errors` map. ```ts const getUser = effectOs .errors({ NOT_FOUND: { message: "User not found", data: z.object({ id: z.string() }), }, UserNotFoundError, USER_NOT_FOUND: UserNotFoundError, }) .effect(function* ({ input, errors }) { const user = yield* UsersRepo.findById(input.id); if (!user) { return yield* new UserNotFoundError(); // or return `yield* Effect.fail(errors.USER_NOT_FOUND())` } return user; }); ``` ### Error Stack Traces When an Effect procedure fails, the span includes a formatted stack trace pointing to the definition site. ``` -------------------------------- ### Define Effect-based procedure handlers with EffectBuilder.effect Source: https://context7.com/utopyin/effect-orpc/llms.txt The `EffectBuilder.effect` method allows defining procedures using Effect generators. It provides automatic service injection, type-safe error handling using tagged errors, and integrates with OpenTelemetry for tracing. This enables developers to leverage Effect's full capabilities within oRPC procedures. ```typescript import { Effect, ManagedRuntime } from "effect"; import { makeEffectORPC, ORPCTaggedError } from "effect-orpc"; import z from "zod"; // Define services class UserRepo extends Effect.Service()("UserRepo", { accessors: true, sync: () => ({ findById: (id: number) => Effect.succeed({ id, name: "Alice", email: "alice@example.com" }), list: () => Effect.succeed([ { id: 1, name: "Alice" }, { id: 2, name: "Bob" }, ]), }), }) {} class EmailService extends Effect.Service()("EmailService", { accessors: true, sync: () => ({ send: (to: string, subject: string) => Effect.succeed({ messageId: "msg_123" }), }), }) {} // Create runtime const runtime = ManagedRuntime.make(UserRepo.Default, EmailService.Default); const effectOs = makeEffectORPC(runtime); // Define tagged errors class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", { status: 404, schema: z.object({ userId: z.number() }), }) {} // Create procedures with full Effect support const router = { users: { list: effectOs .output(z.array(z.object({ id: z.number(), name: z.string() }))) .effect(function* () { const repo = yield* UserRepo; return yield* repo.list(); }), getById: effectOs .input(z.object({ id: z.number() })) .errors({ UserNotFoundError }) .effect(function* ({ input }) { const repo = yield* UserRepo; const user = yield* repo.findById(input.id); if (!user) { return yield* new UserNotFoundError({ data: { userId: input.id } }); } return user; }), sendEmail: effectOs .input(z.object({ userId: z.number(), subject: z.string() })) .effect(function* ({ input }) { const repo = yield* UserRepo; const email = yield* EmailService; const user = yield* repo.findById(input.userId); const result = yield* email.send(user.email, input.subject); return { success: true, messageId: result.messageId }; }), }, }; ``` -------------------------------- ### EffectBuilder.errors - Mixed error maps with tagged errors Source: https://context7.com/utopyin/effect-orpc/llms.txt Demonstrates how to combine traditional oRPC errors with Effect tagged error classes for flexible error handling within an Effect-ORPC endpoint. ```APIDOC ## EffectBuilder.errors - Mixed error maps with tagged errors ### Description Combines traditional oRPC errors with Effect tagged error classes for flexible error handling. ### Method POST (inferred from effect-orpc usage) ### Endpoint Not explicitly defined, but used within a handler. ### Parameters #### Request Body (No explicit request body defined for the builder configuration itself, but the handler `getUser` takes an input object) - **id** (string) - Required - The ID of the user to retrieve. ### Request Example ```typescript // Example of how the getUser handler might be called: // Assuming effectOs and UserRepo are set up // const result = await getUser({ id: "123" }); ``` ### Response #### Success Response (200) - **user** (object) - The user object if found. #### Response Example ```json { "id": "123", "name": "Example User" } ``` #### Error Responses - **VALIDATION_ERROR** (400) - Returned when the input ID is not numeric. - **UserNotFoundError** (404) - Returned when the user is not found. - **DatabaseError** (500) - Returned when a database operation fails. ``` -------------------------------- ### Define and Handle Tagged Errors in Effect-ORPC Source: https://github.com/utopyin/effect-orpc/blob/main/README.md Demonstrates how to define custom tagged errors using ORPCTaggedError and integrate them into Effect-ORPC procedures for type-safe error handling. These errors automatically convert to ORPCError when thrown and can be explicitly mapped in the `.errors()` builder. ```typescript const getUser = effectOs // Mixed error maps .errors({ // Regular oRPC error NOT_FOUND: { message: "User not found", data: z.object({ id: z.string() }), }, // Effect oRPC tagged error UserNotFoundError, // Note: The key of an oRPC error is not used as the error code // So the following will only change the key of the error when accessing it // from the errors object passed to the handler, but not the actual error code itself. // To change the error's code, please see the next section on creating tagged errors. USER_NOT_FOUND: UserNotFoundError, // ^^^ same code as the `UserNotFoundError` error key, defined at the class level }) .effect(function* ({ input, errors }) { const user = yield* UsersRepo.findById(input.id); if (!user) { return yield* new UserNotFoundError(); // or return `yield* Effect.fail(errors.USER_NOT_FOUND())` } return user; }); ``` -------------------------------- ### Combine oRPC and Effect Tagged Errors Source: https://context7.com/utopyin/effect-orpc/llms.txt This snippet shows how to create an Effect ORPC endpoint that merges traditional oRPC errors (like VALIDATION_ERROR) with custom Effect tagged errors (UserNotFoundError, DatabaseError). It demonstrates defining tagged errors with schemas and status codes, and then using them within the endpoint's effect function for flexible error propagation. Dependencies include 'effect-orpc', 'effect', and 'zod'. ```typescript import { makeEffectORPC, ORPCTaggedError } from "effect-orpc"; import { Effect, ManagedRuntime } from "effect"; import z from "zod"; // Define tagged errors class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", { status: 404, schema: z.object({ userId: z.string() }), }) {} class DatabaseError extends ORPCTaggedError("DatabaseError", { status: 500, message: "Database operation failed", }) {} const runtime = ManagedRuntime.make(/* services */); const effectOs = makeEffectORPC(runtime); const getUser = effectOs .input(z.object({ id: z.string() })) // Mixed error map .errors({ // Traditional oRPC error VALIDATION_ERROR: { status: 400, message: "Invalid input", data: z.object({ field: z.string() }), }, // Effect tagged errors UserNotFoundError, DatabaseError, // Custom key for tagged error (code stays "USER_NOT_FOUND_ERROR") NOT_FOUND: UserNotFoundError, }) .effect(function* ({ input, errors }) { // Validate input if (!input.id.match(/^\d+$/)) { return yield* Effect.fail( errors.VALIDATION_ERROR({ data: { field: "id" }, message: "ID must be numeric", }) ); } // Use tagged error directly const user = yield* UserRepo.findById(input.id).pipe( Effect.catchAll(() => Effect.fail(new DatabaseError({ message: "Query failed" })) ) ); if (!user) { // Yield tagged error via errors object return yield* Effect.fail( errors.NOT_FOUND({ data: { userId: input.id } }) ); } return user; }); ``` -------------------------------- ### Create Effect-native ORPCTaggedError Classes Source: https://context7.com/utopyin/effect-orpc/llms.txt Defines factory functions for creating Effect-compatible error classes that integrate with oRPC's error handling. These errors can include default codes, status, messages, and schemas for typed data validation, enabling type-safe error instantiation and handling within Effect procedures. ```typescript import { ORPCTaggedError } from "effect-orpc"; import { Effect } from "effect"; import z from "zod"; // Basic error - code defaults to CONSTANT_CASE of tag class UserNotFound extends ORPCTaggedError("UserNotFound") {} // Error code: "USER_NOT_FOUND" // With explicit error code class NotFound extends ORPCTaggedError("NotFound", { code: "NOT_FOUND" }) {} // With status and message defaults class ValidationError extends ORPCTaggedError("ValidationError", { status: 400, message: "Validation failed", }) {} // With typed data schema class ForbiddenError extends ORPCTaggedError("ForbiddenError", { code: "FORBIDDEN", status: 403, message: "Access denied", schema: z.object({ reason: z.string(), resource: z.string() }), }) {} // Usage in Effect procedures const deleteUser = effectOs .input(z.object({ id: z.number() })) .errors({ ForbiddenError, UserNotFound }) .effect(function* ({ input, context, errors }) { // Type-safe error instantiation if (!context.isAdmin) { return yield* new ForbiddenError({ data: { reason: "admin_required", resource: "user" }, }); } const user = yield* UserRepo.findById(input.id); if (!user) { // Direct yield of error class return yield* new UserNotFound(); } // Or use errors object if (user.protected) { return yield* Effect.fail( errors.ForbiddenError({ data: { reason: "protected_user", resource: "user" }, }) ); } yield* UserRepo.delete(input.id); return { success: true }; }); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.