src/query-ast-to-sql-ast/index.js
function spreadFragments(selections, fragments, typeName) {
  return flatMap(selections, selection => {
    switch (selection.kind) {
    case 'FragmentSpread':
      const fragmentName = selection.name.value
      const fragment = fragments[fragmentName]
      return spreadFragments(fragment.selectionSet.selections, fragments, typeName)
    case 'InlineFragment':
      if (selection.typeCondition.name.value === typeName) {
        return spreadFragments(selection.selectionSet.selections, fragments, typeName)
      }
      return []

    default:
      return selection
    }
  })
}
function spreadFragments(selections, fragments, typeName) {
  return flatMap(selections, selection => {
    switch (selection.kind) {
    case 'FragmentSpread':
      const fragmentName = selection.name.value
      const fragment = fragments[fragmentName]
      return spreadFragments(fragment.selectionSet.selections, fragments, typeName)
    case 'InlineFragment':
      if (selection.typeCondition.name.value === typeName) {
        return spreadFragments(selection.selectionSet.selections, fragments, typeName)
      }
      return []

    default:
      return selection
    }
  })
}
instead of fields, selections can be fragments, which is another group of selections fragments can be arbitrarily nested this function recurses through and gets the relevant fields