npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

the-conductor

v0.3.0

Published

An API Orchestration Layer (OL)

Downloads

20

Readme

NPM version Build Status Dependency Status

logo of the conductor

Why?

Jump into the future of API design and add an orchestration layer to your APIs.

In order to understand what an orchestration layer does, take a look at the following image:

api comparison

In simple words: an API Orchestration Layer (OL) is responsible for assembling different API responses (ie. /product/123 and /product/123/reviews) at a single endpoint (ie. /view/web/product/123), so that a service can make one single call to that endpoint and retrieve all the informations needed to render its views with a single API call.

The advantage of this approach is that:

  • you can spread the workload over multiple, concurrent requests
  • you can have very clean, separate, decoupled, resource-oriented APIs on the background

If your applications uses different APIs to build user interfaces, you might want to invest on your own orchestra! And in order for an orchestra to be succesfull, it needs a very good conductor.

Not a simple conductor, the conductor.

Installation

You can simply include it via npm install the-conductor

This package follow semantic versioning, so if you want to always ensure backwards compatibility just use:

"the-conductor": "~0.1"

How it works

There are 2 main concepts that you will have to understand before using the conductor:

  • resources: these are objects representing your resource-oriented APIs, that the conductor will call in order to assemble its responses
  • facade: the endpoint of the conductor, a facade allows you to define which resources should be aggregated there (and a few other, minor, things, like how the conductor should assemble the responses from the sub-requests it makes)

Try it live!

After you cloned the repository, run npm install and then run the example server:

node example/index.js

then hit http://127.0.0.1:6971/example.json and see it in action: you will see some JSON which is the result of a bunch of HTTP calls:

example calls

Have a look at the example folder in the source code.

Usage

Using the conductor it's a matter of a few lines of code:

var conductor   = require('the-conductor');
var http        = require('http');

http.createServer(conductor.run()).listen(conductor.getPort());
console.log("server started on port " + conductor.getPort())

and then you can simply run node yourFile.js: by default, the conductor will run on port 6971, but you can override this setting by just specifying the port during the startup process (for example -p 8080).

Configuration

Of course, the above example won't be very helpful since we are not telling the conductor on which routes it should listen to, nor which facades to run for those routes; to do that, we simply have to 1. add some routes and 2. add facades and configure them.

In order for our conductor to respond to a request hitting http://example.org/web/view/users/john, for example, we simply need to add a new route (the router used by the conductor is odino.router):

conductor.router.load({
    example: {
        pattern: "/example.json",
        facade: "f1"
    }
});

In this way we are telling the conductor that for whichever request that hits /web/view/users/:username it will need to run the user_detail facade. But how do we define what the facade does?

We can simply populate the conductor.config object with these informations:

conductor.config.resources = {
    user_details: {
        url: "http://api.example.org/users/:username"
    },
    user_friends: {
        url: "http://api.example.org/users/:username/friends"
    }
};

conductor.config.facades = {
    'web_user_details': {
        resources: [
            'user_details',
            'user_friends'
        ]
    }
};

or, alternatively (recommended), dump the configuration in a more readable YML file and load it:

conductor.loadConfigFile('./path/to/your/config.yml');

The YML would then look very simple:

resources:
 user_detail:
    url:  "http://api.example.org/users/:username"
  user_friends:
    url:  "http://api.example.org/users/:username/friends"
facades:
  web_user_details:
    resources:
      - user_detail
      - user_friends

At this point everything should be a bit clearer:

  • whenever someone hits http://example.org/web/view/users/:username the conductor will match the route web_user_details
  • at that point, it will see that it needs to run the facade user_detail
  • the conductor will then look into its config, finding out that it needs to hit http://api.example.org/users/:username and http://api.example.org/users/:username/friends, assembles the responses and serve them to the client

Simple as that!

Facade strategies

A facade strategy is what the conductor runs to assemble the sub-responses it obtains from a facade's resources.

The conductor comes bundled with some very simple strategies:

  • join, which simply joins all the response bodies altogether
  • merge, which merges the responses (to be used with JSON responses)

In order to specify which facade strategy to use for a particular facade, simply specify it in your config:

facades:
  web_user_details:
    strategy: join
    resources:
      - user_detail
      - user_friends

The join strategy can be omitted, as it's the default one.

Custom facade strategies

You can also register and use your own facade strategies by simply specifying them in your configuration and register them inside the conductor.

First, update your configuration:

facades:
  web_user_details:
    strategy: custom
    resources: ...

then register the strategy in the conductor:

conductor.addFacadeStrategy('custom', function(response, resources, defaultHeaders){
    response.writeHead(200, defaultHeaders);
    response.write(resources[0].response.body);
    response.end();
});

In this example, the custom facade strategy only returns the body of the first sub-response.

Debugging help

If you start the conductor in dev environment (--env dev or -e dev) you will get some additional informations everytime you hit an endpoint, such as a list of resources and the body of each resource the conductor hit in order to serve the response:

~  ᐅ curl -X GET -I http://127.0.0.1:6971/example.json

HTTP/1.1 200 OK
Content-Type: text/plain
The-Conductor-Resource-ip.jsontest.com/: {"ip": "91.72.216.244"}
The-Conductor-Resource-echo.jsontest.com/key/value/one/two: {"one": "two","key": "value"}
The-Conductor-Resource-cookie.jsontest.com/: {"cookie_status": "Cookie set with name jsontestdotcom"}
The-Conductor-Resource-headers.jsontest.com/: {"Host": "headers.jsontest.com","User-Agent": ""}
The-Conductor-Resource-md5.jsontest.com/?text=sometext: {"md5": "a29e90948f4eee52168fab5fa9cfbcf8","original": "sometext"}
The-Conductor-Resource-date.jsontest.com/: {"time": "03:16:35 PM","milliseconds_since_epoch": 1388848595956,"date": "01-04-2014"}
The-Conductor-Resources: ip.jsontest.com/, echo.jsontest.com/key/value/one/two, cookie.jsontest.com/, headers.jsontest.com/, md5.jsontest.com/?text=sometext, date.jsontest.com/
The-Conductor-Runtime: 566
Connection: keep-alive
Transfer-Encoding: chunked

Tests

Tests are written with mocha and should.js: after you cloned the repo simply run mocha or its verbose cousin ./node_modules/mocha/bin/mocha and everything should be green!