Skip to main content

Async GraphQL Helper Library

Project description

Cannula

CircleCI Documentation Status

GraphQL for people who like Python!

Why Cannula?

We wanted to make the world a better place, but we are programmers so we settled on making the web fun again. Too much attention has been given to Javascript client libraries. They all seem to compete on size and speed and features but most of them do not solve any of the actual problems you have. So while the todo application is quick and easy to follow the hard parts take a long time to complete.

Now a days if you want a fancy single page application you need to invest a good week or so planning out all the tools you will need to assemble your site. Every decision is full of sorrow and doubt as you google for the latest trends or how to setup unit tests. Or searching for a bootstrapped version of the library you like.

Using GraphQL you can simplify your web application stack and reduce dependencies to achieve the same customer experience without regret. By using just a few core libraries you can increase productivity and make your application easier to maintain.

Our Philosophy:

  1. Make your site easy to maintain.
  2. Document your code.
  3. Don't lock yourself into a framework.
  4. Be happy!

Installation

Requires Python 3.6 or greater! The only dependency is graphql-core-next.

pip3 install cannula

Quick Start

Here is a small hello world example:

import logging
import typing
import sys

import cannula
from cannula.middleware import DebugMiddleware

SCHEMA = cannula.gql("""
  type Message {
    text: String
  }
  type Query {
    hello(who: String): Message
  }
""")

logging.basicConfig(level=logging.DEBUG)

api = cannula.API(
  __name__,
  schema=SCHEMA,
  middleware=[
    DebugMiddleware()
  ]
)


class Message(typing.NamedTuple):
    text: str


# The query resolver takes a source and info objects
# and any arguments defined by the schema. Here we
# only accept a single argument `who`.
@api.resolver('Query')
async def hello(source, info, who):
    return Message(f"Hello, {who}!")

# Pre-parse your query to speed up your requests.
# Here is an example of how to pass arguments to your
# query functions.
SAMPLE_QUERY = cannula.gql("""
  query HelloWorld ($who: String!) {
    hello(who: $who) {
      text
    }
  }
""")


who = 'world'
if len(sys.argv) > 1:
    who = sys.argv[1]

print(api.call_sync(SAMPLE_QUERY, variables={'who': who}))

Now you should see the results if you run the sample on the command line:

$ python3 examples/hello.py
DEBUG:asyncio:Using selector: KqueueSelector
DEBUG:cannula.schema:Adding default empty Mutation type
DEBUG:cannula.middleware.debug:Resolving Query.hello expecting type Message
DEBUG:cannula.middleware.debug:Field Query.hello resolved: Message(text='Hello, world!') in 0.000108 seconds
DEBUG:cannula.middleware.debug:Resolving Message.text expecting type String
DEBUG:cannula.middleware.debug:Field Message.text resolved: 'Hello, world!' in 0.000067 seconds
ExecutionResult(
  data={'hello': {'text': 'Hello, world!'}},
  errors=None
)

$ python3 examples/hello.py Bob
DEBUG:asyncio:Using selector: KqueueSelector
DEBUG:cannula.schema:Adding default empty Mutation type
DEBUG:cannula.middleware.debug:Resolving Query.hello expecting type Message
DEBUG:cannula.middleware.debug:Field Query.hello resolved: Message(text='Hello, Bob!') in 0.000104 seconds
DEBUG:cannula.middleware.debug:Resolving Message.text expecting type String
DEBUG:cannula.middleware.debug:Field Message.text resolved: 'Hello, Bob!' in 0.000101 seconds
ExecutionResult(
  data={'hello': {'text': 'Hello, Bob!'}},
  errors=None
)

But what about Django integration or flask?

# pip install channels, Django
import cannula
from channels.db import database_sync_to_async
from django.contrib.auth.models import User

schema = cannula.gql("""
  type User {
    username: String   # Only expose the fields you actually use
    first_name: String
    last_name: String
    made_up_field: String
  }
  extend type Query {
    getUserById(user_id: String): User
  }
""")

@api.query()
async def getUserById(source, info, user_id):
    return await get_user(user_id)

@database_sync_to_async
def get_user(user_id):
    return User.objects.get(pk=user_id)

@api.resolve('User')
async def made_up_field(source, info):
    return f"{source.get_full_name()} is a lying lier there is no 'made_up_field'"

Since GraphQL is agnostic about where or how you store your data all you need to do is provide a function to resolve a query. The results you return just need to match the schema and you are done.

Django and sqlalchemy already provide tools to query the database. And they work quite well. Or you may choose to use an async database library to make concurrent requests work even better. Try them all and see what works best for your team and your use case.

Examples and Documentation

Documentation

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

cannula-0.0.2.tar.gz (18.6 kB view details)

Uploaded Source

Built Distribution

cannula-0.0.2-py3-none-any.whl (23.1 kB view details)

Uploaded Python 3

File details

Details for the file cannula-0.0.2.tar.gz.

File metadata

  • Download URL: cannula-0.0.2.tar.gz
  • Upload date:
  • Size: 18.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.32.0 CPython/3.6.8

File hashes

Hashes for cannula-0.0.2.tar.gz
Algorithm Hash digest
SHA256 27a4a940fb19a34dc427befc5e6230f0233d31aac7a623130edbf084802d4da2
MD5 3544ee6f5476b74e692b2602e9e6dfec
BLAKE2b-256 dd03b3d86660879d792b27eb51e49daf8219b0fce78217037c76e77a41875115

See more details on using hashes here.

File details

Details for the file cannula-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: cannula-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 23.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.32.0 CPython/3.6.8

File hashes

Hashes for cannula-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 83b37cfcbed4e231c4e9703498d90ab8bdb0c2ff07a1fab3cd33914a8221ee95
MD5 81cb28e2fd12c677bf4accfe650329dd
BLAKE2b-256 14a56516e808e5edb4f0e8356d95cd4809aa1444f750e579584e474a1ca30e29

See more details on using hashes here.

Supported by

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