Raw HTTP APIs
import { import HttpApp
HttpApp, import HttpServerRequest
HttpServerRequest, import HttpServerResponse
HttpServerResponse,} from "@effect/platform";import { import Effect
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.
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.
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 responseconst const exampleEffectHandler: Effect.Effect<HttpServerResponse.HttpServerResponse, RequestError | ParseError | HttpBodyError, HttpServerRequest.HttpServerRequest>
exampleEffectHandler = import Effect
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}`})
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<...>
schemaBodyJson( import Schema
Schema.function Struct<{ name: typeof Schema.String;}>(fields: { name: typeof Schema.String;}): Schema.Struct<{ name: typeof Schema.String;}> (+1 overload)
Struct({ name: typeof Schema.String
name: import Schema
Schema.class Stringexport String
String, }), ); return yield* import HttpServerResponse
HttpServerResponse.const json: (body: unknown, options?: HttpServerResponse.Options.WithContentType | undefined) => Effect.Effect<HttpServerResponse.HttpServerResponse, HttpBodyError>
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>
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
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
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<...>
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 HttpApiEndpoint
s. You can use an HttpApiGroup
to
represent a portion of your domain.
The endpoints can be implemented later using the HttpApiBuilder.group
api.
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)
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)
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".
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 Stringexport String
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)
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)
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".
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)
Struct({ name: typeof Schema.String
name: import Schema
Schema.class Stringexport String
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)
Struct({ ok: typeof Schema.Boolean
ok: import Schema
Schema.class Booleanexport Boolean
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 HttpApiEndpoint
s. You can use an HttpApi
to
represent a portion of your domain.
The endpoints can be implemented later using the HttpApiBuilder.make
api.
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>
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.
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
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)
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
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}`})
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.
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.
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.
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.
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.
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>
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
.
middleware(import HttpMiddleware
HttpMiddleware.const logger: <E, R>(httpApp: Default<E, R>) => Default<E, R>
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.
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.
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.
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.
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.
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.
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
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}`})
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<...>
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.
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
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 Layer
s, 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"// []
provide(import FetchHttpClient
FetchHttpClient.const layer: Layer.Layer<HttpClient, never, never>
layer));
waitUntil
import { import Effect
Effect, import Runtime
Runtime, import Context
Context, import Layer
Layer } from "effect";
class class WaitUntil
WaitUntil extends import Context
Context.const Tag: <"WaitUntil">(id: "WaitUntil") => <Self, Shape>() => Context.TagClass<Self, "WaitUntil", Shape>
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
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.
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
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.
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
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 optionconst 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' ]
zip(class WaitUntil
WaitUntil), import Effect
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))// orconst flatMappedEffect = Effect.flatMap(myEffect, transformation)// orconst 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 amountconst 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 databaseconst 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
flatMap(([runtime: Runtime.Runtime<R>
runtime, waitUntil: (promise: Promise<unknown>) => void
waitUntil]) => import Effect
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!")
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.
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
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.
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
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.
succeed(class WaitUntil
WaitUntil, ctx: ExecutionContext
ctx.ExecutionContext.waitUntil(promise: Promise<any>): void
waitUntil);