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

shape-api-utils

v0.1.0

Published

utils to orchestrate data API requests

Downloads

3

Readme

##API orchestration module (AOM)

1 - AOM provides following method to be called from within the request handling chain of an Express server:

  • orchestrateApis(opts, callback, objs)
    • This passes control to a main method in an application-specific orchestration module, or returns an error to the callback method if no application-specific orchestration module can be found.
    • Argument opts is cloned and then passed into the main method. It is expected to contain:
      • appOrchModule: a resolvable path to an application-specific orchestration module
      • apiOptions: a json configuration object containing request options for each API call
      • reqParams: an object with request params (a reference to Express' req.params)
      • reqQuery: an object with querystring values (a reference to Express' req.query)
      • contextProp: the name of a context property, to be used as container for derived API response values (see mergeResultWithDerived below)
    • Argument callback is expected to be a callback function which will be called when the orchestration finishes.
    • Argument objs is optional, it can be used to pass objects that should not be cloned, like class instances.

2 - AOM provides following methods for application's orchestration code:

  • replaceUriVars(apiOptions, uriVars)

    • takes a uri (apiOptions.uri) with placeholder variables and replaces those placeholders with actual values.
    • Argument apiOptions: options to be passed in to the request module; the method will modify the uri property of this object
    • Argument uriVars: an object containing all uri placeholders and their values
  • getApiData(apiOptions, uriVars, callback)

    • makes an API call, using the request module and returns a callback with (err, result) signature, and standardizes error handling/messaging.
    • Argument apiOptions: options to be passed in to the request module: it should contain at least a uri property which may have variable placeholders enclosed in curly braces ({myVar}), and may contain optional headers that will be passed to the API request
    • Argument uriVars: object with replacement values for the uri placeholders
  • getErrorResponse(err, body)

    • returns a standardized error response object (proper response status still needs to be handled by the server)
    • Argument err: an error ID or short string
    • Argument body: the full error dump, could be stack trace or error response from API
  • lodash

    • a wrapper for all lodash methods

3 - AOM makes an instance of a flow object available to the application-specific orchestration module. This flow object provides:

  • a facade to a select subset of methods that are derived from the vasync module:

    • parallel(ArrayOfFunctions, callback): invoke N functions in parallel (and merge the results). Each function should expect arguments (flow, callback).
    • waterfall(ArrayOfFunctions, callback): invoke N functions in series, propagating results between stages. The first function in the array should expect arguments (flow, callback). All subsequent functions should expect arguments (flow, dependency, callback), where dependency contains the data results of the previous functions within the chain.
  • access to the apiOptions, envVars, reqParams and reqQuery properties.

  • a method mergeResultWithDerived(result, derived)

    • merges a result object with a "derived" object; the derived object should contain any data that is derived/added by the orchestration logic to enhance API data. The derived data is placed in the contextProp namespace (='gc', the client-side global context)

Sample application-specific pseudo-code:

var apiUtils = require('shape-api-utils');

// the main function is called from the server
function main(flow, callback){
    // we need to get data for events and artist, these calls can be made in
    // 'parallel' since there is no dependency
    flow.parallel([getUpcomingEvents, getArtistInfo], function(err, result){
      // add derived data, for instance environment-specific settings
      flow.mergeResultWithDerived(result, {
        target_host: flow.apiOptions.params.host
      });
      callback(err, result);
    });
}

// all functions that are called by the 'flow' methods have access to the
// 'apiOptions', 'reqParams' and 'reqQuery' objects (which were passed into 'main')
function getUpcomingEvents(flow, callback){
    var uriVars = {
        host: flow.apiOptions.params.host,
        performerId: flow.reqParams.param,
        start: flow.apiOptions.upcomingEvents.params.start,
        rows: flow.apiOptions.upcomingEvents.params.rows
    };
    apiUtils.getApiData(flow.apiOptions.upcomingEvents, uriVars, callback);
}

// to get the artist info, we need to make two API calls: the URI for the second
// call needs to be constructed with data from the first call, hence there is a
// dependency between the two, which is handled by 'waterfall'.
function getArtistInfo(flow, callback){
    flow.waterfall([callPerformerApi, callArtistApi], callback);
}

function callPerformerApi(flow, callback){
    var uriVars = {
        host: flow.apiOptions.params.host,
        performerId: flow.reqParams.param
    };
    return apiUtils.getApiData(flow.apiOptions.performer, uriVars, callback);
}

// this method is called from within a waterfall and gets the result
// from the previous call through the 'dependency' argument
function callArtistApi(flow, dependency, callback){
    var uriVars = {
        extcatalogapi_host: flow.apiOptions.params.extcatalogapi_host,
        artistNames: getPerformerName(dependency)
    };
    return apiUtils.getApiData(flow.apiOptions.artist, uriVars,
      function(err, result){
        // add derived data, and then return the result
        flow.mergeResultWithDerived(result, {
            performerName: artistNames,
            artist_image: getArtistImagePath(result)
        });
        callback(err, result);
      }
    );
}

// a helper function that parses result data
function getPerformerName(result){
    var performerName = "";
    // custom logic to extract a performer name from the result
    return performerName;
}

// a helper function that derives an image path
function getArtistImagePath(result){
    var imgPath = "";
    // custom logic to extract an image path from the result
    return imgPath;
}

Sample SPA config for a QA environment:

General app.json:

{
  "bootstrapApiData": true,
  "suppressJs": false,
  "app_context": {
    "appName": "performer",
    "apiOptions": {
      "performer": {
        "uri": "http://{host}/shape/catalog/performers/v2/{performerId}",
        "headers": {
          "Accept": "application/json",
          "Authorization": "Bearer JYf0azPrf1RAvhUhpGZudVU9bBEa"
        }
      },
      "artist": {
        "uri": "http://{extcatalogapi_host}/extcatalogapi/artists?names={artistNames}"
      },
      "upcomingEvents": {
        "uri": "http://{host}/shape/search/catalog/events/v2/?performerId={performerId}&status=active&sort=dateLocal asc&start={start}&rows={rows}",
        "headers": {
          "Authorization": "Bearer JYf0azPrf1RAvhUhpGZudVU9bBEa"
        },
        "params": {
          "start": 0,
          "rows": 20
        }
      }
    }
  }
}

Environment-specific app_*.json, for example app_development.json:

{
  "app_context": {
    "apiOptions": {
      "params": {
        "host": "www.srwd33.com",
        "domain": "srwd33.com",
        "subdomain": "www",
        "tld": "com",
        "app_token": "JYf0azPrf1RAvhUhpGZudVU9bBEa",
        "extcatalogapi_host": "srwd10evx001.srwd10.com:8080"
      }
    }
  }
}