You may have noticed a type error when running main
:
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
.
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
:
const MainLayer = Layer.mergeAll(
PokeApi.Live,
PokemonCollection.Live,
BuildPokeApiUrl.Live.pipe(Layer.provide(PokeApiUrl.Live)),
PokeApiUrl.Live
);
We use
Layer.provide
when composing layers, andEffect.provide
when providing the final layer to run effects.
Layer.provide
andEffect.provide
are two different APIs!
Now our program compiles and works:
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 }