Skip to main content

Easy to use abstraction layer for GraphQL, with support for Django ORM.

Project description

Easy GraphQL Server

easy_graphql_server provides an easy way to expose a database in GraphQL, using ORM models and web frameworks (so far only Django is supported, but SQLAlchemy & Flask will soon come).

easy_graphql_server can be installed from PyPI using the built-in pip command:

pip install easy-graphql-server

Usage

Expose methods

There are three ways to expose a method in the GraphQL schema.

Calling Schema.expose_query() and Schema.expose_mutation()

import easy_graphql_server as egs

schema = egs.Schema()

schema.expose_query(
    name = 'foo',
    input_format = {
        'input_string': egs.Required(str),
        'input_integer': int,
    },
    output_format = {
        'output_string': str,
        'output_integer': int,
    },
    method = lambda input_string, input_integer=None: {
        'output_string': 2 * input_string,
        'output_integer': None if input_integer is None else 2 * input_integer,
    },
)

_internal_value = 0
def bar_mutation_method(value=None, increment_value=None):
    if value is not None:
        _internal_value = value
    if increment_value is not None:
        _internal_value += increment_value
    return {
        'value': _internal_value,
    }

schema.expose_mutation(
    name = 'bar',
    input_format = {
        'input_string': egs.Required(str),
        'input_integer': int,
    },
    output_format = {
        'output_string': str,
        'output_integer': int,
    },
    method = bar_mutation_method,
)

Subclassing Schema.ExposedQuery and Schema.ExposedMutation

import easy_graphql_server as egs

schema = egs.Schema()

class FooQuery(schema.ExposedQuery):
    name = 'foo'
    input_format = {
        'input_string': egs.Required(str),
        'input_integer': int,
    }
    output_format = {
        'output_string': str,
        'output_integer': int,
    }
    @staticmethod
    def method(input_string, input_integer=None):
        return {
            'output_string': 2 * input_string,
            'output_integer': None if input_integer is None else 2 * input_integer,
        }

class BarMutation(schema.ExposedMutation):
    name = 'bar'
    _internal_value = 0
    input_format = {
        'value': int,
        'increment_value': int,
    }
    output_format = {
        'value': int,
    }
    @classmethod
    def method(cls, value=None, increment_value=None):
        if value is not None:
            cls._internal_value = value
        if increment_value is not None:
            cls._internal_value += increment_value
        return {
            'value': cls._internal_value,
        }

Subclassing easy_graphql_server.ExposedQuery and easy_graphql_server.ExposedMutation

This is very similar to the previous way.

import easy_graphql_server as egs

class FooQuery(schema.ExposedQuery):
    name = 'foo'
    input_format = {
        'input_string': egs.Required(str),
        'input_integer': int,
    }
    output_format = {
        'output_string': str,
        'output_integer': int,
    }
    @staticmethod
    def method(input_string, input_integer=None):
        return {
            'output_string': 2 * input_string,
            'output_integer': None if input_integer is None else 2 * input_integer,
        }

class BarMutation(schema.ExposedMutation):
    name = 'bar'
    _internal_value = 0
    input_format = {
        'value': int,
        'increment_value': int,
    }
    output_format = {
        'value': int,
    }
    @classmethod
    def method(cls, value=None, increment_value=None):
        if value is not None:
            cls._internal_value = value
        if increment_value is not None:
            cls._internal_value += increment_value
        return {
            'value': cls._internal_value,
        }

schema = egs.Schema()
schema.expose(FooQuery)
schema.expose(BarMutation)

Available options for exposing methods

The same options can be passed either as class attributes for subclasses of Schema.ExposedQuery and Schema.ExposedMutation, or as keyword arguments to Schema.expose_query() and Schema.expose_mutation() methods.

Options for queries and mutations are the same.

  • name is the name under which the method shall be exposed

  • method is the callback function of your choice

  • input_format is the input format for the GraphQL method, passed as a (possibly recursive) mapping; if unspecified or None, the defined GraphQL method will take no input; the mapping keys are

  • output_format is the output format of the GraphQL method, passed as a (possibly recursive) mapping or as a list containing one mapping

  • pass_graphql_selection can either be a bool or a str; if set to True, the graphql_selection parameter will be passed to the callback method, indicating which fields are requested for output; if set to a str, the given string will be the name of the keyword parameter passed to the callback method instead of graphql_selection

  • pass_graphql_path can either be a bool or a str; if set to True, the graphql_path parameter will be passed to the callback method, indicating as a list[str] the GraphQL path in which the method is being executed; if set to a str, the given string will be the name of the keyword parameter passed to the callback method instead of graphql_path

  • pass_authenticated_user can either be a bool or a str; if set to True, the authenticated_user parameter will be passed to the callback method, indicating the user authenticated in the source HTTP request (or None if the request was unauthenticated); if set to a str, the given string will be the name of the keyword parameter passed to the callback method instead of authenticated_user

  • require_authenticated_user is a bool indicating whether or not authentication is required for the exposed method

Expose ORM models

What does it do?

For instance, exposing a model called Thing will expose the following queries...

  • thing: fetch a single instance of the model given its unique identifier
  • things: fetch a collection of model instances, given filtering criteria, or unfiltered, paginated or not

