Package: python-qlient
Qlient: Python GraphQL ClientA fast and modern graphql client library designed with simplicity in mind.Getting StartedInstallingpipinstallpython-qlientUsageThis example shows a simple queryfromqlientimportClientclient=Client("https://countries.trevorblades.com/")response=client.query.countries(select=["name","capital"],where={"filter":{"code":{"eq":"CH"}}})# which is equal toresponse=client.query.countries.select(["name","capital"]).where({"filter":{"code":{"eq":"CH"}}}).exec()# The 'where' method is only available on queries and named like this just for synthetic sugar.# for mutations it's 'set_variables'.# both methods do the same.DocumentationThe Documentation covers the following points:QueryMutationSubscriptionTransporterAsyncTransporterSettingsmax_recursion_depthbase_response_keybase_payload_keyreturn_requests_responsedisable_selection_lookupreturn_full_subscription_bodyCacheCLIQueryQueries are the A and O of graphql and are as easy as:fromqlientimportClientclient=Client("https://countries.trevorblades.com/")response=client.query.country(select=["name","capital"],where={"filter":{"code":{"eq":"CH"}}})Which will make a request to the server and return the name and capital of the country switzerland.But what if you want to make a more complex query with fields within fields?
Don't need to worry, we got you covered:fromqlientimportClient,fieldsclient=Client("https://countries.trevorblades.com/")response=client.query.country(select=fields("name","capital",languages=fields("name")),# languages is an object that needs a subselectionwhere={"filter":{"code":{"eq":"CH"}}})Using the fields method fromqlientyou can simply use*argsand**kwargsfor making deeper selections.
By the way, you could stack this like forever.Last but not least, what if you don't know the fields you could select?
Yup, we got you somewhat covered as well. The thing is, that due to performance issues,
this package is not able to completely create a query that retrieves all fields for a Query.
I have set the max depth to2(Can be changed viaSettings). This allows to still send a query without selecting any fields
but you won't get them all. If you want all, use thefieldsfunction defined above.fromqlientimportClientclient=Client("https://countries.trevorblades.com/")response=client.query.country(where={"filter":{"code":{"eq":"CH"}}})MutationI haven't found a real world example for making mutations without being authenticated,
so here's a hypothetical one.fromqlientimportClientclient=Client("https://some-host.com/authentication")response=client.mutation.register(data={"email":"foo@bar.com","password":"987654321"},select=["userId"])SubscriptionSometimes you want to execute things when something - an action - happened on the server.
In those cases, you can subscribe to an event.
For subscribing to an endpoint, I am using the asynciowebsocketslibrary.
So have a look at theirdocumentationfor clarification.Here is a basic exampleimportasynciofromqlientimportClientclient=Client("http://your-host:8080")defon_event(data:dict):# ... do something with the dataprint(data)asyncio.run(client.subscription.my_subscription(handle=on_event))# the asyncio.run() function is important!Different Websocket endpointIf no websocket endpoint was specified, it gets adapted based on the given request host.
for examplehttp://localhost:3000becomesws://localhost:3000.
Same goes for secured connections:httpsbecomeswss.
But it may be, that you have different endpoints. Therefor you can specify the websocket endpoint
manually.fromqlientimportClientclient=Client("http://your-host:8080",ws_endpoint="wss://your-other-host:3000")DebuggingWhen you need to see the to be executed query, you simply do as following:fromqlientimportClientclient=Client("https://countries.trevorblades.com/")print(client.query.countries.query_string)# should print something like:# query countries { countries { code name native phone capital currency emoji emojiU continent { code name } languages { code name native rtl } states { code name } } }And when you need to change the selection:print(client.query.countries.select(["code","name"]).query_string)# which prints: query countries { countries { code name } }Or with variables:print(client.query.countries.select(["code","name"]).set_variables({"filter":{"code":{"eq":"CH"}}}).query_string)# which prints: query countries ($filter: CountryFilterInput) { countries (filter: $filter) { code name } }# the variables are not visible in the query but rather will be send as variables dict to the server.TransporterFor making requests, we use a transporter. (Irrelevant for Websockets.)If none is given, a new one will be created.Sometimes you want your own custom session to be used for making requests.
For example if you need to authenticate yourself with some sort of an api key.
Therefor, you can pass it directly to the transporter.importrequestsfromqlientimportClient,Transportermy_session=requests.sessions.session()my_session.headers["Authorization"]="Bearer some-api-token"client=Client("https://foo.bar/",transporter=Transporter(session=my_session))AsyncTransporterAnd an AsyncTransporter:importasynciofromqlientimportClient,AsyncTransporterclient=Client("https://countries.trevorblades.com/",transporter=AsyncTransporter())asyncdefrequest_data():returnawaitclient.query.country(select=["name","capital"],where={"code":"CH"})asyncio.run(request_data())SettingsMost things can be adjusted using the settings.
When no settings are passed by to a client, the default values will be used insteadmax_recursion_depthThe max_recursion_depth can be used for changing the max depth for deeper automatic selection lookup.
Default is 2.fromqlientimportClient,Settingssettings=Settings(max_recursion_depth=5)# Due to performance reasons I do not recommend to go any higher than that.client=Client("https://countries.trevorblades.com/",settings=settings)base_response_keyThe base_response_key can be changed for setting the base key that is being used to get the data from the server.
Default is "data".fromqlientimportClient,Settingssettings=Settings(base_response_key="my_custom_data_key")client=Client("https://countries.trevorblades.com/",settings=settings)base_payload_keyThe base_payload_key can be changed for setting the base key that is being used to read the data from the websocket response.
Default is "payload".fromqlientimportClient,Settingssettings=Settings(base_payload_key="my_custom_payload_key")client=Client("https://countries.trevorblades.com/",settings=settings)return_requests_responseThe return_requests_response can be set to True if you want the whole request back instead of just the json.
Default is False.fromqlientimportClient,Settingssettings=Settings(return_requests_response=True)client=Client("https://countries.trevorblades.com/",settings=settings)disable_selection_lookupThe disable_selection_lookup can be set to True if you want to disable the automatic selection lookup.
Default is False.fromqlientimportClient,Settingssettings=Settings(disable_selection_lookup=True)client=Client("https://countries.trevorblades.com/",settings=settings)return_full_subscription_bodyThe return_full_subscription_body can be set to True if you want to get the full websocket response instead of only
the data.fromqlientimportClient,Settingssettings=Settings(return_full_subscription_body=True)client=Client("https://countries.trevorblades.com/",settings=settings)CacheThe qlient packages comes with two built in caching mechanisms that are optional.
Bear in mind that the cache isonlyused for schema introspection and queries.MemoryCacheThe memory cache uses a dictionary to store all keys and values.fromqlientimportClient,MemoryCacheclient=Client("https://countries.trevorblades.com/",cache=MemoryCache(ttl=5))# Creates a new client with a in memory cache where each object has a max time-to-live of 5 seconds.DiskCacheThe disk cache writes and reads files to and from the physical disk.
Even though the disk cache is efficient, i'd recommend only using this for requests where the data doesn't change a lot.fromqlientimportClient,DiskCacheclient=Client("https://countries.trevorblades.com/",cache=DiskCache(ttl=60*60*24))# Creates a new client with a disk cache where each object has a max time-to-live of 86'400 seconds or a day.Custom DiskCache LocationThe default disk cache location is in thepython-qlientfolder within the system specific tempdir folder.Windows:C:\Users\{myUser}\AppData\Local\Temp\python-qlientLinux:/tmp/python-qlientHowever, this can be changed by setting the root variable:fromqlientimportDiskCachecache=DiskCache(root=r"path/to/your/desired/root/folder")CLIQlient also provides a CLI for inspecting a schema.qlient--inspect"https://countries.trevorblades.com/"# or short:# qlient -i "https://countries.trevorblades.com/"AuthorsDaniel Seifert-Initial work-Lab9AcknowledgmentsHeavily inspired byZeep