Skip to main content

ahasura

Async and sync Hasura client.

Install

ahasura is available on PyPi:

pip install ahasura
# Or
poetry add ahasura

Quick example

from ahasura import ADMIN, Hasura
hasura = Hasura("http://localhost:8080", admin_secret="fake secret")

data = hasura(
    """
    query($id: uuid!) {
        item_by_pk(id: $id) {
            name
        }
    }
    """,
    auth=ADMIN,
    id="00000000-0000-0000-0000-000000000001",
)

item = data["item_by_pk"]
assert item["name"] == "Some name"

Create client

  • hasura = Hasura(...)
  • Args:
    • endpoint: str - HASURA_GRAPHQL_ENDPOINT, without trailing / or /v1/graphql
    • admin_secret: Optional[str] - HASURA_GRAPHQL_ADMIN_SECRET, required for auth=ADMIN only
    • timeout: Optional[float] = 10 - Seconds of network inactivity allowed. None disables the timeout
  • hasura client just keeps the configuration above, so you can reuse global client(s)
  • Shortcuts:
    • hasura(...) is a shortcut for sync GraphQL client: hasura.gql(...)
    • You can define a shortcut for Async GraphQL client: ahasura = hasura.agql

Execute GraphQL query

  • With shortcuts:
    • Sync: data = hasura(...)
    • Async: data = await ahasura(...)
  • Without shortcuts:
    • Sync: data = hasura.gql(...)
    • Async: data = await hasura.agql(...)
  • Args:
    • query: str - GraphQL query, e.g. query { item { id }}
    • auth: str - Either ADMIN or value of Authorization header, e.g. Bearer {JWT}
    • headers: Optional[Dict[str, str]] - Custom headers, if any
    • **variables - Variables used in query, if any
  • Returns: GraphQL response data, e.g. {"item": [{"id": "..."}]}
  • Raises: HasuraError - If JSON response from Hasura contains errors key

Execute SQL query

  • Sync: rows = hasura.sql(...)
  • Async: rows = await hasura.asql(...)
  • Args:
    • query: str - SQL query, e.g. SELECT "id" FROM "item"
    • headers: Optional[Dict[str, str]] - Custom headers, if any
  • Returns:
    • Rows selected by SELECT query, e.g. [{"id": "..."}]
    • Or [{"ok": True}] for non-SELECT query
  • Raises: HasuraError - If JSON response from Hasura contains error key

Auth

  • SQL queries are admin-only
  • GraphQL queries can use both admin and non-admin auth
  • auth=ADMIN is not default, because:
    • sudo is not default
    • It's easy to forget to propagate Authorization header of the user to Hasura
    • Declarative Hasura permissions are better than checking permissions in Python
    • When we set Hasura permissions, we should test them for each role supported

How to test

test_item.py

from typing import Any, Dict

from ahasura import Hasura, HasuraError
import pytest


def test_reader_reads_item_ok(
    hasura: Hasura,
    reader_auth: str,
    ok_item: Dict[str, Any],
) -> None:
    data = hasura(
        """
        query($id: uuid!) {
            item_by_pk(id: $id) {
                name
            }
        }
        """,
        auth=reader_auth,
        id=ok_item["id"],
    )

    item = data["item_by_pk"]
    assert item["name"] == "Some name"


def test_error(hasura: Hasura, reader_auth: str) -> None:
    with pytest.raises(HasuraError) as error:
        hasura("bad query", auth=reader_auth)

    assert error.value.response == {"errors": [...]}

conftest.py

from typing import Any, Dict, Generator, List

from ahasura import ADMIN, Hasura
import jwt
import pytest

_TABLE_NAMES = ["item"]


@pytest.fixture(scope="session")
def hasura() -> Hasura:
    return Hasura("http://localhost:8080", admin_secret="fake secret")


@pytest.fixture(scope="session")
def reader_auth() -> str:
    decoded_token = ...
    encoded_token = jwt.encode(decoded_token, "")
    return f"Bearer {encoded_token}"


@pytest.fixture(scope="session")
def test_row_ids() -> List[str]:
    """
    When a test function creates a row in any table,
    it should append this `row["id"]` to `test_row_ids`

    UUIDs are unique across all tables with enough probability
    """
    return []


@pytest.fixture(scope="function")
def ok_item(hasura: Hasura, test_row_ids: List[str]) -> Dict[str, Any]:
    data = hasura(
        """
        mutation($item: item_insert_input!) {
            insert_item_one(object: $item) {
                id
                name
            }
        }
        """,
        auth=ADMIN,
        name="Some name",
    )
    item: Dict[str, Any] = data["insert_item_one"]
    test_row_ids.append(item["id"])
    return item


@pytest.fixture(scope="function", autouse=True)
def cleanup(hasura: Hasura, test_row_ids: List[str]) -> Generator[None, None, None]:
    """
    When the test function ends,
    this autouse fixture deletes all test rows from all tables
    """
    yield

    if test_row_ids:
        for table_name in _TABLE_NAMES:
            hasura(
                """
                mutation($ids: [uuid!]!) {
                    delete_{table_name}(where: {id: {_in: $ids}}) {
                        affected_rows
                    }
                }
                """.replace(
                    "{table_name}", table_name
                ),
                auth=ADMIN,
                ids=test_row_ids,
            )
        test_row_ids.clear()

Metadata

Release files for ahasura 1.4.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ahasura 1.4.2
File Size Uploaded Provenance Actions
ahasura-1.4.2.tar.gz 5.9 kB Without provenance Details for ahasura-1.4.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ahasura 1.4.2
File Interpreter ABI Platform Actions
ahasura-1.4.2-py3-none-any.whl Python 3 none any Details for ahasura-1.4.2-py3-none-any.whl

Total release size: 11.6 kB

Release files / ahasura-1.4.2.tar.gz

Download URL ahasura-1.4.2.tar.gz
Size 5.9 kB
Tags Source
SHA-256 checksum
How to use checksums
c19e907c3685b8c46d00311e0dcbe4dbd887c17338a280f963b79f4fbd6ab827
BLAKE2b-256 checksum
How to use checksums
4f3630a052933e2bab74f41c0a67b31f855ae6134045e5e92f20690c389e2c85
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.13 CPython/3.8.13 Linux/5.4.109+

Release files / ahasura-1.4.2-py3-none-any.whl

Download URL ahasura-1.4.2-py3-none-any.whl
Size 5.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d4c8822be1828bbbded1a53decad03f818bacad721402e38e4ededcc3a2c52ed
BLAKE2b-256 checksum
How to use checksums
15b5f4e3c28f52ecfff4b634053c91fc056f98c8ddfefc4b56e34a3111078044
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.13 CPython/3.8.13 Linux/5.4.109+

Security for ahasura 1.4.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.4.2 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page