# HTTP Client

The HTTP client lets you talk to external APIs as Effects. Instead of a one-shot
`fetch` call, you get a `HttpClient` service that you can configure once — base
URL, default headers, retries, rate limiting, tracing — and then reuse across
your application. Requests are values you build up with `HttpClientRequest`, and
responses are decoded with schema-aware helpers from `HttpClientResponse`, so
both the success and failure channels are fully typed.

The modules live under `effect/unstable/http`:

```ts
import {
  FetchHttpClient,
  HttpClient,
  HttpClientRequest,
  HttpClientResponse
} from "effect/unstable/http"
```

Here is the shape of a typical client service. It wraps the `HttpClient` service
with some common middleware, then exposes a few domain methods that decode their
responses into schemas:

```ts
import { Context, Effect, flow, Layer, Schedule, Schema } from "effect"
import {
  FetchHttpClient,
  HttpClient,
  HttpClientRequest,
  HttpClientResponse
} from "effect/unstable/http"

// The domain model we expect the API to return.
class Todo extends Schema.Class<Todo>("Todo")({
  userId: Schema.Number,
  id: Schema.Number,
  title: Schema.String,
  completed: Schema.Boolean
}) {}

export class JsonPlaceholder extends Context.Service<JsonPlaceholder, {
  getTodo(id: number): Effect.Effect<Todo, JsonPlaceholderError>
}>()("app/JsonPlaceholder") {
  static readonly layer = Layer.effect(
    JsonPlaceholder,
    Effect.gen(function*() {
      // Take the base HttpClient and apply middleware that should run on every
      // request this service makes.
      const client = (yield* HttpClient.HttpClient).pipe(
        // Prepend a base URL and ask for JSON on all requests.
        HttpClient.mapRequest(flow(
          HttpClientRequest.prependUrl("https://jsonplaceholder.typicode.com"),
          HttpClientRequest.acceptJson
        )),
        // Turn non-2xx responses into a typed failure.
        HttpClient.filterStatusOk,
        // Retry transient failures (network errors, 5xx, 429) with backoff.
        HttpClient.retryTransient({
          schedule: Schedule.exponential(100),
          times: 3
        })
      )

      const getTodo = Effect.fn("JsonPlaceholder.getTodo")(function*(id: number) {
        return yield* client.get(`/todos/${id}`).pipe(
          // Decode the JSON body with the Todo schema.
          Effect.flatMap(HttpClientResponse.schemaBodyJson(Todo)),
          // Collapse client and decode errors into one domain error.
          Effect.mapError((cause) => new JsonPlaceholderError({ cause }))
        )
      })

      return JsonPlaceholder.of({ getTodo })
    })
  ).pipe(
    // Provide the fetch-based implementation of HttpClient.
    Layer.provide(FetchHttpClient.layer)
  )
}

export class JsonPlaceholderError extends Schema.TaggedErrorClass<JsonPlaceholderError>()(
  "JsonPlaceholderError",
  { cause: Schema.Defect }
) {}
```

A few things worth noticing:

- **`HttpClient.HttpClient`** is a service. You access it from the context and
  derive a configured client with `.pipe(...)`. The configuration is shared by
  every request, so you write it once.
- **`FetchHttpClient.layer`** provides the concrete implementation backed by the
  platform `fetch`. You provide it at the edge of your application, which makes
  the client trivial to swap in tests.
- **Errors are values.** A request fails with `HttpClientError`, schema decoding
  fails with `Schema.SchemaError`, and `mapError` lets you fold them into a
  single domain error you control.
**Note:** The HTTP modules are **unstable** — they are production-ready but their API may
still change between minor versions. Import them from `effect/unstable/http`.

## In this section

[Making requests](https://effect.plants.sh/http-client/making-requests/)
  [Handling responses](https://effect.plants.sh/http-client/handling-responses/)
  [Resilience](https://effect.plants.sh/http-client/resilience/)
To provide the `HttpClient` service, see [Platform](https://effect.plants.sh/platform/) for runtime
specifics, and [Services & Layers](https://effect.plants.sh/services-and-layers/) for how layers compose.