Effect HTTP

0.87.0 · active · verified Wed Apr 22

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

Warnings

Install

Imports

Quickstart

This quickstart defines a simple REST API with GET and POST endpoints, implements them using `effect-http/Router`, starts a Node.js server with `effect-http-node`, and then uses `effect-http/Client` to interact with the running API programmatically.

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);

view raw JSON →