Metadata-Version: 2.4
Name: gql-builder
Version: 0.9.4
Summary: Build GraphQL queries in Python.
Keywords: GraphQL
License-Expression: LGPL-3.0-or-later
License-File: LICENSE
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
Classifier: Topic :: Utilities
Requires-Dist: graphql-core>=3.2.12
Requires-Python: >=3.14
Project-URL: Homepage, https://codeberg.org/dan-passaro/gql-builder
Description-Content-Type: text/markdown

<!--
SPDX-FileCopyrightText: 2026 Dan Passaro

SPDX-License-Identifier: LGPL-3.0-or-later
-->
# gql-builder

Tools for generating GraphQL queries in Python using a hacky builder.

## Table of contents

<!--TOC-->

- [gql-builder](#gql-builder)
  - [Table of contents](#table-of-contents)
  - [Installation](#installation)
  - [Usage](#usage)
    - [Field arguments](#field-arguments)
    - [Named queries and query arguments](#named-queries-and-query-arguments)
    - [Variable default values](#variable-default-values)
    - [Fragments and aliases](#fragments-and-aliases)
    - [Non-nullable types, inline fragments](#non-nullable-types-inline-fragments)
    - [Directives](#directives)
    - [Mutations and subscriptions](#mutations-and-subscriptions)
  - [License](#license)

<!--TOC-->

## Installation

This package is available on PyPI so installation is straightforward:

```
pip install gql-builder
```

## Usage

In order to query a GraphQL field, import the `g` name from `gql_builder`. Typically, Python subscripts are used in place of the curly braces in GraphQL.

    >>> from gql_builder import g
    >>> print(g.hero[
    ...     g.name
    ... ])
    {
      hero {
        name
      }
    }

### Field arguments

Fields can be called like Python methods to provide GraphQL field arguments:

    >>> print(g.human(id="1000")[
    ...     g.name,
    ...     g.height,
    ... ])
    {
      human(id: "1000") {
        name
        height
      }
    }

### Named queries and query arguments

To create named queries, give the name as an attribute of `Gql.query`,
e.g. `Gql.query.MyQuery [ g.someField ]`. To refer to a GraphQL variable,
prefix the field with the Python `~` operator; this will produce the GraphQL
`$` symbol.

    >>> from gql_builder import Gql
    >>> print(Gql.query.HeroNameAndFriends(episode=g.Episode)[
    ...     g.hero(episode=~g.episode)[
    ...         g.name,
    ...         g.friends[
    ...             g.name,
    ...         ],
    ...     ],
    ... ])
    query HeroNameAndFriends($episode: Episode) {
      hero(episode: $episode) {
        name
        friends {
          name
        }
      }
    }


### Variable default values

GraphQL uses `:` to declare arguments and `=` to assign defaults. In Python this
syntax isn't available; this can only be used in actual Python function
definitions. `gql-builder` doesn't use function definitions to notate GraphQL
queries, but function calls, so instead the `<=` operator has been repurposed to
provide default values, because it looks like an arrow.

    >>> print(Gql.query.HeroNameAndFriends(episode=g.Episode <= g.JEDI) [
    ...     g.hero(episode=~g.episode)[
    ...         g.name,
    ...         g.friends[
    ...             g.name,
    ...         ],
    ...     ]
    ... ])
    query HeroNameAndFriends($episode: Episode = JEDI) {
      hero(episode: $episode) {
        name
        friends {
          name
        }
      }
    }

### Fragments and aliases

Fragments can be defined by calling `Gql` as a function to provide multiple
toplevel definitions, and having one of the definitions be a `Gql.fragment`
attribute. You can reference fragments using `g(...).fragmentName`. Aliases are
defined with the `<<` operator.

    >>> print_skip_blank_lines(Gql(
    ...     Gql.query.HeroComparison(first=g.Int <= 3)[
    ...         g.leftComparison << g.hero(episode=g.EMPIRE)[
    ...             g(...).comparisonFields,
    ...         ],
    ...         g.rightComparison << g.hero(episode=g.JEDI)[
    ...             g(...).comparisonFields,
    ...         ],
    ...     ],
    ...
    ...     Gql.fragment.comparisonFields(on=g.Character)[
    ...         g.name,
    ...         g.friendsConnection(first=~g.first)[
    ...             g.totalCount,
    ...             g.edges[
    ...                 g.node[
    ...                     g.name,
    ...                 ],
    ...             ],
    ...         ],
    ...     ]
    ... ))
    query HeroComparison($first: Int = 3) {
      leftComparison: hero(episode: EMPIRE) {
        ...comparisonFields
      }
      rightComparison: hero(episode: JEDI) {
        ...comparisonFields
      }
    }
    fragment comparisonFields on Character {
      name
      friendsConnection(first: $first) {
        totalCount
        edges {
          node {
            name
          }
        }
      }
    }

(Note that there's a blank line between toplevel GraphQL definitions, but
they're stripped in this example because Python's doctest breaks on blank lines
in output.)

### Non-nullable types, inline fragments

Non-nullable types can be notated using `('!')` after the `g.identifierName`
syntax, e.g. `g.String('!')`. Inline fragments can be created using
`g(..., on=g.SomeType) [ g.someField ]` syntax.

    >>> print(Gql.query.HeroForEpisode(ep=g.Episode('!'))[
    ...     g.hero(episode=~g.ep)[
    ...         g.name,
    ...         g(..., on=g.Droid)[
    ...             g.primaryFunction
    ...         ],
    ...         g(..., on=g.Human) [
    ...             g.height
    ...         ],
    ...     ]
    ... ])
    query HeroForEpisode($ep: Episode!) {
      hero(episode: $ep) {
        name
        ... on Droid {
          primaryFunction
        }
        ... on Human {
          height
        }
      }
    }

### Directives

Directives are supported by adding `.__directive_name` to the field it's
attached to. Arguments supplied are passed through to the directive, with the
added rule that the trailing underscore is removed if present, which allows
directive arguments to have names that are Python keywords.

    >>> print(Gql.query.Hero(episode=g.Episode, withFriends=g.Boolean('!'))[
    ...     g.hero(episode=~g.episode)[
    ...         g.name,
    ...         g.friends.__include(if_=~g.withFriends)[
    ...             g.name,
    ...         ]
    ...     ]
    ... ])
    query Hero($episode: Episode, $withFriends: Boolean!) {
      hero(episode: $episode) {
        name
        friends @include(if: $withFriends) {
          name
        }
      }
    }

### Mutations and subscriptions

Mutations and subscriptions have all the same rules as queries, they're simply
defined using `Gql.mutation` and `Gql.subscription`.

    >>> print(Gql.mutation.CreateReviewForEpisode(ep=g.Episode('!'), review=g.ReviewInput('!'))[
    ...     g.createReview(episode=~g.ep, review=~g.review)[
    ...         g.stars,
    ...         g.commentary,
    ...     ]
    ... ])
    mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
      createReview(episode: $ep, review: $review) {
        stars
        commentary
      }
    }

## License

Copyright (C) 2026 Dan Passaro

This program is free software, licensed under the GNU Lesser General Public
License v3.0 or later (`LGPL-3.0-or-later`). See the [LICENSE](LICENSE) file
for the full text.
