Case Study: An HTTP server - deanrad/rx-helper GitHub Wiki

How would you use Rx-Helper to build a server?

It may be sufficient to encode an HTTP request like /api/users?q=bob as:

{ 
  type: 'http/get',
  payload: {
     path: '/api/users',
     query: { q: 'bob' }
  }
} // FSA representation of /api/users?q=bob

But you'd quickly run into trouble. When rendering that response, how does the server know which response object to call res.write on? How do we give the correct, live response object to the Handler that is receiving the event object?

The answer lies in the context parameter you can give when processing or trigger-ing an event:

A vanilla express application would look like:

app.get("*", function(request, response) {
  const { path, query } = request
  response.json({ path, query })
})

In other case studies with Rx-Helper, we saw code that creates an FSA, populates it, and hands it off to agent.process, like this:

agent.process(event)

But now, let's include a little more context, as we give the agent its item to process:

app.get("*", function(request, response) {
  const type = "http/" + req.method.toLowerCase()
  const event = { type, payload }

  // Provide the response object in the 2nd parameter 'context'
  agent.process(event, { response })
})

As before, we populate a Flux Standard Action object with whatever fields we care about from the request. Then we pass it, and an object with a single field "response", containing the Express response. Finally, in a renderer, which you'll recall is a function responsible for fulfilling an FSA, we'll be able to ask for the event AND the context:

agent.on("http/get", ({ event, context }) => {
  const { query, path } = event.payload

  // Get our response object from the context
  const { response } = context
  if (path.includes("api")) {
    response.json({ path, query })
  }
})

Decoupling - Why?

You may wonder what this buys us, this separation of the request definition and the response fulfillment.

The main benefit comes from the fact that now there is a single object, the event stream, representing ALL requests that come into the website. You can now use all the querying tools that RxJS provides for Observables in order to do things like rate-limiting that weren't feasible to do with conventional express code. Questions that involve the correlation between requests like these become easier to answer:

  • What requests were also being processed during this one?
  • Can we rate-limit by country?

Demo

Clone the rx-helper repo, and its main startup script will run the server in the file demos/express

git clone https://github.com/deanius/rx-helper
cd rx-helper && npm install && npm start

http://localhost:3120