I wonder how many hidden n+1 problems there are. Reading the blog post, it looks like they mostly only tested existing pathways as part of the migration. Who knows what random requests could do.

Yes, this is the biggest problem with GraphQL that I haven't addressed anywhere.

The architectural beauty of GraphQL is that you can write isolated, functional nodes that only have to know about their object type. For example, the User node only has to know how to get the user's last_login, active status, and return a reference to another node type like a profile Image.

Inside the User node, you are free to write code (e.g. Java or Python) that can compute which Image node is the right one to return. i.e. you can write:

  class UserNode:
      def get_user_profile_image() -> ImageNode:
          return some_arbitrary_function()

There is no way I've seen to square this kind of arbitrary node computation with the desire to write an optimized SQL query that selects all needed information in one big-ass statement.
You could use https://github.com/join-monster/join-monster, but setting that aside it's usually enough to use Dataloaders that resolve based on some source ID, with some server-local caching on top if latency becomes an issue.