cyclejs/cycle-core

View on GitHub
docs/api/http.html

Summary

Maintainability
Test Coverage
<!doctype html>
<html>

<head>
  <meta charset='utf-8'>
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width">
  <title>Cycle.js - API reference (http)</title>

  <!-- Flatdoc -->
  <script src='../support/vendor/jquery.js'></script>
  <script src='../support/vendor/highlight.pack.js'></script>
  <script src='../legacy.js'></script>
  <script src='../flatdoc.js'></script>

  <!-- Algolia's DocSearch main theme -->
  <link href='//cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css' rel='stylesheet' />

  <!-- Others -->
  <script async src="//static.jsbin.com/js/embed.js"></script>

  <!-- Flatdoc theme -->
  <link href='../theme/style.css' rel='stylesheet'>
  <script src='../theme/script.js'></script>
  <link href='../support/vendor/highlight-github-gist.css' rel='stylesheet'>

  <!-- Meta -->
  <meta content="Cycle.js - API reference (http)" property="og:title">
  <meta content="A functional and reactive JavaScript framework for predictable code" name="description">

  <!-- Content -->
  <script id="markdown" type="text/markdown" src="index.html">
# Cycle HTTP - [source](https://github.com/cyclejs/cyclejs/tree/master/http)

A Driver for making HTTP requests, based on [superagent](https://github.com/visionmedia/superagent).

```
npm install @cycle/http
```

## Usage

Basics:

```js
import xs from 'xstream';
import {run} from '@cycle/run';
import {makeHTTPDriver} from '@cycle/http';

function main(sources) {
  // ...
}

const drivers = {
  HTTP: makeHTTPDriver()
}

run(main, drivers);
```

Simple and normal use case:

```js
function main(sources) {
  let request$ = xs.of({
    url: 'http://localhost:8080/hello', // GET method by default
    category: 'hello',
  });

  let response$ = sources.HTTP
    .select('hello')
    .flatten();

  let vdom$ = response$
    .map(res => res.text) // We expect this to be "Hello World"
    .startWith('Loading...')
    .map(text =>
      div('.container', [
        h1(text)
      ])
    );

  return {
    DOM: vdom$,
    HTTP: request$
  };
}
```

A thorough guide to the API inside `main`:

```js
function main(source) {
  // The HTTP Source has properties:
  // - select(category) or select()
  // - filter(predicate)
  // Notice $$: it means this is a metastream, in other words,
  // a stream of streams.
  let httpResponse$$ = source.HTTP.select();

  httpResponse$$.addListener({
    next: httpResponse$ => {
      // Notice that httpResponse$$ emits httpResponse$.

      // The response stream has a special field attached to it:
      // `request`, which is the same object we emit in the sink stream.
      // This is useful for filtering: you can find the
      // httpResponse$ corresponding to a certain request.
      console.log(httpResponse$.request);
    },
    error: () => {},
    complete: () => {},
  });

  let httpResponse$ = httpResponse$$.flatten(); // flattens the metastream
  // the reason why we need to flatten in this API is that you
  // should choose which concurrency strategy to use.
  // Normal xstream flatten() has limited concurrency of 1, meaning that
  // the previous request will be canceled once the next request to the
  // same resource occurs.
  // To have full concurrency (no cancelling), use flattenConcurrently()

  httpResponse$.addListener({
    next: httpResponse => {
      // httpResponse is the object we get as response from superagent.
      // Check the documentation in superagent to know the structure of
      // this object.
      console.log(httpResponse.status); // 200
    },
    error: (err) => {
      // This is a network error
      console.error(err);
    },
    complete: () => {},
  });

  // The request stream is an object with property `url` and value
  // `http://localhost:8080/ping` emitted periodically, every second.
  let request$ = xs.periodic(1000)
    .mapTo({ url: 'http://localhost:8080/ping', method: 'GET' });

  return {
    HTTP: request$ // HTTP driver expects the request$ as input
  };
}
```

## Error handling

You can handle errors using standard xstream or RxJS operators. The response stream is a stream of streams, i.e. each response will be its own stream so usually you want to catch errors for that single response stream:

```js
sources.HTTP
  .select('hello')
  .map((response$) =>
    response$.replaceError(() => xs.of(errorObject))
  ).flatten()
