Provide HttpClient inside Api service

It's common practice to define a static Live attribute inside a service class that exports a Layer for the service.

Since make is an Effect we use Layer.effect to create a layer from it:

src/services/Api.ts
const make = Effect.gen(function* () {
  const baseClient = yield* HttpClient.HttpClient;
  const client = baseClient.pipe(
    HttpClient.mapRequest(
      flow(
        HttpClientRequest.prependUrl("https://jsonplaceholder.typicode.com"),
        HttpClientRequest.acceptJson
      )
    )
  );

  return {
    getPosts: client
      .get("/posts")
      .pipe(Effect.scoped),
    getPostById: (id: string) =>
      client
        .get(`/posts/${id}`)
        .pipe(Effect.scoped),
  } as const;
});

export class Api extends Context.Tag("Api")<
  Api,
  Effect.Effect.Success<typeof make>
>() {
  static readonly Live = Layer.effect(this, make);
}

Because inside make we used HttpClient, Live has a dependency on it in its type: Layer<Api, never, HttpClient.HttpClient.Service>.

We can directly provide a valid implementation of HttpClient using Layer.provide. @effect/platform includes a HttpClient derived from fetch called FetchHttpClient:

FetchHttpClient is a valid HttpClient implementation that uses fetch to make requests.

src/services/Api.ts
export class Api extends Context.Tag("Api")<
  Api,
  Effect.Effect.Success<typeof make>
>() {
  static readonly Live = Layer.effect(this, make).pipe(
    Layer.provide(FetchHttpClient.layer)
  );
}

With this we created the following dependency graph: Api 👉 HttpClient (Api depends on HttpClient).

In effect each dependency implementation is provided as a layer using Layer.provide. Live is now of type Layer<Api>.