Effect by Example: Next.js API Handler (App Router)

Tags:

nextjs

Raw HTTP APIs

src/app/api/example/route.ts
import {
import HttpApp
HttpApp
,
import HttpServerRequest
HttpServerRequest
,
import HttpServerResponse
HttpServerResponse
,
} from "@effect/platform";
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Layer
Layer
,
import ManagedRuntime
ManagedRuntime
,
import Schema
Schema
} from "effect";
// your main layer representing all of the services your handler needs (db, auth, etc.)
const
const mainLive: Layer.Layer<never, never, never>
mainLive
=
import Layer
Layer
.
const empty: Layer.Layer<never, never, never>

A Layer that constructs an empty Context.

@since2.0.0

empty
;
const
const managedRuntime: ManagedRuntime.ManagedRuntime<never, never>
managedRuntime
=
import ManagedRuntime
ManagedRuntime
.
const make: <never, never>(layer: Layer.Layer<never, never, never>, memoMap?: Layer.MemoMap | undefined) => ManagedRuntime.ManagedRuntime<never, never>

Convert a Layer into an ManagedRuntime, that can be used to run Effect's using your services.

@since2.0.0

@example

import { Console, Effect, Layer, ManagedRuntime } from "effect"
class Notifications extends Effect.Tag("Notifications")<
Notifications,
{ readonly notify: (message: string) => Effect.Effect<void> }
>() {
static Live = Layer.succeed(this, { notify: (message) => Console.log(message) })
}
async function main() {
const runtime = ManagedRuntime.make(Notifications.Live)
await runtime.runPromise(Notifications.notify("Hello, world!"))
await runtime.dispose()
}
main()

make
(
const mainLive: Layer.Layer<never, never, never>
mainLive
);
const
const runtime: Runtime<never>
runtime
= await
const managedRuntime: ManagedRuntime.ManagedRuntime<never, never>
managedRuntime
.
ManagedRuntime<never, never>.runtime: () => Promise<Runtime<never>>
runtime
();
// everything interesting happens in this effect
// Which is of type Effect<HttpServerResponse, _, HttpServerRequest>
// it consumes the request from context anywhere
// and ultimately produces some http response
const
const exampleEffectHandler: Effect.Effect<HttpServerResponse.HttpServerResponse, RequestError | ParseError | HttpBodyError, HttpServerRequest.HttpServerRequest>
exampleEffectHandler
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<{
readonly name: string;
}, RequestError | ParseError, HttpServerRequest.HttpServerRequest>> | YieldWrap<...>, HttpServerResponse.HttpServerResponse>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

Example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
// consumes request from context
const {
const name: string
name
} = yield*
import HttpServerRequest
HttpServerRequest
.
const schemaBodyJson: <{
readonly name: string;
}, {
readonly name: string;
}, never>(schema: Schema.Schema<{
readonly name: string;
}, {
readonly name: string;
}, never>, options?: ParseOptions | undefined) => Effect.Effect<...>

@since1.0.0

schemaBodyJson
(
import Schema
Schema
.
function Struct<{
name: typeof Schema.String;
}>(fields: {
name: typeof Schema.String;
}): Schema.Struct<{
name: typeof Schema.String;
}> (+1 overload)

@since3.10.0

Struct
({
name: typeof Schema.String
name
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
,
}),
);
return yield*
import HttpServerResponse
HttpServerResponse
.
const json: (body: unknown, options?: HttpServerResponse.Options.WithContentType | undefined) => Effect.Effect<HttpServerResponse.HttpServerResponse, HttpBodyError>

@since1.0.0

json
({
message: string
message
: `Hello, ${
const name: string
name
}`,
});
});
const
const handler: (request: Request, context?: Context<never> | undefined) => Promise<Response>
handler
=
import HttpApp
HttpApp
.
const toWebHandlerRuntime: <never>(runtime: Runtime<never>) => <E>(self: HttpApp.Default<E, Scope>, middleware?: HttpMiddleware | undefined) => (request: Request, context?: Context<never> | undefined) => Promise<Response>

@since1.0.0

toWebHandlerRuntime
(
const runtime: Runtime<never>
runtime
)(
const exampleEffectHandler: Effect.Effect<HttpServerResponse.HttpServerResponse, RequestError | ParseError | HttpBodyError, HttpServerRequest.HttpServerRequest>
exampleEffectHandler
);
type
type Handler = (req: Request) => Promise<Response>
Handler
= (
req: Request
req
:
interface Request
Request
) =>
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface Response
Response
>;
export const
const POST: Handler
POST
:
type Handler = (req: Request) => Promise<Response>
Handler
=
const handler: (request: Request, context?: Context<never> | undefined) => Promise<Response>
handler
;

Effect HttpApi framework

src/app/api/[[...path]]/route.ts
import {
import FetchHttpClient
FetchHttpClient
,
import HttpApi
HttpApi
,
import HttpApiBuilder
HttpApiBuilder
,
import HttpApiClient
HttpApiClient
,
import HttpApiEndpoint
HttpApiEndpoint
,
import HttpApiGroup
HttpApiGroup
,
import HttpApiSwagger
HttpApiSwagger
,
import HttpMiddleware
HttpMiddleware
,
import HttpServer
HttpServer
,
import OpenApi
OpenApi
,
} from "@effect/platform";
import {
import Config
Config
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Layer
Layer
,
import Schema
Schema
} from "effect";
// ------------------------------------------------
// schema
// ------------------------------------------------
class
class FooError
FooError
extends
import Schema
Schema
.
const TaggedError: <FooError>(identifier?: string) => <Tag, Fields>(tag: Tag, fieldsOr: Fields | HasFields<Fields>, annotations?: ClassAnnotations<FooError, { [K in keyof Schema.Struct<Fields extends Schema.Struct.Fields>.Type<...>]: Schema.Struct.Type<...>[K]; }> | undefined) => Schema.TaggedErrorClass<...>

