GQL simultaneously addresses those two specific problems: resolution of normalized data, and giving UI consumers the power to declaratively fetch their desired data shape. It also has first-class TypeScript support through Apollo and the open-source community built around that.
I think it’s important to stress the tooling support, because you are correct… oneofs and union types are conceptually the same thing. A lot of it comes down to ergonomics in how you consume those types. In code generation GQL unions represent themselves as actual TypeScript union types, which means I can write type guards or switch on their discriminant to narrow to their correct member, whereas proto oneofs use a string value to access the correctly populated property. Small things in the day-to-day, but in how it manifested itself in code, it definitely felt like an improvement.
GQL unions also give you the power to do some really cool projection declaratively in queries [2]. Once again because of the nice compatibility of TS’ type system and GQL, the types returned from those queries code generate into really nice structures to work with.
I’m getting a bit rambly and don’t feel like I adequately answered your question, but it’s a bit late and I wanted to give you some response off the top of my head. I don’t want to knock on grpc-web or anything. A good deal of it has to deal with code ownership and team communication structures, and GQL ultimately felt like a better seam for our UI team to interact with our services.
I probably should write a blog post, because I have a lot of disconnected thoughts and need to have a more coherent narrative here. I think some code examples would better illustrate what seems like non-problems from how I've described them here. I’ll follow up once I’ve let it settle in my head.
[1] https://samnewman.io/patterns/architectural/bff/ [2] https://graphql.org/learn/schema/#union-types