(
path: string,
serialization?: Layer.Layer<RpcSerialization.RpcSerialization>
): Layer.Layer<RpcClient.Protocol>Build an ipc client Protocol — Effect socket RPC over a Unix-domain path
(NodeSocket.layerNet({ path })). Same-machine counterpart to protocolHttp /
protocolWebsocket.
export const const protocolIpc: (
path: string,
serialization?: Layer.Layer<RpcSerialization.RpcSerialization>
) => Layer.Layer<RpcClient.Protocol>
Build an ipc client Protocol — Effect socket RPC over a Unix-domain path
(NodeSocket.layerNet({ path })). Same-machine counterpart to
protocolHttp
/
protocolIpc = (
path: stringpath: string,
serialization: Layer.Layer<
RpcSerialization.RpcSerialization,
never,
never
>
(parameter) serialization: {
build: (memoMap: Layer.MemoMap, scope: Scope.Scope) => Effect.Effect<Context.Context<RpcSerialization.RpcSerialization>, never, never>;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
serialization: import LayerLayer.interface Layer<in ROut, out E = never, out RIn = never>A Layer describes how to build one or more services for dependency injection.
When to use
Use to model construction of application services for dependency injection,
especially when services have dependencies, can fail during construction, or
need scoped setup and release.
Details
A Layer<ROut, E, RIn> represents ROut as the services this layer
provides, E as the possible errors during layer construction, and RIn as
the services this layer requires as dependencies.
Layer<import RpcSerializationRpcSerialization.class RpcSerializationclass RpcSerialization {
key: Identifier;
Service: {
makeUnsafe: () => RpcSerialization.Parser;
contentType: string;
includesFraming: boolean;
};
}
Service that describes how RPC protocol messages are encoded and decoded,
including the content type and whether the serialization format provides
message framing.
When to use
Use to provide the serialization boundary shared by RPC clients and servers
for a chosen wire format.
RpcSerialization> = const defaultSerialization: Layer.Layer<
RpcSerialization.RpcSerialization,
never,
never
>
const defaultSerialization: {
build: (memoMap: Layer.MemoMap, scope: Scope.Scope) => Effect.Effect<Context.Context<RpcSerialization.RpcSerialization>, never, never>;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
defaultSerialization,
): import LayerLayer.interface Layer<in ROut, out E = never, out RIn = never>A Layer describes how to build one or more services for dependency injection.
When to use
Use to model construction of application services for dependency injection,
especially when services have dependencies, can fail during construction, or
need scoped setup and release.
Details
A Layer<ROut, E, RIn> represents ROut as the services this layer
provides, E as the possible errors during layer construction, and RIn as
the services this layer requires as dependencies.
Layer<import RpcClientRpcClient.class Protocolclass Protocol {
key: Identifier;
Service: {
run: (clientId: number, f: (data: FromServerEncoded) => Effect.Effect<void>) => Effect.Effect<never>;
send: (clientId: number, request: FromClientEncoded, transferables?: ReadonlyArray<globalThis.Transferable>) => Effect.Effect<void, RpcClientError>;
supportsAck: boolean;
supportsTransferables: boolean;
};
}
Defines the service interface for an RPC client transport, responsible for running the
receive loop and sending encoded client messages.
When to use
Use to provide the transport boundary for RPC clients over HTTP, WebSocket,
workers, sockets, or custom protocols.
Protocol> =>
import LayerLayer.const unwrap: <RpcClient.Protocol, never, never, never, never>(self: Effect.Effect<Layer.Layer<RpcClient.Protocol, never, never>, never, never>) => Layer.Layer<RpcClient.Protocol, never, never>Unwraps a Layer from an Effect, flattening the nested structure.
When to use
Use when you have an Effect that produces a Layer and you want to
use that layer directly.
Details
The resulting Layer will have the combined error and dependency types from
both the outer Effect and the inner Layer.
Example (Unwrapping an effectful layer)
import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const layerEffect = Effect.succeed(
Layer.succeed(Database, { query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result")) })
)
const unwrappedLayer = Layer.unwrap(layerEffect)
unwrap(
// guard BEFORE the node-only dynamic import, so a browser gets the clear die (not the import error).
const dieIfIpcInBrowser: Effect.Effect<
void,
never,
never
>
const dieIfIpcInBrowser: {
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
dieIfIpcInBrowser.Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/index"), never, never>, Effect.Effect<Layer.Layer<RpcClient.Protocol, never, never>, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/index"), never, never>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)pipe(
import EffectEffect.const andThen: <typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/index"), never, never>(f: Effect.Effect<typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/index"), never, never>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<...> (+3 overloads)Runs this effect and then runs another effect, optionally using the first
effect's success value to choose the next effect.
When to use
Use when you need one effect to run after another and the second effect may
depend on the first effect's success value.
Details
When the second argument is an Effect, the first success value is discarded
and the returned effect produces the second effect's value. When the second
argument is a function, it receives the first success value and must return
the next Effect.
Failures or requirements from either effect are preserved in the returned
effect.
Example (Choosing andThen syntax variants)
import { Effect, pipe } from "effect"
const myEffect = Effect.succeed(1)
const anotherEffect = Effect.succeed("done")
const transformedWithPipe = pipe(myEffect, Effect.andThen(anotherEffect))
const transformedWithDataFirst = Effect.andThen(myEffect, anotherEffect)
const transformedWithMethod = myEffect.pipe(Effect.andThen(anotherEffect))
Example (Sequencing a discount calculation after fetching a total)
import { Data, Effect, pipe } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => Effect.succeed(amount * 2)),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190
andThen(import EffectEffect.const promise: <typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/index")>(evaluate: (signal: AbortSignal) => PromiseLike<typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/index")>) => Effect.Effect<typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/index"), never, never>Creates an Effect that represents an asynchronous computation guaranteed to
succeed.
When to use
Use to convert a Promise into an Effect when the async operation is
guaranteed to succeed and will not reject.
Details
An optional AbortSignal can be provided to allow for interruption of the
wrapped Promise API.
Gotchas
The Promise must not reject. If it rejects, the rejection is treated as a
defect, not as a typed failure. Use tryPromise when rejection is expected.
Interruption aborts the provided AbortSignal, but the underlying
asynchronous operation only stops if it observes that signal.
Example (Wrapping a non-rejecting Promise)
import { Effect } from "effect"
const delay = (message: string) =>
Effect.promise<string>(
() =>
new Promise((resolve) => {
setTimeout(() => {
resolve(message)
}, 2000)
})
)
// ┌─── Effect<string, never, never>
// ▼
const program = delay("Async operation completed successfully!")
promise(() => import("@effect/platform-node"))),
import EffectEffect.const map: <typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/index"), Layer.Layer<RpcClient.Protocol, never, never>>(f: (a: typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/index")) => Layer.Layer<RpcClient.Protocol, never, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)Transforms the value inside an effect by applying a function to it.
When to use
Use to transform an effect's success value with a function that returns a
plain value, producing a new effect without changing the original effect's
typed error or context requirements.
Details
map takes a function and applies it to the value contained within an
effect, creating a new effect with the transformed value.
It's important to note that effects are immutable, meaning that the original
effect is not modified. Instead, a new effect is returned with the updated
value.
Example (Choosing map syntax variants)
import { Effect, pipe } from "effect"
const myEffect = Effect.succeed(1)
const transformation = (n: number) => n + 1
const mappedWithPipe = pipe(myEffect, Effect.map(transformation))
const mappedWithDataFirst = Effect.map(myEffect, transformation)
const mappedWithMethod = myEffect.pipe(Effect.map(transformation))
Example (Adding a service charge)
import { Effect, pipe } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const finalAmount = pipe(
fetchTransactionAmount,
Effect.map(addServiceCharge)
)
Effect.runPromise(finalAmount).then(console.log)
// Output: 101
map(({ type NodeSocket: typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/NodeSocket")(parameter) NodeSocket: {
layerWebSocketConstructor: Layer.Layer<Socket.WebSocketConstructor>;
layerWebSocketConstructorWS: Layer.Layer<Socket.WebSocketConstructor>;
layerWebSocket: (url: string | Effect.Effect<string>, options?: { readonly closeCodeIsError?: ((code: number) => boolean) | undefined; readonly openTimeout?: Duration.Input | undefined; readonly protocols?: string | Array<string> | undefined } | undefined…;
NodeWS: any;
NetSocket: typeof NetSocket;
makeNet: (options: combineByNode & { readonly openTimeout?: Duration.Input | undefined }) => Effect.Effect<Socket.Socket>;
fromDuplex: <RO>(open: Effect.Effect<combineByNode, Socket.SocketError, RO>, options?: { readonly openTimeout?: Duration.Input | undefined }) => Effect.Effect<Socket.Socket, never, Exclude<RO, Scope.Scope>>;
makeNetChannel: <IE = never>(options: combineByNode) => Channel<NonEmptyReadonlyArray<Uint8Array>, Socket.SocketError | IE, void, NonEmptyReadonlyArray<Uint8Array | string | Socket.CloseEvent>, IE>;
layerNet: (options: Net.NetConnectOpts) => Layer.Layer<Socket.Socket, Socket.SocketError>;
}
NodeSocket }) =>
import RpcClientRpcClient.const layerProtocolSocket: (options?: {
readonly retryTransientErrors?:
| boolean
| undefined
}) => Layer.Layer<
RpcClient.Protocol,
never,
| Socket.Socket
| RpcSerialization.RpcSerialization
>
Provides a client Protocol backed by the current Socket and
RpcSerialization services.
layerProtocolSocket().Pipeable.pipe<Layer.Layer<RpcClient.Protocol, never, RpcSerialization.RpcSerialization | Socket.Socket>, Layer.Layer<RpcClient.Protocol, never, Socket.Socket>, Layer.Layer<RpcClient.Protocol, Socket.SocketError, never>, Layer.Layer<RpcClient.Protocol, never, never>>(this: Layer.Layer<...>, ab: (_: Layer.Layer<RpcClient.Protocol, never, RpcSerialization.RpcSerialization | Socket.Socket>) => Layer.Layer<...>, bc: (_: Layer.Layer<...>) => Layer.Layer<...>, cd: (_: Layer.Layer<...>) => Layer.Layer<...>): Layer.Layer<...> (+21 overloads)pipe(
import LayerLayer.const provide: <never, never, RpcSerialization.RpcSerialization>(that: Layer.Layer<RpcSerialization.RpcSerialization, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, E2, Exclude<RIn2, RpcSerialization.RpcSerialization>> (+3 overloads)Feeds the output services of the dependency layer into the requirements of
this layer, returning a layer that only provides the services from this layer.
When to use
Use when you need to hide an implementation dependency layer from callers.
Details
In serviceLayer.pipe(Layer.provide(dependencyLayer)), the dependency layer is
built first and is used to satisfy the requirements of serviceLayer.
Example (Providing layer dependencies)
import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
class Logger extends Context.Service<Logger, {
readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}
// Create dependency layers
const databaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
})
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
})
// UserService depends on Database and Logger
const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
return {
getUser: Effect.fn("UserService.getUser")(function*(id: string) {
yield* logger.log(`Looking up user ${id}`)
const result = yield* database.query(
`SELECT * FROM users WHERE id = ${id}`
)
return { id, name: result }
})
}
}))
// Provide dependencies to UserService layer
const userServiceWithDependencies = userServiceLayer.pipe(
Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now UserService layer has no dependencies
const program = Effect.gen(function*() {
const userService = yield* UserService
return yield* userService.getUser("123")
}).pipe(
Effect.provide(userServiceWithDependencies)
)
provide(serialization: Layer.Layer<
RpcSerialization.RpcSerialization,
never,
never
>
(parameter) serialization: {
build: (memoMap: Layer.MemoMap, scope: Scope.Scope) => Effect.Effect<Context.Context<RpcSerialization.RpcSerialization>, never, never>;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
serialization),
import LayerLayer.const provide: <never, Socket.SocketError, Socket.Socket>(that: Layer.Layer<Socket.Socket, Socket.SocketError, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, Socket.SocketError | E2, Exclude<RIn2, Socket.Socket>> (+3 overloads)Feeds the output services of the dependency layer into the requirements of
this layer, returning a layer that only provides the services from this layer.
When to use
Use when you need to hide an implementation dependency layer from callers.
Details
In serviceLayer.pipe(Layer.provide(dependencyLayer)), the dependency layer is
built first and is used to satisfy the requirements of serviceLayer.
Example (Providing layer dependencies)
import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
class Logger extends Context.Service<Logger, {
readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}
// Create dependency layers
const databaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
})
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
})
// UserService depends on Database and Logger
const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
return {
getUser: Effect.fn("UserService.getUser")(function*(id: string) {
yield* logger.log(`Looking up user ${id}`)
const result = yield* database.query(
`SELECT * FROM users WHERE id = ${id}`
)
return { id, name: result }
})
}
}))
// Provide dependencies to UserService layer
const userServiceWithDependencies = userServiceLayer.pipe(
Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now UserService layer has no dependencies
const program = Effect.gen(function*() {
const userService = yield* UserService
return yield* userService.getUser("123")
}).pipe(
Effect.provide(userServiceWithDependencies)
)
provide(type NodeSocket: typeof import("/Users/nikolasstow/Coding/packages/effect-pm-alt/node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@effect/platform-node/dist/NodeSocket")(parameter) NodeSocket: {
layerWebSocketConstructor: Layer.Layer<Socket.WebSocketConstructor>;
layerWebSocketConstructorWS: Layer.Layer<Socket.WebSocketConstructor>;
layerWebSocket: (url: string | Effect.Effect<string>, options?: { readonly closeCodeIsError?: ((code: number) => boolean) | undefined; readonly openTimeout?: Duration.Input | undefined; readonly protocols?: string | Array<string> | undefined } | undefined…;
NodeWS: any;
NetSocket: typeof NetSocket;
makeNet: (options: combineByNode & { readonly openTimeout?: Duration.Input | undefined }) => Effect.Effect<Socket.Socket>;
fromDuplex: <RO>(open: Effect.Effect<combineByNode, Socket.SocketError, RO>, options?: { readonly openTimeout?: Duration.Input | undefined }) => Effect.Effect<Socket.Socket, never, Exclude<RO, Scope.Scope>>;
makeNetChannel: <IE = never>(options: combineByNode) => Channel<NonEmptyReadonlyArray<Uint8Array>, Socket.SocketError | IE, void, NonEmptyReadonlyArray<Uint8Array | string | Socket.CloseEvent>, IE>;
layerNet: (options: Net.NetConnectOpts) => Layer.Layer<Socket.Socket, Socket.SocketError>;
}
NodeSocket.const layerNet: (
options: NetConnectOpts
) => Layer.Layer<
Socket.Socket,
Socket.SocketError
>
Provides a Socket.Socket by opening a TCP connection with the supplied
Node net connection options.
layerNet({ IpcSocketConnectOpts.path: stringpath })),
import LayerLayer.const orDie: <A, E, R>(
self: Layer.Layer<A, E, R>
) => Layer.Layer<A, never, R>
Converts layer construction failures into defects, removing them from the
layer's error type.
Details
Use this only when failures should be treated as unrecoverable defects rather
than typed errors that callers can handle.
Example (Converting layer failures to defects)
import { Context, Data, Effect, Layer } from "effect"
class DatabaseError extends Data.TaggedError("DatabaseError")<{
message: string
}> {}
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
// Layer that can fail during construction
const flakyDatabaseLayer = Layer.effect(Database, Effect.gen(function*() {
console.log("connecting")
return yield* new DatabaseError({ message: "Connection failed" })
}))
// Convert failures to fiber death - removes error from type
const reliableDatabaseLayer = flakyDatabaseLayer.pipe(Layer.orDie)
// Now the layer type is Layer<Database, never, never> - no error in type
const program = Effect.gen(function*() {
const database = yield* Database
return yield* database.query("SELECT * FROM users")
}).pipe(
Effect.provide(reliableDatabaseLayer)
)
// Running the program prints "connecting", then the DatabaseError is
// converted into a fiber defect instead of remaining a typed error.
orDie,
),
),
),
);