@example

import { Schema } from "effect"
class MyError extends Schema.TaggedError<MyError>("MyError")(
"MyError",
{
module: Schema.String,
method: Schema.String,
description: Schema.String
}
) {
get message(): string {
return `${this.module}.${this.method}: ${this.description}`
}
}

@since3.10.0

TaggedError
<
class FooError
FooError
>("FooError")(
"FooError",
{},
) {}
class
class FooApi
FooApi
extends
import HttpApiGroup
HttpApiGroup
.
const make: <"foo", false>(identifier: "foo", options?: {
readonly topLevel?: false | undefined;
} | undefined) => HttpApiGroup.HttpApiGroup<"foo", never, never, never, false>

An HttpApiGroup is a collection of HttpApiEndpoints. You can use an HttpApiGroup to represent a portion of your domain.

The endpoints can be implemented later using the HttpApiBuilder.group api.

@since1.0.0

make
("foo")
.
HttpApiGroup<"foo", never, never, never, false>.add<HttpApiEndpoint.HttpApiEndpoint<"bar", "GET", never, never, never, {
readonly page: number;
}, string, never, never, never>>(endpoint: HttpApiEndpoint.HttpApiEndpoint<"bar", "GET", never, never, ... 5 more ..., never>): HttpApiGroup.HttpApiGroup<...>

Add an HttpApiEndpoint to an HttpApiGroup.

add
(
import HttpApiEndpoint
HttpApiEndpoint
.
const get: <"bar">(name: "bar", path: HttpApiEndpoint.PathSegment) => HttpApiEndpoint.HttpApiEndpoint<"bar", "GET", never, never, never, never, void, never, never, never> (+1 overload)

@since1.0.0

get
("bar", "/bar")
.
HttpApiEndpoint<"bar", "GET", never, never, never, never, void, never, never, never>.setHeaders<Schema.Struct<{
page: typeof Schema.NumberFromString;
}>>(schema: Schema.Struct<{
page: typeof Schema.NumberFromString;
}>): HttpApiEndpoint.HttpApiEndpoint<"bar", "GET", never, never, ... 5 more ..., never>

Set the schema for the headers of the endpoint. The schema will be used to validate the headers before the handler is called.

setHeaders
(
import Schema
Schema
.
function Struct<{
page: typeof Schema.NumberFromString;
}>(fields: {
page: typeof Schema.NumberFromString;
}): Schema.Struct<{
page: typeof Schema.NumberFromString;
}> (+1 overload)

@since3.10.0

Struct
({
page: typeof Schema.NumberFromString
page
:
import Schema
Schema
.
class NumberFromString

This schema transforms a string into a number by parsing the string using the parse function of the effect/Number module.

It returns an error if the value can't be converted (for example when non-numeric characters are provided).

The following special string values are supported: "NaN", "Infinity", "-Infinity".

@since3.10.0

NumberFromString
}))
.
HttpApiEndpoint<"bar", "GET", never, never, never, { readonly page: number; }, void, never, never, never>.addSuccess<typeof Schema.String>(schema: typeof Schema.String, annotations?: {
readonly status?: number | undefined;
} | undefined): HttpApiEndpoint.HttpApiEndpoint<"bar", "GET", never, never, never, {
readonly page: number;
}, string, never, never, never>

Add a schema for the success response of the endpoint. The status code will be inferred from the schema, otherwise it will default to 200.

addSuccess
(
import Schema
Schema
.
class String
export String

@since3.10.0

String
),
)
.
HttpApiGroup<"foo", HttpApiEndpoint<"bar", "GET", never, never, never, { readonly page: number; }, string, never, never, never>, never, never, false>.add<HttpApiEndpoint.HttpApiEndpoint<"baz", "POST", {
readonly id: number;
}, never, {
readonly name: string;
}, never, {
readonly ok: boolean;
}, FooError, never, never>>(endpoint: HttpApiEndpoint.HttpApiEndpoint<...>): HttpApiGroup.HttpApiGroup<...>

Add an HttpApiEndpoint to an HttpApiGroup.

