1. Protobufs use integer IDs for fields. GraphQL uses string names. IMHO this is a clear win for protobufs. Changing the name of a field of GraphQL is essentially impossible. Once a name is there it's there forever (eg mobile client versions are out there forever) so you're going to have to return null from it and create a new one. In protobufs, the name you see in code is nothing more than the client's bindings. Get a copy of the .proto file, change a name (but not the ID number) and recompile and everything will work. The wire format is the same;
2. People who talk about auto-generating GraphQL wrappers for Postgres database schemas (not the author of this post, to be clear, but it's common enough) are missing the point entirely. The whole point of GraphQL is to span heterogeneous and independent data sources;
3. Protobuf's notions of required vs optional fields was a design mistake that's now impossible to rectify without breaking changes. Maybe protobuf v3/gRPC did this. I'm honestly not sure.
4. Protobuf is just a wire format plus a way of generating language bindings for it. There are RPC extensions for this (Stubby internally at Google; gRPC externally and no they're not the same thing). GraphQL is a query language. I do think it's better than protobufs in this regard;
5. GraphQL fragments are one of these things that are probably a net positive but they aren't as good as they might appear. You will find in any large codebase that there are key fragments that if you change in any way you'll generate a massive recompile across hundreds or thousands of callsites. And if just one caller uses one of the fields in that fragment, you can't remove it;
6. GraphQL does kind of support union types (eg foo as Bar1, foo as Bar2) but it's awkward and my understanding is the mobile code generated is... less than ideal. Still, it's better than not having it. The protobuf equivalent is to have many optional submessages and there's no way to express that only one of them will be popualated;
7. Under the hood I believe the GraphQL query is stored on the server and identified by ID but the bindings for it are baked into the client. Perhaps this is just how FB uses it? It always struck me as somewhat awkward. Perhaps certain GraphQL queries are particularly large? I never bothered to look into the reason for this but given that the bindings are baked into the code it doesn't seem to gain you much;
8. GraphQL usage in Facebook is pervasive and it has first class support in iOS, Android and React. This is in stark contrast to protobufs where protobuf v2 in Google is probably there forever and protobuf v3/gRPC is largely for the outsiders. It's been several years now since I worked at Google but I would be shocked if this had changed or there was even an intention of changing it at this point;
9. The fact that you can do a GraphQL mutation and declare what fields are returned is, IMHO, very nice. It saves really awkward create/update then re-query hops.
10. This is probably a problem only for Google internally but another factor on top of protobuf version was the API version. Build artifacts were declared with this API version, which was actually a huge headache if you wanted to bring in dependencies, some of which were Java APIv1 and others Java APIv2. I don't really understand why you had to make this kind of decision in creating build artifacts. Again, maybe this has improved. I would be surprised however.
Lastly, as for Sourcehut, I had a look at their home page. I'm honestly still not exactly sure what they are or what value they create. There are 3 pricing plans that provide access to all features so I'd have to dig in to find the difference (hint: I didn't). So it's hard for me to say if GraphQL is an appropriate choice for them. At least their pages loaded fast. That's a good sign.