Just as you can transform outgoing requests, you can also transform the response pipeline. HttpClient.transformResponse() wraps the response effect itself, letting you apply Effect combinators to every response.
This is useful for cross-cutting response concerns: adding timeouts, retries, or caching behavior to every request made through a client.
import { FetchHttpClient, HttpClient, HttpClientRequest,} from "effect/unstable/http";import { Effect } from "effect";
function fetchFromApi() { return Effect.gen(function* () { const client = (yield* HttpClient.HttpClient).pipe( HttpClient.transformResponse((responseEffect) => responseEffect.pipe( Effect.tap((response) => Effect.log(`Response received: ${response.status}`), ), Effect.timeout("10 seconds"), ), ), );
const response = yield* client.get("https://dummyjson.com/users/1"); const user = yield* response.json;
return user; }).pipe(Effect.provide(FetchHttpClient.layer));}
// Test itEffect.runPromise(fetchFromApi()).then((user) => { console.log("User:", user.firstName, user.lastName);});Output:
[12:47:43.151] INFO (#2): Response received: 200User: Emily JohnsonThe function passed to transformResponse receives the full Effect<HttpClientResponse, E, R>, not just the response, but the effect that produces it. This means you can wrap it with any Effect combinator: timeout, retry, tap, catchAll, and so on.
Because transformResponse replaces the entire response pipeline, any errors or requirements introduced by your transform (like a TimeoutException from Effect.timeout) become part of the client’s error type.