effect-client-wrapperlisted
Install: claude install-skill Mark-Life/agent-task-manager
# Effect Client Wrapper Pattern
Wrap third-party SDK clients with Effect using the "use" pattern for consistent error handling, tracing, and dependency injection.
## Pattern Structure
```typescript
import { Context, Data, Effect, Layer, Config, Redacted } from "effect";
// 1. Define tagged errors
export class MyClientError extends Data.TaggedError("MyClientError")<{
cause: unknown;
}> {}
export class MyClientInstantiationError extends Data.TaggedError(
"MyClientInstantiationError"
)<{
cause: unknown;
}> {}
// 2. Define service interface with `use` method
export type IMyClient = Readonly<{
client: ThirdPartyClient;
use: <A>(
fn: (client: ThirdPartyClient) => Promise<A>
) => Effect.Effect<A, MyClientError, never>;
}>;
// 3. Create the service implementation
const make = Effect.gen(function* () {
const apiKey = yield* Config.redacted("MY_CLIENT_API_KEY");
const client = yield* Effect.try({
try: () => new ThirdPartyClient(Redacted.value(apiKey)),
catch: (cause) => new MyClientInstantiationError({ cause }),
});
const use = <A>(fn: (client: ThirdPartyClient) => Promise<A>) =>
Effect.tryPromise({
try: () => fn(client),
catch: (cause) => new MyClientError({ cause }),
}).pipe(Effect.withSpan(`my_client.${fn.name ?? "use"}`));
return { client, use };
});
// 4. Export as Context.Tag with Default layer
export class MyClient extends Context.Tag("MyClient")<MyClient, IMyClient>() {
static Default = Layer.effect(this, make).