The problems you outline are real, but have straightforward solutions. You can persist queries to the server and then GET them in a really natural way, using query params to provide arguments for your GraphQL query's variables. Then, when it comes to caching, just do it at the resolver level, using something like DataLoader, perhaps with a KV store.
For the Semantic Web, I developped a query repository and a query builder GUI (cf datao.net). And my frontend (search.datao.net) fetches thoses queries and apply them upon resource IDs it receives from a search engine.