add
(
import HttpApiEndpoint
HttpApiEndpoint
.
const post: <"baz">(name: "baz", path: HttpApiEndpoint.PathSegment) => HttpApiEndpoint.HttpApiEndpoint<"baz", "POST", never, never, never, never, void, never, never, never> (+1 overload)

@since1.0.0

post
("baz", "/baz/:id")
.
HttpApiEndpoint<"baz", "POST", never, never, never, never, void, never, never, never>.setPath<Schema.Struct<{
id: typeof Schema.NumberFromString;
}>>(schema: Schema.Struct<{
id: typeof Schema.NumberFromString;
}>): HttpApiEndpoint.HttpApiEndpoint<"baz", "POST", {
readonly id: number;
}, ... 6 more ..., never>

Set the schema for the path parameters of the endpoint. The schema will be used to validate the path parameters before the handler is called.

setPath
(
import Schema
Schema
.
function Struct<{
id: typeof Schema.NumberFromString;
}>(fields: {
id: typeof Schema.NumberFromString;
}): Schema.Struct<{
id: typeof Schema.NumberFromString;
}> (+1 overload)

@since3.10.0

Struct
({
id: typeof Schema.NumberFromString
id
:
import Schema
Schema
.
class NumberFromString

This schema transforms a string into a number by parsing the string using the parse function of the effect/Number module.

It returns an error if the value can't be converted (for example when non-numeric characters are provided).

The following special string values are supported: "NaN", "Infinity", "-Infinity".

@since3.10.0

NumberFromString
}))
.
HttpApiEndpoint<"baz", "POST", { readonly id: number; }, never, never, never, void, never, never, never>.setPayload<Schema.Struct<{
name: typeof Schema.String;
}>>(schema: Schema.Struct<{
name: typeof Schema.String;
}>): HttpApiEndpoint.HttpApiEndpoint<"baz", "POST", {
readonly id: number;
}, never, {
readonly name: string;
}, ... 4 more ..., never>

Set the schema for the request body of the endpoint. The schema will be used to validate the request body before the handler is called.

For endpoints with no request body, the payload will use the url search parameters.

You can set a multipart schema to handle file uploads by using the HttpApiSchema.Multipart combinator.

setPayload
(
import Schema
Schema
.
function Struct<{
name: typeof Schema.String;
}>(fields: {
name: typeof Schema.String;
}): Schema.Struct<{
name: typeof Schema.String;
}> (+1 overload)

@since3.10.0

Struct
({
name: typeof Schema.String
name
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
}))
.
HttpApiEndpoint<"baz", "POST", { readonly id: number; }, never, { readonly name: string; }, never, void, never, never, never>.addSuccess<Schema.Struct<{
ok: typeof Schema.Boolean;
}>>(schema: Schema.Struct<{
ok: typeof Schema.Boolean;
}>, annotations?: {
readonly status?: number | undefined;
} | undefined): HttpApiEndpoint.HttpApiEndpoint<...>

Add a schema for the success response of the endpoint. The status code will be inferred from the schema, otherwise it will default to 200.

addSuccess
(
import Schema
Schema
.
function Struct<{
ok: typeof Schema.Boolean;
}>(fields: {
ok: typeof Schema.Boolean;
}): Schema.Struct<{
ok: typeof Schema.Boolean;
}> (+1 overload)

@since3.10.0

Struct
({
ok: typeof Schema.Boolean
ok
:
import Schema
Schema
.
class Boolean
export Boolean

@since3.10.0

Boolean
}))
.
HttpApiEndpoint<"baz", "POST", { readonly id: number; }, never, { readonly name: string; }, never, { readonly ok: boolean; }, never, never, never>.addError<typeof FooError>(schema: typeof FooError, annotations?: {
readonly status?: number | undefined;
} | undefined): HttpApiEndpoint.HttpApiEndpoint<"baz", "POST", {
readonly id: number;
}, never, {
readonly name: string;
}, ... 4 more ..., never>

Add an error response schema to the endpoint. The status code will be inferred from the schema, otherwise it will default to 500.

addError
(
class FooError
FooError
),
) {}
class
class MyApi
MyApi
extends
import HttpApi
HttpApi
.
const make: <"api">(identifier: "api") => HttpApi.HttpApi<"api", never, HttpApiDecodeError, never>

An HttpApi is a collection of HttpApiEndpoints. You can use an HttpApi to represent a portion of your domain.

The endpoints can be implemented later using the HttpApiBuilder.make api.

@since1.0.0

make
("api")
.
HttpApi<"api", never, HttpApiDecodeError, never>.add<typeof FooApi>(group: typeof FooApi): HttpApi.HttpApi<"api", typeof FooApi, HttpApiDecodeError, never>

Add a HttpApiGroup to the HttpApi.

add
(
class FooApi
FooApi
)
.
HttpApi<"api", typeof FooApi, HttpApiDecodeError, never>.prefix(prefix: HttpApiEndpoint.PathSegment): HttpApi.HttpApi<"api", typeof FooApi, HttpApiDecodeError, never>

Prefix all endpoints in the HttpApi.

prefix
("/api")
.
HttpApi<"api", typeof FooApi, HttpApiDecodeError, never>.annotateContext<never>(context: Context<never>): HttpApi.HttpApi<"api", typeof FooApi, HttpApiDecodeError, never>

Annotate the HttpApi with a Context.

annotateContext
(
import OpenApi
OpenApi
.
const annotations: (options: {
readonly identifier?: string | undefined;
readonly title?: string | undefined;
readonly version?: string | undefined;
readonly description?: string | undefined;
readonly license?: OpenApi.OpenAPISpecLicense | undefined;
readonly summary?: string | undefined;
readonly deprecated?: boolean | undefined;
readonly externalDocs?: OpenApi.OpenAPISpecExternalDocs | undefined;
readonly servers?: ReadonlyArray<OpenApi.OpenAPISpecServer> | undefined;
readonly format?: string | undefined;
readonly override?: Record<string, unknown> | undefined;
readonly exclude?: boolean | undefined;
readonly transform?: ((openApiSpec: Record<string, any>) => Record<string, any>) | undefined;
}) => Context<never>

@since1.0.0

annotations
({
title?: string | undefined
title
: "My API",
description?: string | undefined
description
: "API for my endpoints",
}),
) {}
// ------------------------------------------------
// implementation
// ------------------------------------------------
const
const FooLive: Layer.Layer<HttpApiGroup.ApiGroup<"api", "foo">, never, never>
FooLive
=
import HttpApiBuilder
HttpApiBuilder
.
const group: <"api", typeof FooApi, HttpApiDecodeError, never, "foo", HttpApiBuilder.Handlers<HttpApiDecodeError, never, never, never>>(api: HttpApi.HttpApi<...>, groupName: "foo", build: (handlers: HttpApiBuilder.Handlers<E, Provides, R, Endpoints extends HttpApiEndpoint.HttpApiEndpoint.Any = never>.FromGroup<...>) => HttpApiBuilder.Handlers<...>) => Layer.Layer<...>

Create a Layer that will implement all the endpoints in an HttpApi.

An unimplemented Handlers instance is passed to the build function, which you can use to add handlers to the group.

You can implement endpoints using the handlers.handle api.

@since1.0.0

