Cannula Documentation

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!

Listen to me talk about GraphQL:

Installation

Requirements:

Use pip:

$ 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}))

Dataloaders

TODO: example dataloader

Testing Your Code

Since GraphQL is typed it is trivial to mock the responses to any Query or Mutation. Cannula provides a MockMiddleware which can mock all types or only select few to provide flexibility when writing your tests. Here is a small example:


import cannula
from cannula.middleware import MockMiddleware

schema = cannula.gql("""
  type Brocoli {
    taste: String
  }
  type Message {
    text: String
    number: Int
    float: Float
    isOn: Boolean
    id: ID
    brocoli: Brocoli
  }
  type Query {
    mockity: [Message]
  }
""")

sample_query = cannula.gql("""{
  mockity {
    text
    number
    float
    isOn
    id
    brocoli {
      taste
    }
  }
}
""")

default = cannula.API(
  __name__,
  schema=schema,
  middleware=[
    MockMiddleware()
  ],
)


print(f'''
  Results with the default 'mock_all=True'. Since the result
  is a list you will get a random number of results unless
  you specify '__list_length' in mock_objects:
  {default.call_sync(sample_query).data}
''')


custom_mocks = {
  'String': 'This will be used for all Strings',
  'Int': 42,
  'Brocoli': {
    'taste': "Delicious"
  },
  'Query': {
    'mockity': [
      {'isOn': False, 'brocoli': {}}
    ]
  }
}

custom = cannula.API(
  __name__,
  schema=schema,
  middleware=[
    MockMiddleware(
      mock_objects=custom_mocks,
      mock_all=True
    ),
  ],
)

print(f'''
  Custom `mock_objects` with `mock_all=True` will return
  a fake result for every field:
  {custom.call_sync(sample_query).data}
''')

limited_mocks = cannula.API(
  __name__,
  schema=schema,
  middleware=[
    MockMiddleware(
      mock_objects=custom_mocks,
      mock_all=False
    ),
  ],
)

print(f'''
  Limited mocks with `mock_all=False` will only return
  fake results for fields mocked in `mock_objects`:
  {limited_mocks.call_sync(sample_query).data}
''')

Indices and tables