Metadata-Version: 2.4
Name: gql-builder
Version: 0.9.2
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
-->

Table of Contents
=================

* [gql-builder](#gql-builder)
  * [Installation](#installation)
  * [Quick introduction](#quick-introduction)
    * [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)

# gql-builder

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

## Installation

This package is available on PyPI so installation is straightforward:

```
pip install gql-builder
```

## Quick introduction

In order to use a GraphQL constant, 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,
preface the constant 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(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
      }
    }
    <BLANKLINE>
    fragment comparisonFields on Character {
      name
      friendsConnection(first: $first) {
        totalCount
        edges {
          node {
            name
          }
        }
      }
    }

(Note that the `<BLANKLINE>` in the above example is actually a blank line in
practice, but is written out here so that doctest can execute this example
succesfully.)

### 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=some_type) [ 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 any trailing underscore is removed, which allows the directive
arguments to use 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.