```
For more information, refer to the [xstream documentation for replaceError](https://github.com/staltz/xstream#replaceError) or the [RxJS documention for catch](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/catch.md).

## More information

For a more advanced usage, check the [Search example](https://github.com/cyclejs/cyclejs/tree/master/examples/http-search-github).

## Browser support

[![Sauce Test Status](https://saucelabs.com/browser-matrix/cyclejs-http.svg)](https://saucelabs.com/u/cyclejs-http)

IE 8 is not supported because this library depends on [superagent](https://github.com/visionmedia/superagent), which knowingly doesn't support IE 8.

# Isolation semantics

Cycle HTTP supports isolation between components using the `@cycle/isolate` package. Here is how isolation contexts work in Cycle HTTP given a `scope` to `isolate(Component, scope)`:

**When the scope is `null`: no isolation.**

The child component will have run in the same context as its parent, and methods like `HTTPSource.select()` will have access to response streams related to the parent. This means the child component is able to see responses that it did not itself produce.

**When the scope is a string: siblings isolation.**

A `HTTPSource.select()` call in a parent component will have access to HTTP responses from its children. However, a `HTTPSource.select()` inside a child component isolated with "siblings isolation" will have no access to HTTP responses in other children components isolated with "siblings isolation".

# API


## <a id="makeHTTPDriver"></a> `makeHTTPDriver()`

HTTP Driver factory.

This is a function which, when called, returns a HTTP Driver for Cycle.js
apps. The driver is also a function, and it takes a stream of requests as
input, and outputs an HTTP Source, an object with some functions to query for
response streams.

**Requests**. The stream of requests should emit either strings or objects.
If the stream emits strings, those should be the URL of the remote resource
over HTTP. If the stream emits objects, these should be instructions how
superagent should execute the request. These objects follow a structure
similar to superagent's request API itself. `request` object properties:

- `url` *(String)*: the remote resource path. **required**
- `method` *(String)*: HTTP Method for the request (GET, POST, PUT, etc).
- `category` *(String)*: an optional and arbitrary key that may be used in
the HTTP Source when querying for the response. E.g.
`sources.http.select(category)`
- `query` *(Object)*: an object with the payload for `GET` or `POST`.
- `send` *(Object|String)*: an object or string with the payload for `POST`.
- `headers` *(Object)*: object specifying HTTP headers.
- `accept` *(String)*: the Accept header.
- `type` *(String)*: a short-hand for setting Content-Type.
- `user` *(String)*: username for authentication.
- `password` *(String)*: password for authentication.
- `field` *(Object)*: object where key/values are Form fields.
- `progress` *(Boolean)*: whether or not to detect and emit progress events
on the response Observable.
- `attach` *(Array)*: array of objects, where each object specifies `name`,
`path`, and `filename` of a resource to upload.
- `withCredentials` *(Boolean)*: enables the ability to send cookies from the
origin.
- `agent` *(Object)*: an object specifying `cert` and `key` for SSL
certificate authentication.
- `redirects` *(Number)*: number of redirects to follow.
- `lazy` *(Boolean)*: whether or not this request runs lazily, which means
the request happens if and only if its corresponding response stream from the
HTTP Source is subscribed to. By default this value is false: requests run
eagerly, even if their response is ignored by the application.
- `responseType` *(String)*: setting for XHR responseType.
- `ok` *(Function): method to decide whether a response is an error or not.
 The callback to the ok function gets a response and returns true if the response should be interpreted as success.

**Responses**. A metastream is a stream that emits streams. The HTTP Source
manages response metastreams. These streams of responses have a `request`
field attached to them (to the stream object itself) indicating which request
(from the driver input) generated this response streams. The HTTP Source has
functions `filter()` and `select()`, but is not itself a stream. So you can
call `sources.HTTP.filter(request => request.url === X)` to get a new HTTP
Source object which is filtered for response streams that match the condition
given, and may call `sources.HTTP.select(category)` to get a metastream of
response that match the category key. With an HTTP Source, you can also call
`httpSource.select()` with no param to get the metastream. You should flatten
the metastream before consuming it, then the resulting response stream will
emit the response object received through superagent.

#### Returns:

*(Function)* the HTTP Driver function


  </script>

  <!-- Initializer -->
  <script>
    Flatdoc.run({
      fetcher: function (callback) {
        callback(null, document.getElementById('markdown').innerHTML);
      },
      highlight: function (code, value) {
        return hljs.highlight(value, code).value;
      },
    });
  </script>

</head>

<body role='flatdoc' class="no-literate">

  

  <div class='header'>
    <div class='left'>
      <h1><a href="/"><img class="logo" src="../img/cyclejs_logo.svg">Cycle.js</a></h1>
      <ul>
        <li><a href='../getting-started.html'>Guide</a></li>
        <li><a href='../api/index.html'>API</a></li>
        <li><a href='../releases.html'>Releases</a></li>
        <li><a href='https://github.com/cyclejs/cyclejs'>GitHub</a></li>
      </ul>
      <input id="docsearch" />
    </div>
    <div class='right'>
      <!-- GitHub buttons: see https://ghbtns.com -->
      <iframe src="https://ghbtns.com/github-btn.html?user=cyclejs&amp;repo=cyclejs&amp;type=watch&amp;count=true"
        allowtransparency="true" frameborder="0" scrolling="0" width="110" height="20"></iframe>
    </div>
  </div>

  <div class='content-root'>
    <div class='menubar'>
      <div class='menu section'>
        
        <div role='flatdoc-menu'></div>
        
      </div>
    </div>
    <div role='flatdoc-content' class='content'></div>
    
  </div>
  

  <script>
    ((window.gitter = {}).chat = {}).options = {
      room: 'cyclejs/cyclejs'
    };
  </script>
  <script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer></script>
  <script src='//cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js'></script>
  <script src='../docsearch.js'></script>
</body>

</html>