profusion / sgqlc

Simple GraphQL Client
https://sgqlc.readthedocs.io/
ISC License
511 stars 85 forks source link

Is there a way to get "subfields" when using the __fields__() method? #104

Open Justin-Wise opened 4 years ago

Justin-Wise commented 4 years ago

Is there a way to use the fields method to query items in the current level, as well as those in a sublevel?

If something looked like this: query { repository(id: "repo1") { issues { number title body reporter { login name } could you do something like issues.__field__('number','{reporter:login}')?

barbieri commented 4 years ago

just a sub-selection, you can't. You need to do in 2 statements:

issues.__fields__() # the the basic fields
issues.reporter.__fields__()

However, if you want the whole subtree -- be careful of loops/cycles -- you can use __to_graphql__(auto_select_depth=3) you can see some examples at https://sgqlc.readthedocs.io/en/latest/sgqlc.operation.html

n-wach commented 3 years ago

How hard would it be to add support for this?

I was somewhat disappointed this wasn't a built-in feature, and ended up creating some utilities to be able to add subfields from a string path.

barbieri commented 3 years ago

@n-wach well, as I said you shouldn't be querying for all fields, just the fields that you use, that's the idea behind GraphQL, the lack of "versions" and one of the way to avoid breaks.

However, during dev/debug it may be helpful, so I added the __to_graphql__(auto_select_depth=3)

But be careful, loops may cause some issues (that's why the depth is low by default).

What kind of app/client are you writing?

n-wach commented 3 years ago

We are writing a python library that wraps a GraphQL API, built on top of SGQLC.

We have a bit of a unique situation--our GraphQL schema is complicated and can be updated frequently, so we're using autogenerated SGQLC. We've added wrapper classes that wrap the SGQLC types, and provide methods for populating additional fields, performing operations, etc.

We frequently want to allow the library user to decide what fields they want (rather than selecting all fields). Since there are a few layers of error handing/abstraction between the public functions and underlying SGQLC operation/query, we had to find an easier way to specify fields.

We have a class that stores a list of strings for fields we want on an operation. Class can be created and modified before the Operation is created, and handles adding Selections prior to the Operation being sent. The major new feature being that we allow specifying subpaths. So you could include "issues.reporter.name" when an operation would return Repository. We've got some other features (default selected fields rather than selecting all, support for arguments, excluding from default fields) that help make the API easier to use.

Although we love SGQLC, we've effectively hidden it from our library's users

barbieri commented 3 years ago

@n-wach wow, my company is a heavy user of GraphQL on both server and client side, we have web applications, react-native applications, federated servers and so on, we never had to reach that. Are you using it as a client on the server side or front-ends?

I ask because on front-ends, at least in JavaScript world, the trend is to specify some fragments or some operations and let the rest be generated, so you just write the DSL and let the infra do stuff for you. At the end this becomes very, very easy to use, you test on some interactive playground, then copy that to your application and it's basically done.

To bring SGQLC closer to that, more recently I've added operations support to sgqlc-codegen, you can type your fragments, your operations and have it to generate the Operation for you in a simple and efficient way. I'm even mulling on some optimizations, such as cache/save the output code (which is basically a "compressed" form of the input, so we remove whitespaces and all), instead of generating it every time as currently done by SGQLC.

Also, I'm planing to add the https://docs.python.org/3/library/typing.html support to that generation (not the whole SGQLC as it uses some metatypes and it makes static typing difficult), so you could get the resulting data shape once you + the operation with the data.

Maybe going that path would bring you more flexibility while making it easier for your users? You can provide a fragment library and people could use that + the sgqlc-codegen operation to generate stuff, instead of using yet-another-layer.

n-wach commented 3 years ago

Yeah, our JS front-end applications use fragments, and so do tools on the server-side.

The goal of our library is to open up our API to Python scripting for other parts of the company. We wanted it to be approachable and simple to use straight out of the box (by including a default subset of fields on queries, etc.), but also allow developers low level access to optimize/extend if they have to.

Most users will be clients writing scripts to automate tasks, we don't use this library on the server side.

barbieri commented 3 years ago

ok, anyway, the code that __fields__ and __to_graphql__() uses are based on primitives like dir() and iterations, then you can probably implement that behavior yourself, correct?

Do you have any issues to get it done? If so, let me know.

n-wach commented 3 years ago

Yeah we've got it working.

I was just wondering if letting __fields__ natively accept subfields (like "issues.reporter.name") would be a useful feature for other folks.

barbieri commented 3 years ago

I think it could be, but I fear people abusing it, with a __fields__ and a huuuuge list of stuff, moving from python to its own "dsl".

Remember that GraphQL allows arguments, so if you add a.b.c you likely will have people asking for a(x=1).b.c and you reinvent SGQLC using strings rather than python code 😅