group
(
class MyApi
MyApi
, "foo", (
handlers: HttpApiBuilder.Handlers.FromGroup<HttpApiDecodeError, never, typeof FooApi>
handlers
) =>
handlers: HttpApiBuilder.Handlers.FromGroup<HttpApiDecodeError, never, typeof FooApi>
handlers
.
Handlers<HttpApiDecodeError, never, never, HttpApiEndpoint<"bar", "GET", never, never, never, { readonly page: number; }, string, never, never, never> | HttpApiEndpoint<...>>.handle<"bar", never>(name: "bar", handler: HttpApiEndpoint.HttpApiEndpoint<out Name extends string, out Method extends HttpMethod, in out Path = never, in out UrlParams = never, in out Payload = never, in out Headers = never, in out Success = void, in out Error = never, out R = never, out RE = never>.HandlerWithName<HttpApiEndpoint.HttpApiEndpoint<"bar", "GET", never, never, never, {
readonly page: number;
}, string, never, never, never> | HttpApiEndpoint.HttpApiEndpoint<...>, "bar", HttpApiDecodeError, never>): HttpApiBuilder.Handlers<...>

Add the implementation for an HttpApiEndpoint to a Handlers group.

handle
("bar", (
_: {
readonly headers: {
readonly page: number;
};
}
_
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const succeed: <string>(value: string) => Effect.Effect<string, never, never>

Creates an Effect that always succeeds with a given value.

When to Use

Use this function when you need an effect that completes successfully with a specific value without any errors or external dependencies.

Example (Creating a Successful Effect)

import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)

@seefail to create an effect that represents a failure.

@since2.0.0

succeed
(`page: ${
_: {
readonly headers: {
readonly page: number;
};
}
_
.
headers: {
readonly page: number;
}
headers
.
page: number
page
}`))
.
Handlers<HttpApiDecodeError, never, never, HttpApiEndpoint<"baz", "POST", { readonly id: number; }, never, { readonly name: string; }, never, { readonly ok: boolean; }, FooError, never, never>>.handle<"baz", never>(name: "baz", handler: HttpApiEndpoint.HttpApiEndpoint<out Name extends string, out Method extends HttpMethod, in out Path = never, in out UrlParams = never, in out Payload = never, in out Headers = never, in out Success = void, in out Error = never, out R = never, out RE = never>.HandlerWithName<HttpApiEndpoint.HttpApiEndpoint<"baz", "POST", {
readonly id: number;
}, never, {
readonly name: string;
}, never, {
readonly ok: boolean;
}, FooError, never, never>, "baz", HttpApiDecodeError, never>): HttpApiBuilder.Handlers<...>

Add the implementation for an HttpApiEndpoint to a Handlers group.

handle
("baz", (
_: {
readonly path: {
readonly id: number;
};
readonly payload: {
readonly name: string;
};
}
_
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<never, FooError, never>>, {
ok: boolean;
}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<never, FooError, never>>, {
ok: boolean;
}, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

Example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
const
const id: number
id
=
_: {
readonly path: {
readonly id: number;
};
readonly payload: {
readonly name: string;
};
}
_
.
path: {
readonly id: number;
}
path
.
id: number
id
;
if (
const id: number
id
< 0) {
return yield* new
constructor FooError(props: void | {}, options?: Schema.MakeOptions): FooError
FooError
();
}
return {
ok: boolean
ok
:
_: {
readonly path: {
readonly id: number;
};
readonly payload: {
readonly name: string;
};
}
_
.
payload: {
readonly name: string;
}
payload
.
name: string
name
.
String.length: number

Returns the length of a String object.

length
===
const id: number
id
,
};
}),
),
);
const
const ApiLive: Layer.Layer<HttpApi.Api, never, never>
ApiLive
=
import HttpApiBuilder
HttpApiBuilder
.
const api: <"api", typeof FooApi, HttpApiDecodeError, never>(api: HttpApi.HttpApi<"api", typeof FooApi, HttpApiDecodeError, never>) => Layer.Layer<...>

Create a top-level HttpApi layer.

@since1.0.0

api
(
class MyApi
MyApi
).
Pipeable.pipe<Layer.Layer<HttpApi.Api, never, HttpApiGroup.ApiGroup<"api", "foo">>, Layer.Layer<HttpApi.Api, never, never>>(this: Layer.Layer<...>, ab: (_: Layer.Layer<HttpApi.Api, never, HttpApiGroup.ApiGroup<...>>) => Layer.Layer<...>): Layer.Layer<...> (+21 overloads)
pipe
(
import Layer
Layer
.
const provide: <never, never, HttpApiGroup.ApiGroup<"api", "foo">>(that: Layer.Layer<HttpApiGroup.ApiGroup<"api", "foo">, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<...>) => Layer.Layer<...> (+3 overloads)

Feeds the output services of this builder into the input of the specified builder, resulting in a new builder with the inputs of this builder as well as any leftover inputs, and the outputs of the specified builder.

@since2.0.0

provide
(
const FooLive: Layer.Layer<HttpApiGroup.ApiGroup<"api", "foo">, never, never>
FooLive
));
// ------------------------------------------------
// handler
// ------------------------------------------------
const
const middleware: Layer.Layer<never, never, HttpApi.Api>
middleware
=
import Layer
Layer
.
const mergeAll: <[Layer.Layer<never, never, never>, Layer.Layer<never, never, HttpApi.Api>, Layer.Layer<never, never, HttpApi.Api>, Layer.Layer<never, never, never>]>(layers_0: Layer.Layer<...>, layers_1: Layer.Layer<...>, layers_2: Layer.Layer<...>, layers_3: Layer.Layer<...>) => Layer.Layer<...>

Combines all the provided layers concurrently, creating a new layer with merged input, error, and output types.

@since2.0.0

