Effect HTTP
effect-http is a high-level, declarative, and type-safe HTTP API layer built specifically for the `effect-ts` ecosystem. It leverages `Effect`'s powerful functional programming primitives, including Effects, Layers, and Schemas, to provide a robust framework for defining both HTTP servers and clients. Currently at version 0.87.0, the library maintains a rapid release cadence, often issuing minor version updates to align with ongoing developments and breaking changes within its core peer dependency, `effect`. This close coupling ensures full compatibility and leverages the latest features of `effect-ts`, but also means users should expect frequent dependency updates. Its primary differentiation lies in its deep integration with the `Effect` paradigm, offering end-to-end type safety from API definition to implementation and client consumption, while remaining platform-agnostic at its core (`effect-http` package) with specific adapters like `effect-http-node` for server execution. It promotes a functional and declarative style for building robust, concurrent, and error-handled web services.
Common errors
-
Error: Operation 'myOperation' not found or has no handler attached.
cause You have defined an operation in your `Api` but have not provided an implementation for it using `Router.handle('myOperation', ...)`.fixEnsure every operation defined in `Api.api()` has a corresponding handler attached to the `Router.make(api)` instance using `Router.handle('operationName', handlerFunction)`. -
TypeError: Cannot read properties of undefined (reading 'pipe')
cause This often indicates that you're trying to call a method like `pipe` on `undefined` because a dependency was not provided or an `Effect` did not resolve as expected, commonly seen when `Layer` composition is incorrect.fixVerify that all necessary dependencies (Layers) are provided to your `Effect` program. Use `Effect.provide()` or `Layer.merge()`/`Layer.provide()` correctly to ensure services are available in the desired scope. Inspect the specific line number for the `undefined` value. -
Error: Decoding error: (Schema validation details)
cause This error occurs when an incoming request (e.g., body, params, query) or an outgoing response does not conform to the `Schema` defined in your `Api` for that specific operation.fixReview the `Schema` definition for the affected operation in your `Api`. Ensure that the data being sent or received by the client/server matches the structure and types specified by the `Schema`.
Warnings
- breaking effect-http frequently updates its peer dependencies on `effect` and `@effect/platform`. Minor version bumps in effect-http often correspond to minor or patch updates in `effect` itself, which can include breaking changes in the underlying `effect-ts` library. Always review the `effect-ts` changelog when updating `effect-http`.
- gotcha The `effect-http` package itself is platform-agnostic, primarily providing the API definition and routing logic. To run an HTTP server, you must install and use a platform-specific adapter like `effect-http-node` for Node.js environments or `effect-http-express` for Express.js integration.
- breaking The `effect` library, upon which `effect-http` is built, underwent significant breaking changes in its transition to version 3. If you are migrating from an `effect` v2 (or earlier) project, expect extensive code refactoring, especially around `Layer` composition, `Effect` constructors, and `Schema` usage.
Install
-
npm install effect-http -
yarn add effect-http -
pnpm add effect-http
Imports
- Api
import { Api } from 'effect-http';import * as Api from 'effect-http/Api';
- Router
import { Router } from 'effect-http';import * as Router from 'effect-http/Router';
- Client
import { Client } from 'effect-http';import * as Client from 'effect-http/Client';
Quickstart
import * as Effect from 'effect';
import * as Schema from '@effect/schema/Schema';
import * as Http from '@effect/platform/Http';
import * as NodeHttpServer from '@effect/platform-node/HttpServer';
import * as NodeHttpClient from '@effect/platform-node/HttpClient';
import * as Api from 'effect-http/Api';
import * as Router from 'effect-http/Router';
import * as Client from 'effect-http/Client';
// 1. Define your API schema
const MyApi = Api.api().pipe(
Api.get('getUser', '/users/:id', {
request: { params: Schema.struct({ id: Schema.NumberFromString }) },
response: Schema.struct({ id: Schema.number, name: Schema.string }),
}),
Api.post('createUser', '/users', {
request: { body: Schema.struct({ name: Schema.string }) },
response: Schema.struct({ id: Schema.number, name: Schema.string }),
}),
);
// 2. Implement the handlers
const app = Router.make(MyApi).pipe(
Router.handle('getUser', ({ params }) =>
Effect.succeed({
id: params.id,
name: `User ${params.id}`,
}),
),
Router.handle('createUser', ({ body }) =>
Effect.succeed({
id: Math.floor(Math.random() * 1000) + 1,
name: body.name,
}),
),
);
// 3. Set up the Node.js server
const server = NodeHttpServer.server.pipe(
Effect.tap((s) => Http.server.serve(app, s)),
Effect.scoped,
Effect.provide(NodeHttpServer.layer(() => Http.listeningOn({ port: 3000 }))),
);
// 4. Create a client for the API
const myClient = Client.make(MyApi);
const httpClient = Http.client.fetch.pipe(NodeHttpClient.layer); // Use Node's fetch
// 5. Run the server and make a client request
const program = Effect.gen(function* () {
yield* Effect.fork(server);
yield* Effect.sleep('100ms'); // Give server time to start
const getUserResult = yield* myClient.getUser({ params: { id: 1 } });
console.log('GET /users/1 result:', getUserResult);
const createUserResult = yield* myClient.createUser({ body: { name: 'Alice' } });
console.log('POST /users result:', createUserResult);
yield* Effect.log('Server and client interaction complete. Stopping server.');
}).pipe(Effect.provide(httpClient));
Effect.runPromise(program);