docs: function addApiRoutes(opts) {
      var resources   = this.pancakes.cook('resources', null);
      var apiPrefix   = opts.apiPrefix;
      var server      = opts.server;
      var me          = this;
      var cors = {
          origin:         opts.hosts || [],
          headers:        ['Accept', 'Accept-Version', 'Content-Type', 'Api-Version', 'X-Requested-With', 'Authorization'],
          credentials:    true
      };
  
      _.each(resources, function (resource) {
          _.each(resource.api, function (methodInfo, httpMethod) {
              _.each(methodInfo, function (operation, path) {
  
                  // if path includes {_id} convert to {_id?} so optional
                  // this will allow both /users/someid and /users to work
                  // we want this because pancakes handles param validation
                  // only do this if path without {_id} is not already defined
                  var lenWithoutId = path.length - 6;
                  var pathWithoutId = path.substring(0, lenWithoutId);
                  if (path.substring(lenWithoutId) === '/{_id}' && !methodInfo[pathWithoutId]) {
                      path = pathWithoutId + '/{_id?}';
                  }
  
                  // if the operation is a string, then it is the name of the handler method
                  if (_.isString(operation)) {
                      server.route({
                          method:             httpMethod,
                          path:               apiPrefix + path,
                          config: {
                              cors:           cors,
                              handler:        me.getApiRouteHandler(resource, operation)
                          }
                      });
                  }
  
                  // else if object with type stream, need to use a slightly different call to server.route
                  else if (_.isObject(operation) && operation.type === 'stream' && operation.operation) {
                      server.route({
                          method:             httpMethod,
                          path:               apiPrefix + path,
                          config: {
                              payload: {
                                  maxBytes:   209715200,
                                  output:     'stream',
                                  parse:      true
                              },
                              cors:           cors,
                              handler:        me.getApiRouteHandler(resource, operation.operation)
                          }
                      });
                  }
                  // or xml...
                  else if (_.isObject(operation) && operation.type === 'xml' && operation.operation) {
                      server.route({
                          method:             httpMethod,
                          path:               apiPrefix + path,
                          config: {
                              cors:           cors,
                              handler:        me.getApiRouteHandler(resource, operation.operation, 'application/xml; charset=utf-8')
                          }
                      });
                  }
  
                  // else error
                  else {
                      throw new Error('Invalid type of operation for ' + resource.name + ' ' + path);
                  }
              });
          });
      });
  
      server.on('response', function (request) {
          // careful because we don't want to screw up all/any requests...
          if ( request.response && request.response.statusCode && request.response.statusCode > 399 && request.method
                  && request.method.toUpperCase && request.url && request.url.path ) {
              var lapse = -1;
              if ( request.info && request.info.responded && request.info.received ) {
                  lapse = request.info.responded - request.info.received;
              }
              var msg = request.method.toUpperCase() + ' ' + request.url.path + ' -> '
                  + request.response.statusCode + ' ' + '(' + lapse + 'ms)';
              if ( request.info && request.info.referrer && request.info.referrer.length ) {
                  msg += ' ref: ' + request.info.referrer;
              }
  
              /* eslint no-console:0 */
              console.log(msg);
          }
      });
  }