EffectTalk
Back to Tour
resource-managementSenior

Create a Managed Runtime for Scoped Resources

Use Layer.launch to safely manage the lifecycle of layers containing scoped resources, ensuring finalizers are always run.

Create a Managed Runtime for Scoped Resources

Guideline

For services that manage resources needing explicit cleanup (e.g., a database connection), define them in a Layer using Layer.scoped. Then, use Layer.launch to provide this layer to your application.

Rationale

Layer.launch is designed for resource safety. It acquires all resources, provides them to your effect, and—crucially—guarantees that all registered finalizers are executed upon completion or interruption.

Good Example

import { Effect, Layer } from "effect";

class DatabasePool extends Effect.Service<DatabasePool>()("DbPool", {
  effect: Effect.gen(function* () {
    yield* Effect.log("Acquiring pool");
    return {
      query: () => Effect.succeed("result"),
    };
  }),
}) {}

// Create a program that uses the DatabasePool service
const program = Effect.gen(function* () {
  const db = yield* DatabasePool;
  yield* Effect.log("Using DB");
  yield* db.query();
});

// Run the program with the service implementation
Effect.runPromise(
  program.pipe(Effect.provide(DatabasePool.Default), Effect.scoped)
);

Explanation:
Layer.launch ensures that resources are acquired and released safely, even in the event of errors or interruptions.

Anti-Pattern

Do not use Layer.toRuntime with layers that contain scoped resources. This will acquire the resource, but the runtime has no mechanism to release it, leading to resource leaks.

Create a Managed Runtime for Scoped Resources | EffectTalk | EffectTalk