# feathers-reactive

> Reactive API extensions for Feathers services

Latest version **0.11.0** (published 2023-07-10) · MIT license · 0 weekly downloads

## Install

```sh
npm install feathers-reactive
pnpm add feathers-reactive
yarn add feathers-reactive
bun add feathers-reactive
```

## Health

**Score 40/100 (D)** — status: abandoned.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads; pre 1.0.

Negative: abandoned.

## Facts

| | |
|---|---|
| Version | 0.11.0 |
| Published | 2023-07-10 |
| First published | 2016-05-24 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >= 16 |
| Dependencies | 7 |
| Unpacked size | 83.6 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 215 |
| Author | Feathers contributors |
| Maintainers | daffl, ekryski |
| Keywords | feathers, feathers-plugin, rxjs, reactive |

## Links

- npm: https://www.npmjs.com/package/feathers-reactive
- Repository: https://github.com/feathersjs-ecosystem/feathers-reactive
- Issues: https://github.com/feathersjs-ecosystem/feathers-reactive/issues
- npm.io page: https://npm.io/package/feathers-reactive

## Dependencies (7)

- [rxjs](https://npm.io/package/rxjs.md) ^7.8.1
- [sift](https://npm.io/package/sift.md) ^17.0.1
- [debug](https://npm.io/package/debug.md) ^4.3.4
- [@feathersjs/commons](https://npm.io/package/@feathersjs/commons.md) ^5.0.6
- [@feathersjs/feathers](https://npm.io/package/@feathersjs/feathers.md) ^5.0.6
- [json-stable-stringify](https://npm.io/package/json-stable-stringify.md) ^1.0.2
- [@feathersjs/adapter-commons](https://npm.io/package/@feathersjs/adapter-commons.md) ^5.0.6

## Alternatives

- [@fortawesome/react-fontawesome](https://npm.io/package/@fortawesome/react-fontawesome.md) — 2.2M weekly downloads
- [roboto-fontface](https://npm.io/package/roboto-fontface.md) — 196.0K weekly downloads
- [@react-native-vector-icons/common](https://npm.io/package/@react-native-vector-icons/common.md) — 150.4K weekly downloads
- [@procore/core-icons](https://npm.io/package/@procore/core-icons.md) — 4.6K weekly downloads
- [@react-md/material-icons](https://npm.io/package/@react-md/material-icons.md) — 1.6K weekly downloads

## Recent versions

- 0.11.0 (latest) — 2023-07-10
- 0.10.0 — 2022-07-05
- 0.9.0 — 2022-03-25
- 0.8.2 — 2020-04-29
- 0.8.1 — 2019-06-19
- 0.8.0 — 2019-06-13
- 0.7.2 — 2018-07-24
- 0.7.1 — 2018-06-23
- 0.7.0 — 2018-05-28
- 0.6.0 — 2018-02-15
- 0.5.4 — 2017-10-27
- 0.5.3 — 2017-09-25
- 0.5.2 — 2017-09-06
- 0.5.1 — 2017-08-30
- 0.5.0 — 2017-08-28
- … 6 more at https://npm.io/package/feathers-reactive/versions

## README

# feathers-reactive

[![CI](https://github.com/feathersjs-ecosystem/feathers-reactive/actions/workflows/nodejs.yml/badge.svg)](https://github.com/feathersjs-ecosystem/feathers-reactive/actions/workflows/nodejs.yml)
[![Download Status](https://img.shields.io/npm/dm/feathers-reactive.svg?style=flat-square)](https://www.npmjs.com/package/feathers-reactive)

> Reactive API extensions for Feathers

## About

`feathers-reactive` adds a `watch()` method to services. The returned object implements all service methods as [RxJS v7](https://github.com/ReactiveX/rxjs) observables that automatically update on [real-time events](https://docs.feathersjs.com/api/events.html#service-events).

## Options

The following options are supported:

- `idField` (mandatory): The id property field of your services. Depends on your service/database. Usually 'id' (SQL, Rethinkdb, …) or '_id' (MongoDB, NeDB, … ).
- `dataField` (default: `data`): The data property field in paginated responses
- `listStrategy` (default: `smart`): The strategy to use for streaming the data. Can be `smart`, `always` or `never`. __Avoid using `always` whenever possible__.
- `sorter` (`function(query, options) {}`): A function that returns a sorting function for the given query and option including pagination and limiting. Does not need to be customized unless there is a sorting mechanism other than Feathers standard in place.
- `matcher` (`function(query)`): A function that returns a function which returns whether an item matches the original query or not.
- `pipe` (`operator | operator[]`) One or multiple rxjs operators of the form `function(observable) => observable` like you would pass them to an Observable's [.pipe method](https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md). The supplied operators are applied to any Observable created by `feathers-reactive`. `options.pipe: tap(data => console.log(data))` would log every emitted value to the console. 

#### Application level

```js
import feathers from '@feathersjs/feathers';
import { rx } from 'feathers-reactive';

const app = feathers().configure(rx(options));
```

#### Service level

With `feathers-reactive` configured on the application individual options can be set at the service level with `service.rx`:

```js
// Set a different id field
app.service('todos').rx({
  idField: '_id'
});
```

#### Method call level

Each method call can also pass its own options via `params.rx`:

```js
// Never update data for this method call
app.service('todos').watch({ listStrategy: 'never' }).find();
```

### List strategies

List strategies are used to determine how a data stream behaves. Currently there are three strategies:

- `never` - Returns a stream from the service promise that only emits the method call data and never updates after that
- `smart` (default) - Returns a stream that smartly emits updated list data based on the services real-time events. It does not re-query any new data (but does not cover some cases in which the `always` strategy can be used). When using smart list strategy, an additional method reset is available to get fresh data from the server.
- `always` - Re-runs the original query to always get fresh data from the server on any matching real-time event. __Avoid this list strategy if possible__ since it will put a higher load on the server than necessary.

## Usage

```js
import {feathers} from '@feathersjs/feathers';
import memory from 'feathers-memory';
import { rx } from 'feathers-reactive';

const app = feathers()
  .configure(rx({
    idField: 'id'
  }))
  .use('/messages', memory());

const messages = app.service('messages');

messages.create({
  text: 'A test message'
}).then(() => {
  // Get a specific message with id 0. Emit the message data once it resolves
  // and every time it changes e.g. through an updated or patched event
  messages.watch().get(0).subscribe(message => console.log('My message', message));

  // Find all messages and emit a new list every time anything changes
  messages.watch().find().subscribe(messages => console.log('Message list', messages));

  setTimeout(() => {
    messages.create({ text: 'Another message' }).then(() =>
      setTimeout(() => messages.patch(0, { text: 'Updated message' }), 1000)
    );
  }, 1000);
});
```

Will output:

```console
My message { text: 'A test message', id: 0 }
Message list [ { text: 'A test message', id: 0 } ]
Message list [ { text: 'A test message', id: 0 },
  { text: 'Another message', id: 1 } ]
My message { text: 'Updated message', id: 0 }
Message list [ { text: 'Updated message', id: 0 },
  { text: 'Another message', id: 1 } ]
```

## Frameworks

Let's assume a simple Feathers Socket.io server in `app.js` like this:

> npm install @feathersjs/feathers @feathersjs/socketio feathers-memory

```js
import {feathers} from '@feathersjs/feathers';
import socketio from '@feathersjs/socketio';
import memory from 'feathers-memory';

const app = feathers()
  .configure(socketio())
  .use('/todos', memory());

app.on('connection', connection => app.channel('everybody').join(connection));
app.publish(() => app.channel('everybody'));

app.listen(3030).on('listening', () =>
  console.log('Feathers Socket.io server running on localhost:3030')
);
```

### Usage

For an ES5 compatible version on the client (e.g. when using `create-react-app`) you can import `feathers-reactive/dist/feathers-reactive`. In `client.js`:

```js
import io from 'socket.io-client';
import feathers from '@feathersjs/client';
import rx from 'feathers-reactive/dist/feathers-reactive';

const socket = io('http://localhost:3030');
const app = feathers()
  .configure(feathers.socketio(socket))
  .configure(rx({
    idField: 'id'
  }));

export default app;
```

### React

A real-time ReactJS Todo application (with Bootstrap styles) can look like this (see the [examples/react-todos](./examples/react-todos) folder for a working example);

```js
import React, { Component } from 'react';
import client from './client';

class App extends Component {
  constructor (props) {
    super(props);
    this.state = {
      todos: [],
      text: ''
    };
  }

  componentDidMount () {
    this.todos = client.service('todos').watch()
      .find().subscribe(todos => this.setState(todos));
  }

  componentWillUnmount () {
    this.todos.unsubscribe();
  }

  updateText (ev) {
    this.setState({ text: ev.target.value });
  }

  createTodo (ev) {
    client.service('todos').create({
      text: this.state.text,
      complete: false
    });
    this.setState({ text: '' });
    ev.preventDefault();
  }

  updateTodo (todo, ev) {
    todo.complete = ev.target.checked;
    client.service('todos').patch(todo.id, todo);
  }

  deleteTodo (todo) {
    client.service('todos').remove(todo.id);
  }

  render () {
    const renderTodo = todo =>
      <li key={todo.id} className={`page-header checkbox ${todo.complete ? 'done' : ''}`}>
        <label>
          <input type='checkbox' onChange={this.updateTodo.bind(this, todo)}
            checked={todo.complete} />
          {todo.text}
        </label>
        <a href='javascript://' className='pull-right delete'
          onClick={this.deleteTodo.bind(this, todo)}>
          <span className='glyphicon glyphicon-remove' />
        </a>
      </li>;

    return <div className='container' id='todos'>
      <h1>Feathers real-time Todos</h1>

      <ul className='todos list-unstyled'>{this.state.todos.map(renderTodo)}</ul>
      <form role='form' className='create-todo' onSubmit={this.createTodo.bind(this)}>
        <div className='form-group'>
          <input type='text' className='form-control' name='description'
            placeholder='Add a new Todo' onChange={this.updateText.bind(this)}
            value={this.state.text} />
        </div>
        <button type='submit' className='btn btn-info col-md-12'>
          Add Todo
        </button>
      </form>
    </div>;
  }
}

export default App;
```

## License

Copyright (c) 2023

Licensed under the [MIT license](LICENSE).

---
_Source: https://npm.io/package/feathers-reactive · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
