Skip to content

Instantly share code, notes, and snippets.

@LuxXx
Created February 12, 2026 13:44
Show Gist options
  • Select an option

  • Save LuxXx/3f4c74d87be315b410445347768fc71b to your computer and use it in GitHub Desktop.

Select an option

Save LuxXx/3f4c74d87be315b410445347768fc71b to your computer and use it in GitHub Desktop.
effect example for marcel
import {
HttpClient,
PlatformConfigProvider,
FetchHttpClient,
FileSystem,
Path,
} from "@effect/platform";
import { BunContext, BunRuntime } from "@effect/platform-bun";
import { Config, Data, Effect, Redacted, Schema } from "effect";
const MySchema = Schema.Struct({
name: Schema.String,
age: Schema.Number,
});
class MyError extends Data.TaggedError("MyError")<{
cause?: unknown;
message?: string;
}> {}
// the requirements are never, because this effect doesnt have any dependencies
// Effect.Effect<"OKAY!", MyError, never>
const FailingEffect = Effect.gen(function* () {
const x = Math.random();
if (x < 0.5) {
return yield* new MyError({ message: "Failed to to something" });
}
return "OKAY!" as const;
});
// Effect.Effect<void, MyError | TimeoutException | ParseError | HttpClientError | ConfigError | PlatformError, HttpClient.HttpClient | Path.Path | FileSystem.FileSystem>
const program = Effect.gen(function* () {
const result = yield* Effect.tryPromise({
try: () =>
new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve("Hello, world!");
}, 1000);
}),
catch: (error) => new MyError({ cause: error, message: "Failed to to something" }),
}).pipe(
// adding this will let the effect timeout after 2 seconds, if it times out the error will be TimeoutException
Effect.timeout(2000),
);
const parsed = yield* Schema.decodeUnknown(MySchema)({ name: result, age: 10 });
// const okayOrRecovered: "OKAY!" | "Failed 3 times but recovered from error"
const okayOrRecovered = yield* FailingEffect.pipe(
// if this effect fails, it will be retried 2 times
Effect.retry({ times: 2 }),
Effect.catchTags({
MyError: (error) => Effect.succeed("Failed 3 times but recovered from error" as const),
}),
);
yield* Effect.log({ response: result, parsed, okayOrRecovered });
const http = yield* HttpClient.HttpClient;
const response = yield* http.get("https://httpbin.org/get");
const body = yield* response.json;
yield* Effect.log({ body });
const awsSecretAccessKey = yield* Config.redacted(Config.string("AWS_SECRET_ACCESS_KEY"));
// we can just log it lol
yield* Effect.log({ awsSecretAccessKey });
const myClientConfig = {
// ok we actually need it now
secretKey: Redacted.value(awsSecretAccessKey),
};
// when yieldind path or fs we need to provide an implementation of it
const path = yield* Path.Path;
const fs = yield* FileSystem.FileSystem;
// when calling this a scope will be required on the type level
const tmpDir = yield* fs.makeTempDirectoryScoped();
const arr = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"];
yield* Effect.forEach(
arr,
Effect.fn(function* (item) {
const content = new TextEncoder().encode(item);
const file = yield* fs.writeFile(path.join(tmpDir, item), content);
yield* Effect.log({ file });
}),
{
concurrency: 3,
},
);
}).pipe(
// we give a scope to the effect so it gets cleaned up after the program runs
// try removing this! when calling makeTempDirectoryScoped, a scope will automaitically be required on the type level
Effect.scoped,
);
program.pipe(
// if we provide this our Config layer will have access to the environment variables in .env
// but once we use this, this effect will need a filesystem (obviously because we are reading a file (.env), so we also provide one)
Effect.provide(PlatformConfigProvider.layerDotEnv(".env")),
// BunContext contains FileSystem, Path and some more stuff
Effect.provide(BunContext.layer),
// when yielding a http client, we need to provide a http client layer
Effect.provide(FetchHttpClient.layer),
BunRuntime.runMain,
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment