Effection Logo
@effectionx/fetchv0.1.0thefrontside/effectionx
NPM Badge with published version

fetch

Effection-native fetch with structured concurrency and streaming response support.

Installation

npm install @effectionx/fetch effection

Usage

import { main } from "effection";
import { fetch } from "@effectionx/fetch";

await main(function* () {
  let users = yield* fetch("https://api.example.com/users").json();
  console.log(users);
});

Fluent API

Chain methods directly on fetch() for concise one-liners:

// JSON
let data = yield* fetch("https://api.example.com/users").json();

// Text
let html = yield* fetch("https://example.com").text();

// With validation - throws HttpError on non-2xx
let data = yield* fetch("https://api.example.com/users").expect().json();

Traditional API

You can also get the response first, then consume the body:

let response = yield* fetch("https://api.example.com/users");
let data = yield* response.json();

Streaming response bodies

import { each } from "effection";
import { fetch } from "@effectionx/fetch";

function* example() {
  for (let chunk of yield* each(fetch("https://example.com/large-file.bin").body())) {
    console.log(chunk.length);
    yield* each.next();
  }
}

Concurrent requests

import { all } from "effection";
import { fetch } from "@effectionx/fetch";

function* fetchMultiple() {
  let [users, posts, comments] = yield* all([
    fetch("https://api.example.com/users").json(),
    fetch("https://api.example.com/posts").json(),
    fetch("https://api.example.com/comments").json(),
  ]);

  return { users, posts, comments };
}

Validate JSON while parsing

import { fetch } from "@effectionx/fetch";

interface User {
  id: string;
  name: string;
}

function parseUser(value: unknown): User {
  if (
    typeof value === "object" &&
    value !== null &&
    "id" in value &&
    "name" in value
  ) {
    return value as User;
  }

  throw new Error("invalid user payload");
}

function* getUser() {
  return yield* fetch("https://api.example.com/user").json(parseUser);
}

Handle non-2xx responses

import { HttpError, fetch } from "@effectionx/fetch";

function* getUser(id: string) {
  try {
    return yield* fetch(`https://api.example.com/users/${id}`).expect().json();
  } catch (error) {
    if (error instanceof HttpError) {
      console.error(error.status, error.statusText);
    }
    throw error;
  }
}

API

fetch(input, init?)

Returns a FetchOperation that supports both fluent chaining and traditional usage.

  • input - URL string, URL object, or Request object
  • init - Optional FetchInit options (same as RequestInit but without signal)

Cancellation is handled automatically via Effection's structured concurrency. When the scope exits, the request is aborted. The signal option is intentionally omitted since Effection manages cancellation for you.

FetchOperation

Chainable fetch operation returned by fetch().

  • json<T>(), json<T>(parse) - parse response as JSON
  • text() - get response as text
  • arrayBuffer() - get response as ArrayBuffer
  • blob() - get response as Blob
  • formData() - get response as FormData
  • body() - stream response body as Stream<Uint8Array, void>
  • expect() - returns a new FetchOperation that throws HttpError on non-2xx

Can also be yielded directly to get a FetchResponse:

let response = yield* fetch("https://api.example.com/users");

FetchResponse

Effection wrapper around native Response with operation-based body readers.

  • json<T>(), json<T>(parse)
  • text()
  • arrayBuffer()
  • blob()
  • formData()
  • body(): Stream<Uint8Array, void>
  • expect() - throws HttpError for non-2xx responses
  • raw - access the underlying native Response

API Reference

type FetchInit = Omit<RequestInit, "signal"> & {}

Request options for fetch, excluding signal since cancellation is handled automatically via Effection's structured concurrency.

interface FetchOperation extends Operation<FetchResponse>

Chainable fetch operation that supports fluent API.

Can be yielded directly to get a FetchResponse, or chained with methods like .json(), .text(), .body() for direct consumption.

Examples

Example 1
// Fluent API - single yield*
let data = yield* fetch("/api/users").json();

// With validation - throws HttpError on non-2xx
let data = yield* fetch("/api/users").expect().json();

// Traditional API - get response first
let response = yield* fetch("/api/users");
let data = yield* response.json();

Methods

json
<T = unknown>(): Operation<T>

Parse response body as JSON.

json
<T>(parse: (value: unknown) => T): Operation<T>

Parse response body as JSON with a custom parser.

text
(): Operation<string>

Get response body as text.

arrayBuffer
(): Operation<ArrayBuffer>

Get response body as ArrayBuffer.

blob
(): Operation<Blob>

Get response body as Blob.

formData
(): Operation<FormData>

Get response body as FormData.

body
(): Stream<Uint8Array, void>

Stream response body as chunks.

expect
(): FetchOperation

Return a new FetchOperation that throws HttpError on non-2xx responses.

interface FetchResponse

Effection wrapper around the native Response object.

Provides operation-based methods for consuming the response body, and exposes common response properties.

Properties

rawreadonly: Response

The underlying native Response object.

bodyUsedreadonly: boolean

Whether the response body has been consumed.

okreadonly: boolean

Whether the response status is in the 200-299 range.

statusreadonly: number

The HTTP status code.

statusTextreadonly: string

The HTTP status message.

headersreadonly: Headers

The response headers.

urlreadonly: string

The final URL after redirects.

redirectedreadonly: boolean

Whether the response was redirected.

typereadonly: ResponseType

The response type (e.g., "basic", "cors").

Methods

json
<T = unknown>(): Operation<T>

Parse response body as JSON.

json
<T>(parse: (value: unknown) => T): Operation<T>

Parse response body as JSON with a custom parser.

text
(): Operation<string>

Get response body as text.

arrayBuffer
(): Operation<ArrayBuffer>

Get response body as ArrayBuffer.

blob
(): Operation<Blob>

Get response body as Blob.

formData
(): Operation<FormData>

Get response body as FormData.

body
(): Stream<Uint8Array, void>

Stream response body as chunks.

expect
(): Operation

Throw HttpError if response is not ok (non-2xx status).

class HttpError extends Error implements

Error thrown when an HTTP response has a non-2xx status code.

Thrown by FetchOperation and FetchResponse when the response is not ok.

Constructors

new HttpError(status: number, statusText: string, url: string, response: FetchResponse)

function fetch(input: RequestInfo | URL, init?: FetchInit): FetchOperation

Perform an HTTP request using the Fetch API with Effection structured concurrency.

Cancellation is automatically handled via the current Effection scope. When the scope exits, the request is aborted.

Examples

Example 1
// Simple GET request
let data = yield* fetch("https://api.example.com/users").json();

// POST request with body
let result = yield* fetch("https://api.example.com/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Alice" }),
}).expect().json();

// Stream response body
for (let chunk of yield* each(fetch("/large-file").body())) {
  console.log(chunk.length);
  yield* each.next();
}

Parameters

input: RequestInfo | URL

  • The URL or Request object

initoptional: FetchInit

  • Optional request configuration (same as RequestInit, but without signal)

Return Type

FetchOperation

A chainable FetchOperation