{"id":15556275,"url":"https://github.com/jhnnsrs/rath","last_synced_at":"2025-04-16T05:56:08.120Z","repository":{"id":37927005,"uuid":"453121663","full_name":"jhnnsrs/rath","owner":"jhnnsrs","description":"rath is an apollo-like graphql client that supports links, multipart uploads, websocket subscriptions with sync and async interface","archived":false,"fork":false,"pushed_at":"2025-04-03T11:10:07.000Z","size":2412,"stargazers_count":10,"open_issues_count":3,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-14T09:17:40.523Z","etag":null,"topics":["aiohttp","asyncio","python"],"latest_commit_sha":null,"homepage":"https://jhnnsrs.github.io/rath/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jhnnsrs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-01-28T15:38:10.000Z","updated_at":"2025-04-03T11:10:11.000Z","dependencies_parsed_at":"2024-11-01T13:14:52.861Z","dependency_job_id":null,"html_url":"https://github.com/jhnnsrs/rath","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhnnsrs%2Frath","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhnnsrs%2Frath/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhnnsrs%2Frath/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhnnsrs%2Frath/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jhnnsrs","download_url":"https://codeload.github.com/jhnnsrs/rath/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249205598,"owners_count":21229955,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aiohttp","asyncio","python"],"created_at":"2024-10-02T15:12:56.879Z","updated_at":"2025-04-16T05:56:08.101Z","avatar_url":"https://github.com/jhnnsrs.png","language":"Python","funding_links":[],"categories":["Implementations"],"sub_categories":["Python"],"readme":"# rath\n\n[![codecov](https://codecov.io/gh/jhnnsrs/rath/branch/master/graph/badge.svg?token=UGXEA2THBV)](https://codecov.io/gh/jhnnsrs/rath)\n[![PyPI version](https://badge.fury.io/py/rath.svg)](https://pypi.org/project/rath/)\n[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://pypi.org/project/rath/)\n![Maintainer](https://img.shields.io/badge/maintainer-jhnnsrs-blue)\n[![PyPI pyversions](https://img.shields.io/pypi/pyversions/rath.svg)](https://pypi.python.org/pypi/rath/)\n[![PyPI status](https://img.shields.io/pypi/status/rath.svg)](https://pypi.python.org/pypi/rath/)\n[![PyPI download month](https://img.shields.io/pypi/dm/rath.svg)](https://pypi.python.org/pypi/rath/)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/jhnnsrs/rath)\n\n\n\n## Inspiration\n\nRath is a transportation agnostic graphql client for python focused on composability. It utilizes `Links` to\ncompose GraphQL request logic, similar to the apollo client in typescript. It comes with predefined links to\nenable transports like aiohttp, websockets and httpx, as well as links to retrieve auth tokens, enable retry logic\nor validating requests on a schema.\n\n## Supported Transports\n\n- aiohttp\n- httpx\n- websockets (both  graphql-ws and subscriptions-transport-ws)\n\n## Installation\n\n```bash\npip install rath\n```\n\n## Usage Example\n\n```python\nfrom rath.links.auth import ComposedAuthLink\nfrom rath.links.aiohttp import AIOHttpLink\nfrom rath.links import compose\nfrom rath import Rath\n\nasync def aload_token():\n    return \"SERVER_TOKEN\"\n\n\nauth = ComposedAuthLink(token_loader=aload_token)\nlink = AIOHttpLink(endpoint_url=\"https://countries.trevorblades.com/\")\n\n\nwith Rath(link=compose(auth,link)) as rath:\n    query = \"\"\"query {\n        countries {\n            native\n            capital\n        }\n        }\n\n    \"\"\"\n\n    result = rath.query(query)\n    print(result)\n```\n\nThis example composes both the AuthToken and AIOHttp link: During each query the Bearer headers are set to the retrieved token, and the query is sent to the specified endpoint.\n(Additionally if the servers raises a 401, the token is refreshed and the query is retried)\n\n\n## Async Usage\n\nRath is build for async usage but uses koil, for async/sync compatibility\n\n```python\nfrom rath.links.auth import ComposedAuthLink\nfrom rath.links.aiohttp import AIOHttpLink\nfrom rath.links import compose\nfrom rath import Rath\n\nasync def aload_token():\n    return \"SERVER_TOKEN\"\n\n\nauth = ComposedAuthLink(token_loader=aload_token)\nlink = AIOHttpLink(endpoint_url=\"https://countries.trevorblades.com/\")\n\nasync def main():\n\n  async with Rath(link=compose(auth,link)) as rath:\n      query = \"\"\"query {\n          countries {\n              native\n              capital\n          }\n          }\n\n      \"\"\"\n\n      result = await rath.aquery(query)\n      print(result)\n\n\nasyncio.run(main())\n```\n\n## Example Transport Switch\n\nLinks allow the composition of additional logic based on your graphql operation. For example you might want\nto use different grapqhl transports for different kind of operations (e.g using websockets for subscriptions,\nbut using standard http requests for potential caching on queries and mutations). This can be easily\naccomplished by providing a split link.\n\n```python\nfrom rath.links.auth import ComposedAuthLink\nfrom rath.links.aiohttp import AIOHttpLink\nfrom rath.links.graphql_ws import GraphQLWSLink\nfrom rath.links import compose, split\n\nfrom rath import Rath\n\nasync def aload_token():\n    return \"SERVER_TOKEN\"\n\n\nauth = ComposedAuthLink(token_loader=aload_token)\nlink = AIOHttpLink(endpoint_url=\"https://countries.trevorblades.com/\")\nws = GraphQLWSLink(ws_endpoint_url=\"wss://countries.trevorblades.com/\") # \n\n\nend_link = split(link, ws, lambda op: op.node.operation != \"subscription\")\n\n\nwith Rath(link=end_link) as rath:\n    query = \"\"\"query {\n        countries {\n            native\n            capital\n        }\n        }\n\n    \"\"\"\n\n    result = rath.query(query) # uses the http link\n    print(result)\n\n    subscription = \"\"\"subscription {\n        newCountry {\n            native\n            capital\n        }\n        }\n\n    \"\"\"\n\n    for i in rath.subscribe(subscription): # uses the ws link\n        print(i) # will fail because the server does not support subscriptions\n\n  \n```\n\n## Included Links\n\n- Validating Link (validate query against local schema (or introspect the schema))\n- Reconnecting WebsocketLink\n- AioHttpLink (with multi-part upload support)\n- SplitLink (allows to split the terminating link - Subscription into WebsocketLink, Query, Mutation into Aiohttp)\n- AuthTokenLink (Token insertion with automatic refreshs)\n\n## Authentication\n\nSearching for a solution to authenticate graphql requests with oauth2. Look no further, rath + herre has you covered. [Herre](https://github.com/jhnnsrs/herre) is an oauth2 client library that allows you to dynamically (and asychronously) retrieve tokens. Rath provides `herre` link in this repository, which can be used to retrieve access tokens e.g for githubs graphql api.\n\n```python\nfrom herre import Herre\nfrom rath import Rath\nfrom rath.links.aiohttp import AIOHttpLink\nfrom rath.contrib.herre.links.auth import HerreAuthLink\nfrom rath.links import compose\n\nfrom herre.grants.oauth2.authorization_code_server import AuthorizationCodeServerGrant\n\n\n# Herre follows a similar design as links with grants\nherre = Herre(\n    grant=AuthorizationCodeServerGrant(\n        base_url=\"https://github.com/login/oauth\",\n        token_path=\"access_token\",\n        client_id=\"dfdb2c594470db113659\",  # This is a demo github oauth2 app\n        client_secret=\"bc59f1e3bc1ed0dcfb3548b457588f3b6e324764\",  #\n        scopes=[],\n        append_trailing_slash=False,  # github does not like trailing slashes\n    )\n)\n\nauth = HerreAuthLink(herre=herre)\nlink = AIOHttpLink(endpoint_url=\"https://api.github.com/graphql\")\n\n\nwith herre:\n    with Rath(link=compose(auth, link)) as rath:\n        query = \"\"\"query {\n            viewer {\n                login\n                }\n            }\n\n        \"\"\"  # this query will return the logined user\n\n        result = rath.query(query)\n        print(result)\n\n```\n\nIn this example on running the script, a browser window will open and ask you to login to github. After logging in, the script will print your username. You can of course use any other grant type, e.g the client credentials grant to authenticate against a graphql api.\n\n\n\n## Typed Operations\n\nSearching for a solution to generate typed operations for your graphql api? Look no further, rath + turms has you covered. [Turms](https://github.com/jhnnsrs/turms) is a graphql code generator that allows you to generate typed operations for your graphql api.\n\nRath works especially well with turms generated typed operations:\n\n```python\nimport asyncio\nfrom examples.api.schema import aget_capsules\nfrom rath.rath import Rath\nfrom rath.links.aiohttp import AIOHttpLink\nfrom rath.links.auth import AuthTokenLink\nfrom rath.links.compose import compose\n\n\nasync def token_loader():\n    return \"\"\n\n\nlink = compose(\n    AuthTokenLink(token_loader), AIOHttpLink(\"https://api.spacex.land/graphql/\")\n)\n\n\nrath = Rath(\n    link=link,\n    register=True, # allows global access (singleton-antipattern, but rath has no state)\n)\n\n\nasync def main():\n\n    async with rath:\n        capsules = await aget_capsules() # fully typed pydantic powered dataclasses generated through turms\n        print(capsules)\n\n\nasyncio.run(main())\n\n```\n\nThis github repository also contains an example client with a turms generated query with the public SpaceX api, as well as a sample of the generated api.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjhnnsrs%2Frath","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjhnnsrs%2Frath","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjhnnsrs%2Frath/lists"}