mergeAll
(
import HttpApiBuilder
HttpApiBuilder
.
const middlewareCors: (options?: {
readonly allowedOrigins?: ReadonlyArray<string> | undefined;
readonly allowedMethods?: ReadonlyArray<string> | undefined;
readonly allowedHeaders?: ReadonlyArray<string> | undefined;
readonly exposedHeaders?: ReadonlyArray<string> | undefined;
readonly maxAge?: number | undefined;
readonly credentials?: boolean | undefined;
} | undefined) => Layer.Layer<never>

A CORS middleware layer that can be provided to the HttpApiBuilder.serve layer.

@since1.0.0

middlewareCors
(), // cors
import HttpApiBuilder
HttpApiBuilder
.
const middlewareOpenApi: (options?: {
readonly path?: HttpApiEndpoint.PathSegment | undefined;
readonly additionalPropertiesStrategy?: OpenApi.AdditionalPropertiesStrategy | undefined;
} | undefined) => Layer.Layer<never, never, HttpApi.Api>

A middleware that adds an openapi.json endpoint to the API.

@since1.0.0

middlewareOpenApi
({
path?: `/${string}` | undefined
path
: "/api/openapi.json",
}), // openapi
import HttpApiSwagger
HttpApiSwagger
.
const layer: (options?: {
readonly path?: `/${string}` | undefined;
}) => Layer.Layer<never, never, HttpApi.Api>

@since1.0.0

layer
({
path?: `/${string}` | undefined
path
: "/api/docs",
}), // swagger
import HttpApiBuilder
HttpApiBuilder
.
const middleware: <never, never>(middleware: HttpApiBuilder.MiddlewareFn<never, HttpRouter<E = never, R = never>.Provided> | Effect.Effect<HttpApiBuilder.MiddlewareFn<never, HttpRouter.Provided>, never, never>, options?: {
readonly withContext?: false | undefined;
}) => Layer.Layer<...> (+3 overloads)

Create an HttpApi level middleware Layer.

@since1.0.0

middleware
(
import HttpMiddleware
HttpMiddleware
.
const logger: <E, R>(httpApp: Default<E, R>) => Default<E, R>

@since1.0.0

logger
), // Standard http middlewares
);
const {
const handler: (request: Request, context?: Context<never> | undefined) => Promise<Response>
handler
} =
import Layer
Layer
.
const empty: Layer.Layer<never, never, never>

A Layer that constructs an empty Context.

@since2.0.0

