Skip to main content

A Python GraphQL library that makes use of type hinting and concurrency support with the new async/await syntax.

Project description

TypeGQL

A Python GraphQL library that makes use of type hinting and concurrency support with the new async/await syntax.

DISCLAIMER

This library is still in it’s infancy, so use with caution and feel free to contribute.

Installation

pip install typegql

Usage

The following demonstrates how to use typegql for implementing a GraphQL API for a library of books. The example can be found in typegql/core/examples and you can run it with Sanic by executing python <path_to_example>/server.py

Define your query

from typing import List
from typegql.core.graph import Graph, Connection
from typegql.examples.library.types import Author, Category
from typegql.examples.library.types import Book
from typegql.examples.library import db

class Query(Graph):
    books: List[Book] = Field()
    authors: List[Author] = Field()
    categories: List[Category] = Field()

    books_connection: Connection[Book] = Field(description='Relay connection')

    async def resolve_authors(self, info, **kwargs):
        return db.get('authors')

    async def resolve_books(self, info, **kwargs):
        return db.get('books')

    async def resolve_categories(self, info, **kwargs):
        return db.get('categories')

   async def resolve_books_connection(self, info, **kwargs):
        data = db.get('books')
        return {
            'edges': [{
                'node': node
            } for node in data]}

Define your types

from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal
from enum import Enum
from typing import List

from typegql import Field, ID, OptionalField, ReadonlyField
from typegql.core.graph import Graph
from examples.library import db


class Gender(Enum):
    MALE = 'male'
    FEMALE = 'female'


class GeoLocation(Graph):
latitude: Decimal = Field()
longitude: Decimal = Field()

def __init__(self, latitude, longitude):
    self.latitude = latitude
    self.longitude = longitude


@dataclass
class Author(Graph):
    """Person that is usually a writer"""

    id: ID = ReadonlyField()
    name: str = Field()
    gender: Gender = OptionalField()
    geo: GeoLocation = OptionalField()


@dataclass
class Category(Graph):
    id: ID = ReadonlyField()
    name: str = Field()


@dataclass
class Book(Graph):
    """A book... for reading :|"""

    id: ID = ReadonlyField()
    author_id: ID = Field()
    title: str = OptionalField()
    author: Author = ReadonlyField(description='The author of this book')
    categories: List[Category] = OptionalField()
    published: datetime = OptionalField()
    tags: List[str] = OptionalField()

    def __post_init__(self):
        self.published = datetime.strptime(self.published, '%Y-%m-%d %H:%M:%S')

    async def resolve_author(self, info):
        data = filter(lambda x: x['id'] == self.author_id, db.get('authors'))
        data = next(data)
        author = Author(**data)
        author.gender = Gender[author.gender.upper()].value
        if 'geo' in data:
            author.geo = GeoLocation(**data.get('geo'))
        return author

    async def resolve_categories(self, selections, name=None):
        data = filter(lambda x: x['id'] in self.categories, db.get('categories'))
        for d in data:  # showcasing async generator
            yield Category(**d)

    def resolve_tags(self, selections):
        return ['testing', 'purpose']

Using Fields instead

You can use the following fields to define your GraphQL schema:

Field, InputField, RequiredField, OptionalField

For example:

from typegql import Field, Connection, OptionalField


class Query(Graph):
    authors: Author = Field()
    categories: Category = Field(description="what's this?")
    books_connection: Connection[Book] = OptionalField()

Run your query

from typegql.core.schema import Schema
from examples.library.query import Query


schema = Schema(Query)
query = '''
query BooksConnection {
  books_connection {
    edges {
      node {
        id
        title
        published
        author {
          id
          name
        }
      }
    }
  }
}
'''

async def run():
    result = await schema.run(query)

Client

TypeGQL supports DSL client for working with a GraphQL API. The client automatically converts snake to camelcase. set camelcase=False if this is not desired

pip install typegql[client]

For example:

from typegql.client import Client

async with Client(url, camelcase=True) as client:
    await client.introspection()
    dsl = client.dsl
    query = dsl.Query.books_connection.select(dsl.BooksConnection.total_count)
    doc = dsl.query(query)

    status, result = await client.execute(doc)

Change Log

2.0.9 [2019-10-29]

  • changed the name of an input object from ObjectMuation to ObjectInput

2.0.8 [2019-10-15]

  • allows forward reference between graph types (ie: Book has an author and an Author has books).

    this only works with python 3.7(using from __future__ import annotations, or python 3.8

2.0.6 [2019-06-24]

  • updates uvloop dependency

2.0.5 [2019-04-24]

  • fixed a bug when sending introspection schema

2.0.4 [2019-04-24]

  • updates assert for introspection add message with status and result

  • adds support for enum objects in resolve_field_velue_or_error

2.0.3 [2019-02-08]

  • changes Connection, Edge, Node and PageInfo to interfaces IConnection, IEdge, etc.

  • implements default Connection and PageInfo objects

  • removes has_next, has_previous from PageInfo

2.0.1 [2019-01-19]

  • all properties that don’t have a Field instance assigned to them will be ignored by the Schema

  • updates docs & example to reflect 2.0 changes

  • fixed a bug when using a List argument in mutations

1.0.7 [2018-12-09]

  • bug fixing

  • adds support for camelcase in Client

1.0.1 [2018-11-19]

  • adds support for client DSL

Initial

TODO

  • testing

  • travis

  • more testing

  • please help with testing :|

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

typegql-2.0.11.tar.gz (14.0 kB view details)

Uploaded Source

Built Distribution

typegql-2.0.11-py3-none-any.whl (14.4 kB view details)

Uploaded Python 3

File details

Details for the file typegql-2.0.11.tar.gz.

File metadata

  • Download URL: typegql-2.0.11.tar.gz
  • Upload date:
  • Size: 14.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.0.0 CPython/3.8.0 Darwin/19.0.0

File hashes

Hashes for typegql-2.0.11.tar.gz
Algorithm Hash digest
SHA256 6142d9311bb5b2c5aec187101fe4a61c500e06368744619c4894f6b2f5f36db8
MD5 f09770233c5bfe4c4d4255c2d7723ee9
BLAKE2b-256 aa6ae936f458a0d321ca07c0c9842ff4d72dc835aea81e8fb534b15cc2820114

See more details on using hashes here.

File details

Details for the file typegql-2.0.11-py3-none-any.whl.

File metadata

  • Download URL: typegql-2.0.11-py3-none-any.whl
  • Upload date:
  • Size: 14.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.0.0 CPython/3.8.0 Darwin/19.0.0

File hashes

Hashes for typegql-2.0.11-py3-none-any.whl
Algorithm Hash digest
SHA256 4e047e100e12d215be4da1f1b715c3b5de318447e3159e7872ca3a015d5c1295
MD5 b6db6d081dd804d52aef5284388621c8
BLAKE2b-256 f3b175cf59d1d4db39cc09bdcf92e42dba30ae1f58d066d8780ea9f6b8c4b7ce

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page