β οΈ alchemy-effectis still experimental and not ready for production use (expect breaking changes). Come hang in our Discord to participate in the early stages of development.
alchemy-effect is an Infrastructure-as-Effects (IaE) framework that unifies business logic and infrastructure config into a single, type-safe program with the following benefits:
- Type-Checked IAM Policies
- Optimally Tree-Shaken Bundles
- Testable Business Logic
- Re-usable Components
- Reviewable Deployment Plans
bun add alchemy-effectType-checked Bindings ensure your IAM Policies are least-privilege - you are never missing or granting excessive permissions:
You will receive a type error if you mess up your Bindings:

Tip
This error means you are missing the SendMessage<Messages> binding (you provided never instead of SendMessage<Messages>).
An alchemy-effect program produces a Plan that can be reviewed prior to deployment:
All knowable information about the Plan is available at compile-time:
Tip
These types can be used to implement type-level validation of infrastructure policies, e.g. disallowing publicly accessible S3 buckets.
Provide runtime clients as Layers and export a handler that can be optimally tree-shaken to only include necessary code.
export default Api.handler.pipe(
Effect.provide(SQS.clientFromEnv()),
Lambda.toHandler,
);Everything (including the CLI) is provided as Effect layers:
The output of deploying a stack is totally known at compile-time, e.g. the .fifo suffix of a SQS FIFO Queue:
Infrastructure-as-Effects has three main concepts: Resources, Functions (as Effects), and Bindings:
Resourcesare the underlying infrastructure components, e.g. a SQS Queue or DynamoDB Table.Functionscontain the business logic as an Effect running in some runtime, e.g. a Lambda Function or a Cloudflare Worker.BindingsconnectFunctionstoResources, e.g.SQS.SendMessage(Messages)
Resources are declared along-side your business logic as classes, e.g. a FIFO SQS Queue:
class Messages extends SQS.Queue("Messages", {
fifo: true,
schema: S.String,
}) {} Functions are a special kind of Resource that includes a runtime implementation function.
The function always returns an Effect<A, Err, Req> which is then used to infer Capabilities and type-check your Bindings.
class Api extends Lambda.serve("Api", {
fetch: Effect.fn(function* (event) {
yield* SQS.sendMessage(Messages, event.body!).pipe(
Effect.catchAll(() => Effect.void),
);
}),
})({
main: import.meta.filename,
bindings: $(SQS.SendMessage(Messages)),
}) {}A Binding is a connection between a Resource and a Function that satisfies a Capability (aka. runtime dependency, e.g. SQS.SendMessage(to: Messages)).
Tip
Bindings are inferred from your business logic and then type-checked to ensure least-privilege IAM policies.
class Api extends Lambda.serve("Api", {
// ...
})({
main: import.meta.filename,
// Policy<Lambda.Function, SQS.SendMessage<Messages>>
bindings: $(SQS.SendMessage(Messages)),
}) {}Caution
Curring (Lambda.serve(..)({ .. })) is required because there's a limitation in TypeScript. We hope to simplify this in the future.
Infrastructure and business logic can be encapsulated as a Component using a simple function.
const Monitor = <const ID extends string, ReqAlarm, ReqResolved>(
id: ID,
{
onAlarm,
onResolved,
}: {
onAlarm: (
batch: SQS.QueueEvent<Message>,
) => Effect.Effect<void, never, ReqAlarm>;
onResolved?: (
batch: SQS.QueueEvent<Message>,
) => Effect.Effect<void, never, ReqResolved>;
},
) => {
class Messages extends SQS.Queue(`${id}-Messages`, {
fifo: true,
schema: Message,
}) {}
return <const Props extends Lambda.FunctionProps<ReqAlarm | ReqResolved>>({
bindings,
...props
}: Props) =>
Lambda.consume(id, {
queue: Messages,
handle: Effect.fn(function* (batch) {
yield* SQS.sendMessage(Messages, {
id: 1,
value: "1",
}).pipe(Effect.catchAll(() => Effect.void));
if (onAlarm) {
yield* onAlarm(batch);
}
if (onResolved) {
yield* onResolved(batch);
}
}),
})({
...props,
// Components are not leaky - the inner SQS.SendMessage(Messages) binding is not required to be passed in by the caler
bindings: bindings.and(SQS.SendMessage(Messages)),
});
};Tip
Components are very similar to React components, but for infrastructure. Instead of declaring state in your closure and returning a React element, you declare Resources and return a Function.
Caution
WIP - docs coming soon!





