
GraphQL Query Rewriter - chanind
https://github.com/ef-eng/graphql-query-rewriter
======
epidemian
I 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)

~~~
chanind
I 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.

~~~
epidemian
That seems like a good approach for those cases, yeah :)

I guess the difference then boils down to whether you're OK with having a
bunch of deprecates stuff in your schema (you can also remove those fields
once they are not used anymore) and some "V2", "V3", etc in your field names,
or your prefer having a more pristine schema but paying some price of
discoverability and tooling support for those deprecated features.

------
revskill
What 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.

~~~
tuyiown
> Stringly type is too hard to be manipulated.

With param and fragments, the json would be too verbose, so you'd end up by
always building your queries programmatically, so sharing queries between
GraphQL implementations and/or runtimes/languages would be harder.

GraphQL language for queries and schema adds a layer that describe what your
runtime and/or compile time has to conform with, which is incredibly helpful
for stacks with heterogeneous environment.

In the end, you can always parse and generate those full text documents and
manipulate them programmatically, as much as you want (just be cautious with
perfs). So the apparent fragility has many ways to be addressed, with a huge
advantage of having common ground on all stacks.

~~~
revskill
With JSON, you have many benefit, such as syntax error for JSON. It's
impossible with stringly type.

With dynamically generation of query, JSON parsing is easier than stringly
type. You don't need a library like graphql-tag to do that.

Instead of posting a string body request, you post a JSON request, which is
the standard way for long time.

In general, stringly type graphql query is not a fine choice to me.

------
migueloller
I 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
Query 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
Basically 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
That 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
Yeah 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
The 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
So 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
The 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

