Dependencies between layers

You may have noticed a type error when running main:

We still cannot run main, since a dependency seems to be missing!
We still cannot run main, since a dependency seems to be missing!

Type 'PokeApiUrl' is not assignable to type 'never'.

That's a missing dependency! Why PokeApiUrl is missing? Didn't we already provide PokeApiUrl to Layer.mergeAll?

const MainLayer = Layer.mergeAll(
  PokeApi.Live,
  PokemonCollection.Live,
  BuildPokeApiUrl.Live,
  PokeApiUrl.Live
);

Yes, but we have a dependency between layers that we didn't resolve: the BuildPokeApiUrl layer depends on PokeApiUrl.

BuildPokeApiUrl.ts
export class BuildPokeApiUrl extends Context.Tag("BuildPokeApiUrl")<
  BuildPokeApiUrl,
  ({ name }: { name: string }) => string
>() {
  static readonly Live = Layer.effect(
    this,
    Effect.gen(function* () {
      const pokeApiUrl = yield* PokeApiUrl; // 👈 Create dependency
      return ({ name }) => `${pokeApiUrl}/${name}`;
    })
  );
}

If we check the type of BuildPokeApiUrl.Live we see the following: Layer.Layer<BuildPokeApiUrl, never, PokeApiUrl>.

Since the full service requires PokeApiUrl, we need to provide it directly to BuildPokeApiUrl.

We do this by using Layer.provide:

index.ts
const MainLayer = Layer.mergeAll(
  PokeApi.Live,
  PokemonCollection.Live,
  BuildPokeApiUrl.Live.pipe(Layer.provide(PokeApiUrl.Live)),
  PokeApiUrl.Live
);

We use Layer.provide when composing layers, and Effect.provide when providing the final layer to run effects.

Layer.provide and Effect.provide are two different APIs!

Now our program compiles and works:

index.ts
import { Effect, Layer } from "effect";
import { BuildPokeApiUrl } from "./BuildPokeApiUrl";
import { PokeApi } from "./PokeApi";
import { PokeApiUrl } from "./PokeApiUrl";
import { PokemonCollection } from "./PokemonCollection";

const MainLayer = Layer.mergeAll(
  PokeApi.Live,
  PokemonCollection.Live,
  BuildPokeApiUrl.Live.pipe(Layer.provide(PokeApiUrl.Live)),
  PokeApiUrl.Live
);

export const program = Effect.gen(function* () {
  const pokeApi = yield* PokeApi;
  return yield* pokeApi.getPokemon;
});

const runnable = program.pipe(Effect.provide(MainLayer));

const main = runnable.pipe(
  Effect.catchTags({
    FetchError: () => Effect.succeed("Fetch error"),
    JsonError: () => Effect.succeed("Json error"),
    ParseError: () => Effect.succeed("Parse error"),
  })
);

Effect.runPromise(main).then(console.log);
> effect-getting-started-course@1.0.0 dev
> BASE_URL=https://pokeapi.co tsx src/index.ts

{ id: 120, order: 191, name: 'staryu', height: 8, weight: 345 }