Config service in Effect

Effect provides a composable service to handle configuration values: Config.

Config is responsible to extract configuration values and provide them in our code. In our example we create a Config for the PokéApi base url using Config.string.

Config.string accepts a string parameter that specifies the key of the configuration value to extract ("BASE_URL"):

import { Config } from "effect";

const config = Config.string("BASE_URL");

Config can then be used just like a normal Effect inside .gen. We first update fetchRequest to accept a baseUrl parameter:

const fetchRequest = (baseUrl: string) =>
  Effect.tryPromise({
    try: () => fetch(`${baseUrl}/api/v2/pokemon/garchomp/`),
    catch: () => new FetchError(),
  });

We then provide it from config:

const config = Config.string("BASE_URL");

const program = Effect.gen(function* () {
  const baseUrl = yield* config;
  const response = yield* fetchRequest(baseUrl);
  if (!response.ok) {
    return yield* new FetchError();
  }

  const json = yield* jsonResponse(response);

  return yield* decodePokemon(json);
});

Since extracting configuration is an effectful operation that may fail, using Config requires yield*.

It also adds a ConfigError to the union of errors of the program type.

program now is of type Effect<Pokemon, FetchError | JsonError | ParseError | ConfigError>.

See how handy is to have all the errors in the type signature? 🚀

Since a missing configuration value is an implementation error, it's common to not handle ConfigError (not use catchTag on it).

Instead it's usually better to let the app fail, since we cannot recover anyway from these kind of errors (you should fix your configuration!).