6

Is it possible to create an ReactiveX Observable from:

app = express();
app.post('/path', function() {...})

?

I mean, there exists a way to create an observable fromEvent in which I already used for lots of events that were registered using object.on('eventName', function(){}) but the post from express is not exactly like that.

2 Answers2

11

Rx-ify express all the way?

The added complexity of trying to convert the callback-style implementation of routing in express towards Rx makes me conclude that this is not the way forward. This is because express does not know Rx thus has no notion of subscription lifetime and multiple event emissions.

The answer of martin is a proof-of-concept in which you can see the above issues clearly. It would mean that for every route you would need to create a separate Subject or share() one subject and then make a lot of filters for every route to separate implementations. Which is redundant because express has already routed your request to the correct handler.

Combine Rx world with express callbacks

If you want to have the benefits of Expressjs routes combined with RxJs I would separate the routing logic and keep that in express callbacks and the actual implementation use that as Rx combined with a .toPromise() to activate it:

app.post('/user/:id', (req, res) => {
  return getUserById(req.params.id)
    .toPromise()/* return the Rx stream as promise to express so it traces its lifecycle */
    .then(
      user => res.send(user),
      err => res.status(500).send(err.message)
    );
});    

function getUserById(id) {
  // stub implementation
  return Rx.Observable.of({ id, name: 'username' }) 
    .delay(100);
}
Mark van Straten
  • 9,287
  • 3
  • 38
  • 57
9

I think the easiest way is using a Subject to reemit events from express.

var express = require('express');
var Rx = require('rxjs');
var app = express();

let subject = new Rx.Subject();

app.get('/', (req, res) => subject.next([req, res]));

subject
  .do(a => console.log('123345'))
  .subscribe(args => {
    let [req, res] = args;
    res.send('Hello World!');
  });

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
});

The main difference is that you need to wrap [req, res] as an array because you want to pass both to subject.next(...). This can be later unpacked with let [req, res] = args;.

Unfortunately, you can't use Observable.bindCallback because it takes just a single response from the wrapped function and then completes which is now what you want in this scenario.

martin
  • 93,354
  • 25
  • 191
  • 226