empty
.
Pipeable.pipe<Layer.Layer<never, never, never>, Layer.Layer<never, never, HttpApi.Api>, Layer.Layer<HttpApi.Api, never, never>, Layer.Layer<HttpPlatform | Generator | FileSystem | Path | HttpApi.Api, never, never>, {
...;
}>(this: Layer.Layer<...>, ab: (_: Layer.Layer<...>) => Layer.Layer<...>, bc: (_: Layer.Layer<...>) => Layer.Layer<...>, cd: (_: Layer.Layer<...>) => Layer.Layer<...>, de: (_: Layer.Layer<...>) => {
...;
}): {
...;
} (+21 overloads)
pipe
(
import Layer
Layer
.
const merge: <HttpApi.Api, never, never>(that: Layer.Layer<never, never, HttpApi.Api>) => <RIn, E1, ROut>(self: Layer.Layer<ROut, E1, RIn>) => Layer.Layer<...> (+1 overload)

Merges this layer with the specified layer concurrently, producing a new layer with combined input and output types.

@since2.0.0

merge
(
const middleware: Layer.Layer<never, never, HttpApi.Api>
middleware
),
import Layer
Layer
.
const provideMerge: <never, never, HttpApi.Api>(self: Layer.Layer<HttpApi.Api, never, never>) => <RIn2, E2, ROut2>(that: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<...> (+1 overload)

Feeds the output services of this layer into the input of the specified layer, resulting in a new layer with the inputs of this layer, and the outputs of both layers.

@since2.0.0

provideMerge
(
const ApiLive: Layer.Layer<HttpApi.Api, never, never>
ApiLive
),
import Layer
Layer
.
const merge: <never, never, HttpPlatform | Generator | FileSystem | Path>(that: Layer.Layer<HttpPlatform | Generator | FileSystem | Path, never, never>) => <RIn, E1, ROut>(self: Layer.Layer<...>) => Layer.Layer<...> (+1 overload)

Merges this layer with the specified layer concurrently, producing a new layer with combined input and output types.

@since2.0.0

merge
(
import HttpServer
HttpServer
.
const layerContext: Layer.Layer<HttpPlatform | Generator | FileSystem | Path, never, never>

A Layer providing the HttpPlatform, FileSystem, Etag.Generator, and Path services.

The FileSystem service is a no-op implementation, so this layer is only useful for platforms that have no file system.

@since1.0.0

layerContext
),
import HttpApiBuilder
HttpApiBuilder
.
const toWebHandler: <LA, LE>(layer: Layer.Layer<LA | HttpApi.Api | HttpRouter.DefaultServices, LE>, options?: {
readonly middleware?: (httpApp: Default) => Default<never, HttpApi.Api | HttpApiBuilder.Router | HttpRouter.DefaultServices>;
readonly memoMap?: Layer.MemoMap;
}) => {
readonly handler: (request: Request, context?: Context<never> | undefined) => Promise<Response>;
readonly dispose: () => Promise<void>;
}

Construct an http web handler from an HttpApi instance.

@since1.0.0

@example

import { HttpApi, HttpApiBuilder, HttpServer } from "@effect/platform"
import { Layer } from "effect"
class MyApi extends HttpApi.make("api") {}
const MyApiLive = HttpApiBuilder.api(MyApi)
const { dispose, handler } = HttpApiBuilder.toWebHandler(
Layer.mergeAll(
MyApiLive,
// you could also use NodeHttpServer.layerContext, depending on your
// server's platform
HttpServer.layerContext
)
)

toWebHandler
,
);
type
type Handler = (req: Request) => Promise<Response>
Handler
= (
req: Request
req
:
interface Request
Request
) =>
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface Response
Response
>;
export const
const GET: Handler
GET
:
type Handler = (req: Request) => Promise<Response>
Handler
=
const handler: (request: Request, context?: Context<never> | undefined) => Promise<Response>
handler
;
export const
const POST: Handler
POST
:
type Handler = (req: Request) => Promise<Response>
Handler
=
const handler: (request: Request, context?: Context<never> | undefined) => Promise<Response>
handler
;
export const
const PUT: Handler
PUT
:
type Handler = (req: Request) => Promise<Response>
Handler
=
const handler: (request: Request, context?: Context<never> | undefined) => Promise<Response>
handler
;
export const
const PATCH: Handler
PATCH
:
type Handler = (req: Request) => Promise<Response>
Handler
=
const handler: (request: Request, context?: Context<never> | undefined) => Promise<Response>
handler
;
export const
const DELETE: Handler
DELETE
:
type Handler = (req: Request) => Promise<Response>
Handler
=
const handler: (request: Request, context?: Context<never> | undefined) => Promise<Response>
handler
;
export const
const OPTIONS: Handler
OPTIONS
:
type Handler = (req: Request) => Promise<Response>
Handler
=
const handler: (request: Request, context?: Context<never> | undefined) => Promise<Response>
handler
;
// ------------------------------------------------
// typesafe client
// ------------------------------------------------
const
const example: Effect.Effect<{
res: string;
res2: {
readonly ok: boolean;
};
}, FooError | HttpApiDecodeError | ConfigError | HttpClientError, never>
example
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<string, ConfigError, never>> | YieldWrap<Effect.Effect<{
readonly foo: {
readonly bar: <WithResponse>(request: {
readonly headers: {
readonly page: number;
};
readonly withResponse?: WithResponse | undefined;
}) => Effect.Effect<...>;
readonly baz: <WithResponse>(request: {
...;
}) => Effect.Effect<...>;
};
}, never, HttpClient>> | YieldWrap<...> | YieldWrap<...>, {
...;
}>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

Example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
// import schema only
const
const client: {
readonly foo: {
readonly bar: <WithResponse extends boolean = false>(request: {
readonly headers: {
readonly page: number;
};
readonly withResponse?: WithResponse | undefined;
}) => Effect.Effect<WithResponse extends true ? [...] : string, HttpApiDecodeError | HttpClientError, never>;
readonly baz: <WithResponse extends boolean = false>(request: {
...;
}) => Effect.Effect<...>;
};
}
client
= yield*
import HttpApiClient
HttpApiClient
.
const make: <"api", typeof FooApi, HttpApiDecodeError, never>(api: HttpApi.HttpApi<"api", typeof FooApi, HttpApiDecodeError, never>, options?: {
readonly transformClient?: ((client: HttpClient) => HttpClient) | undefined;
readonly transformResponse?: ((effect: Effect.Effect<unknown, unknown, Scope>) => Effect.Effect<unknown, unknown, Scope>) | undefined;
readonly baseUrl?: string | undefined;
}) => Effect.Effect<...>

@since1.0.0

make
(
class MyApi
MyApi
, {
baseUrl?: string | undefined
baseUrl
: yield*
import Config
Config
.
const string: (name?: string) => Config.Config<string>

Constructs a config for a string value.

@since2.0.0

string
("BASE_URL"),
});
const
const res: string
res
= yield*
const client: {
readonly foo: {
readonly bar: <WithResponse extends boolean = false>(request: {
readonly headers: {
readonly page: number;
};
readonly withResponse?: WithResponse | undefined;
}) => Effect.Effect<WithResponse extends true ? [...] : string, HttpApiDecodeError | HttpClientError, never>;
readonly baz: <WithResponse extends boolean = false>(request: {
...;
}) => Effect.Effect<...>;
};
}
client
.
foo: {
readonly bar: <WithResponse extends boolean = false>(request: {
readonly headers: {
readonly page: number;
};
readonly withResponse?: WithResponse | undefined;
}) => Effect.Effect<WithResponse extends true ? [...] : string, HttpApiDecodeError | HttpClientError, never>;
readonly baz: <WithResponse extends boolean = false>(request: {
...;
}) => Effect.Effect<...>;
}
foo
.
bar: <false>(request: {
readonly headers: {
readonly page: number;
};
readonly withResponse?: false | undefined;
}) => Effect.Effect<string, HttpApiDecodeError | HttpClientError, never>
bar
({
headers: {
readonly page: number;
}
headers
: {
page: number
page
: 1 } });
const
const res2: {
readonly ok: boolean;
}
res2
= yield*
const client: {
readonly foo: {
readonly bar: <WithResponse extends boolean = false>(request: {
readonly headers: {
readonly page: number;
};
readonly withResponse?: WithResponse | undefined;
}) => Effect.Effect<WithResponse extends true ? [...] : string, HttpApiDecodeError | HttpClientError, never>;
readonly baz: <WithResponse extends boolean = false>(request: {
...;
}) => Effect.Effect<...>;
};
}
client
.
foo: {
readonly bar: <WithResponse extends boolean = false>(request: {
readonly headers: {
readonly page: number;
};
readonly withResponse?: WithResponse | undefined;
}) => Effect.Effect<WithResponse extends true ? [...] : string, HttpApiDecodeError | HttpClientError, never>;
readonly baz: <WithResponse extends boolean = false>(request: {
...;
}) => Effect.Effect<...>;
}
foo
.
baz: <false>(request: {
readonly path: {
readonly id: number;
};
readonly payload: {
readonly name: string;
};
readonly withResponse?: false | undefined;
}) => Effect.Effect<{
readonly ok: boolean;
}, FooError | HttpApiDecodeError | HttpClientError, never>
baz
({
path: {
readonly id: number;
}
path
: {
id: number
id
: 1 },
payload: {
readonly name: string;
}
payload
: {
name: string
name
: "test" },
});
return {
res: string
res
,
res2: {
readonly ok: boolean;
}
res2
};
}).
Pipeable.pipe<Effect.Effect<{
res: string;
res2: {
readonly ok: boolean;
};
}, FooError | HttpApiDecodeError | ConfigError | HttpClientError, HttpClient>, Effect.Effect<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <HttpClient, never, never>(layer: Layer.Layer<HttpClient, never, never>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<...>> (+9 overloads)

Provides necessary dependencies to an effect, removing its environmental requirements.

Details

This function allows you to supply the required environment for an effect. The environment can be provided in the form of one or more Layers, a Context, a Runtime, or a ManagedRuntime. Once the environment is provided, the effect can run without requiring external dependencies.

You can compose layers to create a modular and reusable way of setting up the environment for effects. For example, layers can be used to configure databases, logging services, or any other required dependencies.

Example

import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<Array<unknown>> }
>() {}
const DatabaseLive = Layer.succeed(
Database,
{
// Simulate a database query
query: (sql: string) => Effect.log(`Executing query: ${sql}`).pipe(Effect.as([]))
}
)
// ┌─── Effect<unknown[], never, Database>
// ▼
const program = Effect.gen(function*() {
const database = yield* Database
const result = yield* database.query("SELECT * FROM users")
return result
})
// ┌─── Effect<unknown[], never, never>
// ▼
const runnable = Effect.provide(program, DatabaseLive)
Effect.runPromise(runnable).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"
// []

