/GraphQLRequests

Primary LanguagePythonMIT LicenseMIT

gqlrequests - Build GraphQL query-strings automagically

✅ Create queries from pydantic models

✅ Create queries from annotated classes

✅ Dynamically select query fields


Note: gql's DSL module solves exactly what this module is supposed to solve, but is more mature and has a larger developer base backing the development of it. gqlrequests is still in experimental phases and often undergoes large breaking changes, so if you use this, I highly recommend pinning this package dependency to its exact version and manually update it if needed. For a more stable dynamic query composer, use gql's DSL module.

Ariadne's code generation tool can also be used to generate pydantic classes from a graphql endpoint.

Pytests and Coverage Code Quality codecov

A dynamic, pythonic way to build queries instead of using large multiline strings.

import gqlrequests

# Define the type
class Example(gqlrequests.QueryBuilder):
    age: int
    name: str

# Or with pydantic
class ExampleModel(BaseModel):
    age: int
    name: str

Example = gqlrequests.from_pydantic(ExampleModel)

# Dynamically add or remove fields to the type
Example.friends = list[Example]
Example.age = None  # (This removes the field)

# Create a builder instance:
example_builder = Example()

# Dynamically select fields and set function name for when it's used to create a function query
search = Example(fields=["name"], func_name="search")

# Create a function query with arguments
search_query = search(name="Anna")

# Dynamically add or remove fields to be built
search.name = None  # Doesn't build the name field after all
search.friends = search(name="Anna")

# Build the query string
search.build(indents=2)
{
  search(name="Anna") {
    name
  }
}

More examples

import gqlrequests

class Episode(gqlrequests.QueryBuilder):
    name: str
    length: float

class Character(gqlrequests.QueryBuilder):
    name: str
    appearsIn: list[Episode]

print(Character().build())
# {
#     name
#     appearsIn {
#         name
#         length
#     }
# }

print(Character(fields=["name"]).build())
# {
#     name
# }

print(Character().build(indents=2)) # Default indent is 4
# {
#   name
#   appearsIn {
#     name
#     length
#   }
# }

getCharacter = Character(func_name="getCharacter")
print(getCharacter(name="Luke").build())
# getCharacter(name: "Luke") {
#     name
#     appearsIn {
#         name
#         length
#     }
# }

episode_func = Episode(func_name="appearsIn")

characters_query = Character()
characters_query.appearsIn = episode_func(minLength=5)

print(characters_query.build())
# {
#     name
#     appearsIn(minLength: 5) {
#         name
#         length
#     }
# }

from pydantic import BaseModel

class ExampleModel(BaseModel):
    age: int
    name: str

ExampleQueryBuilder = gqlrequests.from_pydantic(ExampleModel)

print(ExampleQueryBuilder().build())

Edge cases

Some attributes are reserved keywords in Python, such as from, is and not. These cannot be referenced to by property like this: some_query_result.from. This can be circumvented by adding leading or trailing underscores, then passing the strip_underscores argument to the build method.

class Time(gqlrequests.QueryBuilder):
    _from: int
    _to: int
    value: float

print(Time().build(strip_underscores=True))
# {
#     from
#     to
#     value
# }

Other features that are not yet implemented:

print(Character)
# type Character {
#     name: String
#     appearsIn: [Episode]
# }
#

✅ Query validation while developing in your IDE