Network requests can fail for transient reasons: a brief network hiccup, a server restart, or a rate limit. Rather than failing immediately, you can retry the request using HttpClient.retry().
retry takes a Schedule that defines the retry behavior: how many times, how long to wait between attempts, and whether to apply backoff.
import { FetchHttpClient, HttpClient, HttpClientRequest,} from "effect/unstable/http";import { Effect, Schedule } from "effect";
function fetchWithRetry(userId: number) { return Effect.gen(function* () { const client = (yield* HttpClient.HttpClient).pipe( HttpClient.filterStatusOk, // Retry up to 3 times with exponential backoff HttpClient.retry( Schedule.exponential("500 millis").pipe( Schedule.compose(Schedule.recurs(3)), ), ), );
const request = HttpClientRequest.get( `https://dummyjson.com/users/${userId}`, );
const response = yield* client.execute(request); const user = yield* response.json;
return user; }).pipe(Effect.provide(FetchHttpClient.layer));}
// Test with a valid userEffect.runPromise(fetchWithRetry(1)).then( (user) => console.log("User:", user.firstName, user.lastName), (error) => console.error("Failed after retries:", error.message),);In this example, if a request fails, it retries up to 3 times with exponential backoff starting at 500ms (500ms, 1s, 2s). After all retries are exhausted, the original error propagates.
You can also pass an options object for more control:
HttpClient.retry({ times: 3, schedule: Schedule.exponential("1 second"),});The times option caps the total number of retries, and schedule controls the delay between them.
retry applies to all errors from the client. If you have filterStatusOk enabled, a 404 will be retried too. To retry only specific errors, combine retry with a while predicate:
HttpClient.retry({ times: 3, while: (error) => error._tag === "RequestError" && error.reason._tag === "TransportError",});This retries only transport-level failures (network errors), not status code errors.