...and mutations:

  • create_thing: create a single instance of the model given the data to be inserted
  • update_thing: update a single instance of the model given its unique identifier and a mapping the new data to apply
  • delete_thing: delete a single instance of the model given its unique identifier

Calling Schema.expose_model()

import easy_graphql_server
from my_django_application.models import Person

schema = easy_graphql_server.Schema()

schema.expose_model(
	orm_model=Person,
	plural_name='people',
	can_expose=('id', 'first_name', 'last_name'),
	cannot_write=('id',),
)

Subclassing Schema.ExposedModel

import easy_graphql_server
from my_django_application.models import Person

schema = easy_graphql_server.Schema()

class ExposedPerson(schema.ExposedModel):
		orm_model = Person
		plural_name = 'people'
		can_expose = ('id', 'first_name', 'last_name')
		cannot_write = ('id',)

Subclassing easy_graphql_server.ExposedModel

This is very similar to the previous way.

import easy_graphql_server
from my_django_application.models import Person

schema = easy_graphql_server.Schema()

class ExposedPerson(easy_graphql_server.ExposedModel):
		orm_model = Person
		plural_name = 'people'
		can_expose = ('id', 'first_name', 'last_name')
		cannot_write = ('id',)

schema = easy_graphql_server.Schema()
schema.expose(ExposedPerson)

Available options for exposing models

The same options can be passed either as class attributes for subclasses of ExposedModel, or as keyword arguments to Schema.expose_model() method.

  • cannot_expose is either a bool, or a tuple[str]; defaults to False; if set to True, no query or mutation method will be exposed for the model; if set to a list of field names, those fields will be excluded from every query and mutation

  • can_create is either a bool, or a tuple[str]; defaults to True; if set to False, no creation mutation method will be exposed for the model; if set to a list of field names, only those fields will be possible field values passed at insertion time to create_...

  • cannot_create is either a bool, or a tuple[str]; defaults to False; if set to True, no creation mutation method will be exposed for the model; if set to a list of field names, those fields will be excluded from possible field values passed at insertion time to create_...

  • can_read is either a bool, or a tuple[str]; defaults to True; if set to False, no query method will be exposed for the model; if set to a list of field names, only those fields will be exposed for the ... (show one instance) and ...s (show a collection of instances) queries (it also defines which fields are available as mutations results)

  • cannot_read is either a bool, or a tuple[str]; defaults to False; if set to True, no query method will be exposed for the model; if set to a list of field names, only those fields will be exposed for the ... (show one instance) and ...s (show a collection of instances) queries (it also defines which fields are not available as mutations results)

  • can_update is either a bool, or a tuple[str]; defaults to True; if set to True, no delete_... mutation method will be exposed for the model; if set to a list of field names, those fields will be the only possible keys for the _ parameter (new data for instance fields) of the update_... mutation

  • cannot_update is either a bool, or a tuple[str]; defaults to False; if set to True, no update_... mutation method will be exposed for the model; if set to a list of field names, those fields will be excluded from possible keys for the _ parameter (new data for instance fields) of the update_... mutation

  • can_delete is a bool; defaults to True; if set to False, no delete_... mutation method will be exposed for the model

  • cannot_delete is a bool; defaults to False; if set to True, no delete_... mutation method will be exposed for the model

  • only_when_child_of is either None (model does not have to be nested to be exposed), True (the model is not exposed if not nested), an ORM model class, or a tuple/list/set of ORM model classes (the defined model can only be exposed when nested directly under one of models passed as only_when_child_of parameter)

  • require_authenticated_user is a bool indicating whether or not authentication is required for the exposed method

  • has_permission is either None, or a callback method returning a bool (True if operation is authorized, False otherwise), and taking as parameters authenticated_user (self-explanatory), operation (a value of the easy_graphql_server.Operation enum: CREATE, READ, UPDATE or DELETE) and data (new data, only applies to CREATE and UPDATE)

  • filter_for_user is either None, or a callback method returning a queryset, and taking as parameters queryset and authenticated_user

Perform GraphQL queries

If you want to perform GraphQL queries on the schema without going through a schema, you can use Schema.execute(). This method can take the following parameters:

  • query: the GraphQL query, in the form of a str
  • variables: variables to go along with the query (optional), as a dict[str,Any]
  • operation_name: name of the operation to be executed within the query (optional)
  • authenticated_user: parameter that will be passed to the callback functions of GraphQL methods that require it (optional)
  • serializable_output: the output will be rendered as JSON-serializable dict, instead of a graphql.execution.execute.ExecutionResult instance

Credits and history

The easy_graphql_server library was originally a subproject within the Bridger development team, to provide an easy way to expose database models with GraphQL using Graphene.

The project was then rewritten with graphq-core and Graphene was dropped.

License

easy_graphql_server is under MIT license.

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

easy_graphql_server-0.9.31.tar.gz (41.0 kB view hashes)

Uploaded Source

Built Distribution

easy_graphql_server-0.9.31-py3-none-any.whl (46.4 kB view hashes)

Uploaded Python 3

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