docs: function fetchData(pathOptions, routerOptions, locals) {
      pathOptions = pathOptions || {};
      routerOptions = routerOptions || {};
  
      let postQuery = _.cloneDeep(defaultPostQuery),
          props = {};
  
      if (routerOptions.filter) {
          postQuery.options.filter = routerOptions.filter;
      }
  
      if (routerOptions.order) {
          postQuery.options.order = routerOptions.order;
      }
  
      if (pathOptions.hasOwnProperty('page')) {
          postQuery.options.page = pathOptions.page;
      }
  
      if (pathOptions.hasOwnProperty('limit')) {
          postQuery.options.limit = pathOptions.limit;
      }
  
      // CASE: always fetch post entries
      // The filter can in theory contain a "%s" e.g. filter="primary_tag:%s"
      props.posts = processQuery(postQuery, pathOptions.slug, locals);
  
      // CASE: fetch more data defined by the router e.g. tags, authors - see TaxonomyRouter
      _.each(routerOptions.data, function (query, name) {
          const dataQueryOptions = _.merge(query, defaultDataQueryOptions[name]);
          props[name] = processQuery(dataQueryOptions, pathOptions.slug, locals);
      });
  
      return Promise.props(props)
          .then(function formatResponse(results) {
              const response = _.cloneDeep(results.posts);
  
              if (routerOptions.data) {
                  response.data = {};
  
                  _.each(routerOptions.data, function (config, name) {
                      response.data[name] = results[name][config.resource];
  
                      if (config.type === 'browse') {
                          response.data[name].meta = results[name].meta;
                          // @TODO remove in v3
                          response.data[name][config.resource] = results[name][config.resource];
                      }
                  });
              }
  
              return response;
          });
  }