@seeprovideService for providing a service to an effect.

@since2.0.0

provide
(
import FetchHttpClient
FetchHttpClient
.
const layer: Layer.Layer<HttpClient, never, never>

@since1.0.0

layer
));

waitUntil

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Runtime
Runtime
,
import Context

@since2.0.0

@since2.0.0

Context
,
import Layer
Layer
} from "effect";
class
class WaitUntil
WaitUntil
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"WaitUntil">(id: "WaitUntil") => <Self, Shape>() => Context.TagClass<Self, "WaitUntil", Shape>

@example

import * as assert from "node:assert"
import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("WaitUntil")<
class WaitUntil
WaitUntil
,
(
promise: Promise<unknown>
promise
:
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<unknown>) => void
>() {}
const
const effectWaitUntil: <A, E, R>(effect: Effect.Effect<A, E, R>, abortSignal?: AbortSignal) => Effect.Effect<void, never, WaitUntil | R>
effectWaitUntil
= <
function (type parameter) A in <A, E, R>(effect: Effect.Effect<A, E, R>, abortSignal?: AbortSignal): Effect.Effect<void, never, WaitUntil | R>
A
,
function (type parameter) E in <A, E, R>(effect: Effect.Effect<A, E, R>, abortSignal?: AbortSignal): Effect.Effect<void, never, WaitUntil | R>
E
,
function (type parameter) R in <A, E, R>(effect: Effect.Effect<A, E, R>, abortSignal?: AbortSignal): Effect.Effect<void, never, WaitUntil | R>
R
>(
effect: Effect.Effect<A, E, R>
effect
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<
function (type parameter) A in <A, E, R>(effect: Effect.Effect<A, E, R>, abortSignal?: AbortSignal): Effect.Effect<void, never, WaitUntil | R>
A
,
function (type parameter) E in <A, E, R>(effect: Effect.Effect<A, E, R>, abortSignal?: AbortSignal): Effect.Effect<void, never, WaitUntil | R>
E
,
function (type parameter) R in <A, E, R>(effect: Effect.Effect<A, E, R>, abortSignal?: AbortSignal): Effect.Effect<void, never, WaitUntil | R>
R
>,
abortSignal: AbortSignal | undefined
abortSignal
?:
interface AbortSignal
AbortSignal
,
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runtime: <R>() => Effect.Effect<Runtime.Runtime<R>, never, R>

Returns an effect that accesses the runtime, which can be used to (unsafely) execute tasks.

When to Use

This is useful for integration with legacy code that must call back into Effect code.

@since2.0.0

runtime
<
function (type parameter) R in <A, E, R>(effect: Effect.Effect<A, E, R>, abortSignal?: AbortSignal): Effect.Effect<void, never, WaitUntil | R>
R
>().
Pipeable.pipe<Effect.Effect<Runtime.Runtime<R>, never, R>, Effect.Effect<[Runtime.Runtime<R>, (promise: Promise<unknown>) => void], never, WaitUntil | R>, Effect.Effect<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const zip: <(promise: Promise<unknown>) => void, never, WaitUntil>(that: Effect.Effect<(promise: Promise<unknown>) => void, never, WaitUntil>, options?: {
readonly concurrent?: boolean | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

Combines two effects into a single effect, producing a tuple of their results.

Details

This function combines two effects, self and that, into one. It executes the first effect (self) and then the second effect (that), collecting their results into a tuple. Both effects must succeed for the resulting effect to succeed. If either effect fails, the entire operation fails.

By default, the effects are executed sequentially. If the concurrent option is set to true, the effects will run concurrently, potentially improving performance for independent operations.

Example (Combining Two Effects Sequentially)

import { Effect } from "effect"
const task1 = Effect.succeed(1).pipe(
Effect.delay("200 millis"),
Effect.tap(Effect.log("task1 done"))
)
const task2 = Effect.succeed("hello").pipe(
Effect.delay("100 millis"),
Effect.tap(Effect.log("task2 done"))
)
// Combine the two effects together
//
// ┌─── Effect<[number, string], never, never>
// ▼
const program = Effect.zip(task1, task2)
Effect.runPromise(program).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="task1 done"
// timestamp=... level=INFO fiber=#0 message="task2 done"
// [ 1, 'hello' ]

Example (Combining Two Effects Concurrently)

import { Effect } from "effect"
const task1 = Effect.succeed(1).pipe(
Effect.delay("200 millis"),
Effect.tap(Effect.log("task1 done"))
)
const task2 = Effect.succeed("hello").pipe(
Effect.delay("100 millis"),
Effect.tap(Effect.log("task2 done"))
)
// Run both effects concurrently using the concurrent option
const program = Effect.zip(task1, task2, { concurrent: true })
Effect.runPromise(program).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="task2 done"
// timestamp=... level=INFO fiber=#0 message="task1 done"
// [ 1, 'hello' ]

@seezipWith for a version that combines the results with a custom function.

@seevalidate for a version that accumulates errors.

@since2.0.0

zip
(
class WaitUntil
WaitUntil
),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const flatMap: <[Runtime.Runtime<R>, (promise: Promise<unknown>) => void], void, never, never>(f: (a: [Runtime.Runtime<R>, (promise: Promise<unknown>) => void]) => Effect.Effect<...>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

Chains effects to produce new Effect instances, useful for combining operations that depend on previous results.

Syntax

const flatMappedEffect = pipe(myEffect, Effect.flatMap(transformation))
// or
const flatMappedEffect = Effect.flatMap(myEffect, transformation)
// or
const flatMappedEffect = myEffect.pipe(Effect.flatMap(transformation))

Details

flatMap lets you sequence effects so that the result of one effect can be used in the next step. It is similar to flatMap used with arrays but works specifically with Effect instances, allowing you to avoid deeply nested effect structures.

Since effects are immutable, flatMap always returns a new effect instead of changing the original one.

When to Use

Use flatMap when you need to chain multiple effects, ensuring that each step produces a new Effect while flattening any nested effects that may occur.

Example

import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Chaining the fetch and discount application using `flatMap`
const finalAmount = pipe(
fetchTransactionAmount,
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(finalAmount).then(console.log)
// Output: 95

@seetap for a version that ignores the result of the effect.

@since2.0.0

flatMap
(([
runtime: Runtime.Runtime<R>
runtime
,
waitUntil: (promise: Promise<unknown>) => void
waitUntil
]) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sync: <void>(thunk: LazyArg<void>) => Effect.Effect<void, never, never>

Creates an Effect that represents a synchronous side-effectful computation.

Details

The provided function (thunk) must not throw errors; if it does, the error will be treated as a "defect".

This defect is not a standard error but indicates a flaw in the logic that was expected to be error-free. You can think of it similar to an unexpected crash in the program, which can be further managed or logged using tools like

catchAllDefect

.

When to Use

Use this function when you are sure the operation will not fail.

Example (Logging a Message)

import { Effect } from "effect"
const log = (message: string) =>
Effect.sync(() => {
console.log(message) // side effect
})
// ┌─── Effect<void, never, never>
// ▼
const program = log("Hello, World!")

@seetry_try for a version that can handle failures.

@since2.0.0

sync
(() =>
waitUntil: (promise: Promise<unknown>) => void
waitUntil
(
import Runtime
Runtime
.
const runPromise: <R, A, E>(runtime: Runtime.Runtime<R>, effect: Effect.Effect<A, E, R>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...> (+1 overload)

Runs the Effect, returning a JavaScript Promise that will be resolved with the value of the effect once the effect has been executed, or will be rejected with the first error or exception throw by the effect.

This method is effectful and should only be used at the edges of your program.

@since2.0.0

runPromise
(
runtime: Runtime.Runtime<R>
runtime
,
effect: Effect.Effect<A, E, R>
effect
, {
signal?: AbortSignal | undefined
signal
:
abortSignal: AbortSignal | undefined
abortSignal
})),
),
),
);
import {
const waitUntil: (promise: Promise<unknown>) => void | undefined

Extends the lifetime of the request handler for the lifetime of the given

Promise

@seehttps://developer.mozilla.org/en-US/docs/Web/API/ExtendableEvent/waitUntil

@parampromise The promise to wait for.

@example

import { waitUntil } from '@vercel/functions';
export function GET(request) {
waitUntil(fetch('https://vercel.com'));
return new Response('OK');
}

waitUntil
} from "@vercel/functions";
const
const VercelWaitUntil: Layer.Layer<WaitUntil, never, never>
VercelWaitUntil
=
import Layer
Layer
.
const succeed: <WaitUntil, (promise: Promise<unknown>) => void>(tag: Context.Tag<WaitUntil, (promise: Promise<unknown>) => void>, resource: (promise: Promise<unknown>) => void) => Layer.Layer<...> (+1 overload)

Constructs a layer from the specified value.

@since2.0.0

succeed
(
class WaitUntil
WaitUntil
,
const waitUntil: (promise: Promise<unknown>) => void | undefined

Extends the lifetime of the request handler for the lifetime of the given

Promise

@seehttps://developer.mozilla.org/en-US/docs/Web/API/ExtendableEvent/waitUntil

@parampromise The promise to wait for.

@example

import { waitUntil } from '@vercel/functions';
export function GET(request) {
waitUntil(fetch('https://vercel.com'));
return new Response('OK');
}

waitUntil
);
const
const CloudflareWaitUntil: (ctx: ExecutionContext) => Layer.Layer<WaitUntil, never, never>
CloudflareWaitUntil
= (
ctx: ExecutionContext
ctx
:
interface ExecutionContext
ExecutionContext
) =>
import Layer
Layer
.
const succeed: <WaitUntil, (promise: Promise<unknown>) => void>(tag: Context.Tag<WaitUntil, (promise: Promise<unknown>) => void>, resource: (promise: Promise<unknown>) => void) => Layer.Layer<...> (+1 overload)

Constructs a layer from the specified value.

@since2.0.0

succeed
(
class WaitUntil
WaitUntil
,
ctx: ExecutionContext
ctx
.
ExecutionContext.waitUntil(promise: Promise<any>): void
waitUntil
);