4 ms·
GraphQL Query Rewriter
- migueloller 7y agoI was originally skeptical as GraphQL already comes with the `deprecated` directive for this use case and it’s recommended for API evolution. Was actually delighted to see that this can let you make changes to your schema transparently without breaking clients!
- setr 7y agoQuery rewriting is always scary to make use of, because it only takes like 2 rounds of rewrites (original | rewriter1 | rewriter2 | db) before all hope is lost If you end up using query rewriting to upgrade client queries, you'll end up very quickly with divergent SQL-schemas spread across many clients with little proper way to identify (client A module B was written targeting DB v0.1, but module C was looking at DB v0.2, and client B was only written when v1.0 released; A was upgraded by query rewriting from v0.1/0.2 to v0.5 and then v1.0, so the codebase still features ancient references, and applies two rounds of rewrites to find the final query output)
- anaphor 7y agoBasically you would have to prove that your rewrites are confluent in the sense of a term-rewriting system[1]. I'm not sure if the project here gives you any help in doing that, but it would be pretty cool if you could somehow prove the rewrites are confluent. [1]https://en.wikipedia.org/wiki/Confluence_(abstract_rewriting) https://en.wikipedia.org/wiki/Confluence_(abstract_rewriting...
- setr 7y agoThat only half-helps though doesn’t it? It saves you from rewrite-order dependence, but doesn’t the requirement that one has to follow ten rewrite (patches?) to figure out what was actually expressed by a query in the codebase. Which takes it down from hell-mode to hard-mode difficulty
- anaphor 7y agoYeah that's true, and now that you mention it, maybe patch theory could help (in the sense of Pijul, Darcs, etc) Though I don't know how much effort I would spend on trying to make this work.
- setr 7y agoThe problem I think is that at least with patching, its a run-once situation; once its updated, you never have to see that code/binary again. With query rewriting, its a continual patch, which applies both against the old queries.., and when the old queries are modified; that is, two divergent codebases now exist, referring to different schemas, but unknowingly one codebase gets warped into a different universe where its query somehow works. Both codebases continue to exist, and continue to be worked on. Divergent, yet somehow converging. Every client hit by query rewriting is another divergence. The issue is that the codebase’s query in fact needs to be rewritten.. once, and finally! If you only modify it in-stream... then the rewrite rule must exist until the end of time, or the codebase is updated manually. So really you want a way to patch codebases you probably don’t control.. but I think if you can solve that, you’ve solved APIs everywhere
- anaphor 7y agoSo maybe what's really needed is an editor plugin for GraphQL that lets you auto-rewrite queries, or if the queries are generated by a library, then that library should do it.
- setr 7y agoThe thing though is that you don’t actually want to rewrite at runtime; thats the hacky solution which ends up as layers of rewrites. You want to rewrite at compile-time, rewriting the codebase itself, and save the changes to version control. As if a human were updating to a new schema. You don’t want to allow stale, unversioned schema-references littered around the codebase itself; being valid by the time it executes is fine and well functionally, but hell to follow
- rhacker 7y agoYou just described healthcare data formats in a sentence.
- epidemian 7y agoI would personally caution against using an implicit rewriter like this. It is implicit in the sense once you change your schema, there's no documentation about the deprecated queries that are still supported. Yes, old queries will still work, but people who stumble up on them will be very confused as to why they are working, since the schema will say otherwise. Tools like IDE auto-completion for queries[1] or the graphical interface GraphiQL[2] will also ignore these rewrite capabilities and so will not help with writing, editing or running the old-but-still-supported rewritten queries. Instead of that, i'd personally recommend either sticking with your old schema if the change is as superfluous as the one mentioned on the project's README (changing the type of the `userById(id)` field parameter from `String!` to `ID!`), or shamelessly embracing versioning your fields. In the case example case mentioned on the README, that could mean adding a @deprecated directive on the `userById(id: String!)` field, and then adding a new `userByIdV2(id: ID!)`. Users of the new field can alias it on to a friendlier versionless name, like: query { user: userByIdV2(id: 123) { ... } } This way, the changes on the schema are much more explicit: users can still use tools like GraphiQL or schema-aware text editor plugins to write their queries, while receiving feedback about their use of deprecated fields, and what they can do about them :) [1] Like Intellij IDEA's graphql plugin https://plugins.jetbrains.com/plugin/8097-js-graphql https://plugins.jetbrains.com/plugin/8097-js-graphql [2] https://github.com/graphql/graphiql https://github.com/graphql/graphiql
- grrowl 7y agoI'd reckon the vast majority of real-world use of GraphQL is bog-standard web-api apps. With this, you could deploy a single schema changeset which updates the server, client, and middleware; then remove the middleware in a week once the web client has been guaranteed to have been refreshed or reloaded. Best of both worlds really.
- chanind 7y agoI wouldn't necessarily recommend using query rewriting for public graphql apis, but for internal apis or private apis to power mobile apps or web apps it should be fine. Just make sure you add tests for the old queries to ensure they keep working. Once you see the deprecated queries stop being used as users update their clients you can drop the rewriting entirely too.
- revskill 7y agoWhat i don't like about Graphql is that it enable stringly type query. Why not enforcing JSON instead ? Stringly type is too hard to be manipulated.
- alexchamberlain 7y agoCan you give an example of what you mean?
- revskill 7y agoI mean, instead of query { user { id name } } why not query: { user: { id: true, name: true, } }
- strken 7y agoGenerally you'd use a library with much stricter conventions than JSON to generate and parse GraphQL queries, rather than writing them as a string. If you're using JS/ES/TS, you can even configure a linter[0] to validate your queries against a schema as you're writing them. Insofar as JSON is a type system, it's a weaker one than GraphQL, and would be a lot more verbose. Consider the query query(id: Int, hasUser: Boolean) { @include(if: $hasUser) user(id: $id) { name } } It would be possible to represent this in JSON, but it would be a lot less elegant. { "query": { "args": {"id": "int", "hasUser": "boolean"}, "fields": { "user": { "args": {"id": "$id"}, "fields": {"name": true}, "includeIf": "$hasUser" } } } } The JSON still depends on stringly-typed values like "$hasUser", but it's much harder to read and has many more unnecessary characters. EDIT: if you want to manipulate GraphQL queries, you need to parse them[1] and then look at the AST. This is not very well documented, so the best way of doing it is by experimenting with the values you've got, and then verifying your approach against graphql-js. [0] https://github.com/apollographql/eslint-plugin-graphql https://github.com/apollographql/eslint-plugin-graphql [1] https://github.com/apollographql/graphql-tag https://github.com/apollographql/graphql-tag
- tuyiown 7y ago