We have now defined all the services inside the app. The last step is to export a Runtime
that combines all the services to execute the app logic.
If you want to learn more on how to structure an app with
effect
, services andRuntime
, check out Effect: Beginners Complete Getting Started.If you are interested specifically about React (19), see Effect with React 19: Project Template.
Since we used Effect.Service
each service has a Default
property containing a Layer
with the service default instance.
We merge all the layers inside MainLayer
:
const MainLayer = Layer.mergeAll(
WriteApi.Default,
ReadApi.Default,
Migrations.Default,
Pglite.Default
);
Config provider
Remember how we used Config
to define INDEX_DB
in the pglite
service?
export class Pglite extends Effect.Service<Pglite>()("Pglite", {
effect: Effect.gen(function* () {
const indexDb = yield* Config.string("INDEX_DB");
const client = yield* Effect.tryPromise({
try: () => _PGlite.PGlite.create(`idb://${indexDb}`),
catch: (error) => new PgliteError({ cause: error }),
});
const orm = drizzle({ client });
const query = <R>(execute: (_: typeof orm) => Promise<R>) =>
Effect.tryPromise({
try: () => execute(orm),
catch: (error) => new PgliteError({ cause: error }),
});
return { client, orm, query };
}),
}) {}
When building the runtime we need to apply a valid Config
to MainLayer
. We do that using ConfigProvider.fromMap
:
const CustomConfigProvider = Layer.setConfigProvider(
ConfigProvider.fromMap(new Map([["INDEX_DB", "v1"]]))
);
We then need to provide CustomConfigProvider
to MainLayer
:
Make sure to not include
CustomConfigProvider
in the list ofmergeAll
but instead only later as a separateLayer.provide
.That's because
CustomConfigProvider
must be provided to each service in the list instead of alongside them.
const CustomConfigProvider = Layer.setConfigProvider(
ConfigProvider.fromMap(new Map([["INDEX_DB", "v1"]]))
);
const MainLayer = Layer.mergeAll(
WriteApi.Default,
ReadApi.Default,
Migrations.Default,
Pglite.Default
).pipe(Layer.provide(CustomConfigProvider));
Finally, we can export a RuntimeClient
using ManagedRuntime
:
const CustomConfigProvider = Layer.setConfigProvider(
ConfigProvider.fromMap(new Map([["INDEX_DB", "v1"]]))
);
const MainLayer = Layer.mergeAll(
WriteApi.Default,
ReadApi.Default,
Migrations.Default,
Pglite.Default
).pipe(Layer.provide(CustomConfigProvider));
export const RuntimeClient = ManagedRuntime.make(MainLayer);
When executing effects everywhere in the app we can import RuntimeClient
(e.g. RuntimeClient.runPromise
).