src/index.js
async function joinMonster(resolveInfo, context, dbCall, options = {}) {
  // we need to read the query AST and build a new "SQL AST" from which the SQL and
  const sqlAST = queryAST.queryASTToSqlAST(resolveInfo, options, context)
  const { sql, shapeDefinition } = await compileSqlAST(sqlAST, context, options)
  if (!sql) return {}

  // call their function for querying the DB, handle the different cases, do some validation, return a promise of the object
  let data = await handleUserDbCall(dbCall, sql, sqlAST, shapeDefinition)

  // if they are paginating, we'll get back an array which is essentially a "slice" of the whole data.
  // this function goes through the data tree and converts the arrays to Connection Objects
  data = arrToConnection(data, sqlAST)

  // so far we handled the first "batch". up until now, additional batches were ignored
  // this function recursively scanss the sqlAST and runs remaining batches
  await nextBatch(sqlAST, data, dbCall, context, options)

  // check for batch data
  if (Array.isArray(data)) {
    const childrenToCheck = sqlAST.children.filter(child => child.sqlBatch)
    return data.filter(d => {
      for (const child of childrenToCheck) {
        if (d[child.fieldName] == null) {
          return false
        }
      }
      return true
    })
  }

  return data
}
async function joinMonster(resolveInfo, context, dbCall, options = {}) {
  // we need to read the query AST and build a new "SQL AST" from which the SQL and
  const sqlAST = queryAST.queryASTToSqlAST(resolveInfo, options, context)
  const { sql, shapeDefinition } = await compileSqlAST(sqlAST, context, options)
  if (!sql) return {}

  // call their function for querying the DB, handle the different cases, do some validation, return a promise of the object
  let data = await handleUserDbCall(dbCall, sql, sqlAST, shapeDefinition)

  // if they are paginating, we'll get back an array which is essentially a "slice" of the whole data.
  // this function goes through the data tree and converts the arrays to Connection Objects
  data = arrToConnection(data, sqlAST)

  // so far we handled the first "batch". up until now, additional batches were ignored
  // this function recursively scanss the sqlAST and runs remaining batches
  await nextBatch(sqlAST, data, dbCall, context, options)

  // check for batch data
  if (Array.isArray(data)) {
    const childrenToCheck = sqlAST.children.filter(child => child.sqlBatch)
    return data.filter(d => {
      for (const child of childrenToCheck) {
        if (d[child.fieldName] == null) {
          return false
        }
      }
      return true
    })
  }

  return data
}
/* _                _
| |__   ___  __ _(_)_ __    ___  ___  _   _ _ __ ___ ___
| '_ \ / _ \/ _` | | '_ \  / __|/ _ \| | | | '__/ __/ _ \
| |_) |  __/ (_| | | | | | \__ \ (_) | |_| | | | (_|  __/
|_.__/ \___|\__, |_|_| |_| |___/\___/ \__,_|_|  \___\___|
|___/
 
Takes the GraphQL resolveInfo and returns a hydrated Object with the data.
@param {Object} resolveInfo - Contains the parsed GraphQL query, schema definition, and more. Obtained from the fourth argument to the resolver.
@param {Object} context - An arbitrary object that gets passed to the `where` function. Useful for contextual infomation that influeces the  `WHERE` condition, e.g. session, logged in user, localization.
@param {dbCall} dbCall - A function that is passed the compiled SQL that calls the database and returns a promise of the data.
@param {Object} [options]
@param {Boolean} options.minify - Generate minimum-length column names in the results table.
@param {String} options.dialect - The dialect of SQL your Database uses. Currently `'pg'`, `'oracle'`, `'mariadb'`, `'mysql'`, and `'sqlite3'` are supported.
@param {Object} options.dialectModule - An alternative to options.dialect. You can provide a custom implementation of one of the supported dialects.
@returns {Promise.<Object>} The correctly nested data from the database.