Implementing Transaction Context Propagation with Effect in TypeScript

hi! I'm struggling with understanding what to start with to implement transation context propagation, or how to do asynclocalstorage the effectful way in general, would appreciate some directions

heres an example

import { err, ok, Result, ResultAsync } from "@/result/index.js";
import { PgTransactionConfig } from "drizzle-orm/pg-core";
import { createContext } from "../context/index.js";
import { DrizzlePgClient, DrizzlePgExecutor } from "./types.js";

function createPgTransactionContext<S extends Record<string, unknown>>(
  db: DrizzlePgClient<S>,
  schema: S,
) {
  const TransactionContext = createContext<{
    exec: DrizzlePgExecutor<typeof schema>;
  }>();

  function getDb(): DrizzlePgExecutor<typeof schema> {
    return TransactionContext.use()?.exec ?? db;
  }

  async function createTransaction<T, E>(
    cb: () => ResultAsync<T, E>,
    isolationLevel: PgTransactionConfig["isolationLevel"] = "read committed",
  ): Promise<Result<T, E>> {
    try {
      const exec_ = TransactionContext.use()?.exec ?? db;
      const res = await exec_.transaction(
        async (exec) => {
          const res = await TransactionContext.with({ exec }, cb);
          if (!res.isOk()) {
            throw res.error;
          }
          return res.value;
        },
        {
          isolationLevel,
        },
      );
      return ok(res);
    } catch (e) {
      return err(e as E);
    }
  }

  return {
    TransactionContext,
    getDb,
    createTransaction,
  };
}


tl;dr i use getDb() to get the current db session context in my db code. I can then wrap some top-level function in a createTransaction and all db calls will be executed in the same transaction without the need to pass the reference to transaction in params

question is how to provide this context in effect and if it is even considered a good practice in effect

thanks!!
Was this page helpful?