Effection Logo
@effectionx/fetchv0.2.1thefrontside/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, an in-flight request is aborted. A native fetch that has already settled is left intact. 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

interface Fetch

Methods

fetch
(input: RequestInfo | URL, init: FetchInit | undefined, shouldExpect: boolean): Operation<FetchResponse>

No documentation available.

const FetchApi: Api<Fetch>

Type

Api<Fetch>

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

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, an in-flight 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