A library to help construct a graphql-py server supporting react-relay

Overview

Relay Library for GraphQL Python

GraphQL-relay-py is the Relay library for GraphQL-core.

It allows the easy creation of Relay-compliant servers using GraphQL-core.

GraphQL-Relay-Py is a Python port of graphql-relay-js, while GraphQL-Core is a Python port of GraphQL.js, the reference implementation of GraphQL for JavaScript.

Since version 3, GraphQL-Relay-Py and GraphQL-Core support Python 3.6 and above only. For older versions of Python, you can use version 2 of these libraries.

PyPI version Build Status Coverage Status

Getting Started

A basic understanding of GraphQL and of the GraphQL Python implementation is needed to provide context for this library.

An overview of GraphQL in general is available in the README for the Specification for GraphQL.

This library is designed to work with the the GraphQL-Core Python reference implementation of a GraphQL server.

An overview of the functionality that a Relay-compliant GraphQL server should provide is in the GraphQL Relay Specification on the Relay website. That overview describes a simple set of examples that exist as tests in this repository. A good way to get started with this repository is to walk through that documentation and the corresponding tests in this library together.

Using Relay Library for GraphQL Python (graphql-core)

Install Relay Library for GraphQL Python

pip install graphql-core
pip install graphql-relay

When building a schema for GraphQL, the provided library functions can be used to simplify the creation of Relay patterns.

Connections

Helper functions are provided for both building the GraphQL types for connections and for implementing the resolver method for fields returning those types.

  • connection_args returns the arguments that fields should provide when they return a connection type.

  • connection_definitions returns a connection_type and its associated edgeType, given a name and a node type.

  • connection_from_array is a helper method that takes an array and the arguments from connection_args, does pagination and filtering, and returns an object in the shape expected by a connection_type's resolver function.

  • cursor_for_object_in_connection is a helper method that takes an array and a member object, and returns a cursor for use in the mutation payload.

An example usage of these methods from the test schema:

ship_edge, ship_connection = connection_definitions('Ship', shipType)

factionType = GraphQLObjectType(
    name='Faction',
    description='A faction in the Star Wars saga',
    fields=lambda: {
        'id': global_id_field('Faction'),
        'name': GraphQLField(
            GraphQLString,
            description='The name of the faction.',
        ),
        'ships': GraphQLField(
            ship_connection,
            description='The ships used by the faction.',
            args=connection_args,
            resolve=lambda faction, _info, **args: connection_from_array(
                [getShip(ship) for ship in faction.ships], args),
        )
    },
    interfaces=[node_interface]
)

This shows adding a ships field to the Faction object that is a connection. It uses connection_definitions('Ship', shipType) to create the connection type, adds connection_args as arguments on this function, and then implements the resolver function by passing the array of ships and the arguments to connection_from_array.

Object Identification

Helper functions are provided for both building the GraphQL types for nodes and for implementing global IDs around local IDs.

  • node_definitions returns the Node interface that objects can implement, and returns the node root field to include on the query type. To implement this, it takes a function to resolve an ID to an object, and to determine the type of a given object.
  • to_global_id takes a type name and an ID specific to that type name, and returns a "global ID" that is unique among all types.
  • from_global_id takes the "global ID" created by to_global_id, and returns the type name and ID used to create it.
  • global_id_field creates the configuration for an id field on a node.
  • plural_identifying_root_field creates a field that accepts a list of non-ID identifiers (like a username) and maps then to their corresponding objects.

An example usage of these methods from the test schema:

def get_node(global_id, _info):
    type_, id_ = from_global_id(global_id)
    if type_ == 'Faction':
        return getFaction(id_)
    elif type_ == 'Ship':
        return getShip(id_)
    else:
        return None

def get_node_type(obj, _info, _type):
    if isinstance(obj, Faction):
        return factionType
    else:
        return shipType

node_interface, node_field = node_definitions(get_node, get_node_type)

factionType = GraphQLObjectType(
    name= 'Faction',
    description= 'A faction in the Star Wars saga',
    fields= lambda: {
        'id': global_id_field('Faction'),
    },
    interfaces= [node_interface]
)

queryType = GraphQLObjectType(
    name= 'Query',
    fields= lambda: {
        'node': node_field
    }
)

This uses node_definitions to construct the Node interface and the node field; it uses from_global_id to resolve the IDs passed in in the implementation of the function mapping ID to object. It then uses the global_id_field method to create the id field on Faction, which also ensures implements the node_interface. Finally, it adds the node field to the query type, using the node_field returned by node_definitions.

Mutations

A helper function is provided for building mutations with single inputs and client mutation IDs.

  • mutation_with_client_mutation_id takes a name, input fields, output fields, and a mutation method to map from the input fields to the output fields, performing the mutation along the way. It then creates and returns a field configuration that can be used as a top-level field on the mutation type.

An example usage of these methods from the test schema:

class IntroduceShipMutation:
    def __init__(self, shipId, factionId, clientMutationId=None):
        self.shipId = shipId
        self.factionId = factionId
        self.clientMutationId = clientMutationId

def mutate_and_get_payload(_info, shipName, factionId):
    newShip = createShip(shipName, factionId)
    return IntroduceShipMutation(shipId=newShip.id, factionId=factionId)

shipMutation = mutation_with_client_mutation_id(
    'IntroduceShip',
    input_fields={
        'shipName': GraphQLInputField(
            GraphQLNonNull(GraphQLString)
        ),
        'factionId': GraphQLInputField(
            GraphQLNonNull(GraphQLID)
        )
    },
    output_fields={
        'ship': GraphQLField(
            shipType,
            resolve=lambda payload, _info: getShip(payload.shipId)
        ),
        'faction': GraphQLField(
            factionType,
            resolve=lambda payload, _info: getFaction(payload.factionId)
        )
    },
    mutate_and_get_payload=mutate_and_get_payload
)

mutationType = GraphQLObjectType(
    'Mutation',
    fields=lambda: {
        'introduceShip': shipMutation
    }
)

This code creates a mutation named IntroduceShip, which takes a faction ID and a ship name as input. It outputs the Faction and the Ship in question. mutate_and_get_payload then gets each input field as keyword parameter, performs the mutation by constructing the new ship, then returns an object that will be resolved by the output fields.

Our mutation type then creates the introduceShip field using the return value of mutation_with_client_mutation_id.

Contributing

After cloning this repository from GitHub, we recommend using Poetry to create a test environment. With poetry installed, you do this with the following command:

poetry install

You can then run the complete test suite like this:

poetry run pytest

In order to run only a part of the tests with increased verbosity, you can add pytest options, like this:

poetry run pytest tests/node -vv

In order to check the code style with flake8, use this:

poetry run flake8

Use the tox command to run the test suite with different Python versions and perform all additional source code checks. You can also restrict tox to an individual environment, like this:

poetry run tox -e py37
Comments
  • Wheel for v0.4.5 does not match source code

    Wheel for v0.4.5 does not match source code

    When looking at https://pypi.org/project/graphql-relay/0.4.5/#files there appears to be a mistaken upload of the wheel package to v0.4.5.

    The wheel appears to be newer code than that marked for v.0.4.5 in the source code with the newly updated requirements

    opened by sjdines 20
  • Fix utf-8 error on py 2.7

    Fix utf-8 error on py 2.7

    On our pre-production environment we just now got an error related to unicode encoding in the to_global_id code. I fixed it and added a test that ensures that behaviour works.

    As a reference, this is the error one would see otherwise:

      File "/home/app/apps/killik_ops_dashboard.pex/third_party/python/graphql_relay/node/node.py", line 53, in to_global_id
        return base64(':'.join([type, str(id)]))
    UnicodeEncodeError: 'ascii' codec can't encode character u'\u06ed' in position 39: ordinal not in range(128)
    
    opened by Globegitter 9
  • from_global_id() differs from reference implementation

    from_global_id() differs from reference implementation

    The python from_global_id does not exhibit the same behavior as the reference TypeScript implementation when handling invalid input.

    Examples:

    • from_global_id("invalid") raises binascii.Error
    • from_global_id("") raises TypeError
    • from_global_id(b64encode(b"foo")) raises TypeError

    In the first two cases, the reference implementation returns {type: "", id: ""}. In the third case, it returns {type: "", id: "foo"}. In no case does it throw an error.

    This might seem like nitpicking, but it can cause significant differences in how user input ends up being handled.

    opened by markedwards 6
  • Test errors with graphql-core 3.2.0

    Test errors with graphql-core 3.2.0

    After updating graphql-core to 3.2.0 we're seeing the following tests fail on graphql-relay 3.1.0:

    =================================== FAILURES ===================================
    _______________________ test_correctly_refetches_rebels ________________________
    
        @mark.asyncio
        async def test_correctly_refetches_rebels():
            query = """
              query RebelsRefetchQuery {
                node(id: "RmFjdGlvbjox") {
                  id
                  ... on Faction {
                    name
                  }
                }
              }
            """
            expected = {
                "node": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"}
            }
            result = await graphql(StarWarsSchema, query)
    >       assert result == (expected, None)
    E       AssertionError: assert ExecutionResult(data={'node': None}, errors=[GraphQLError('Support for returning GraphQLObjectType from resolve_type was removed in GraphQL-core 3.2, please return type name instead.', locations=[SourceLocation(line=3, column=9)], path=['node'])]) == ({'node': {'id': 'RmFjdGlvbjox', 'name': 'Alliance to Restore the Republic'}},\n None)
    E         +ExecutionResult(data={'node': None}, errors=[GraphQLError('Support for returning GraphQLObjectType from resolve_type was removed in GraphQL-core 3.2, please return type name instead.', locations=[SourceLocation(line=3, column=9)], path=['node'])])
    E         -({'node': {'id': 'RmFjdGlvbjox', 'name': 'Alliance to Restore the Republic'}}, None)
    E         Full diff:
    E         + ExecutionResult(data={'node': None}, errors=[GraphQLError('Support for returning GraphQLObjectType from resolve_type was removed in GraphQL-core 3.2, please return type name instead.', locations=[SourceLocation(line=3, column=9)], path=['node'])],
    E         - (
    E         -  {'node': {'id': 'RmFjdGlvbjox',
    E         -            'name': 'Alliance to Restore the Republic'}},
    E         -  None,
    E           )
    
    tests/test_star_wars_object_identification.py:41: AssertionError
    _______________________ test_correctly_refetches_empire ________________________
    
        @mark.asyncio
        async def test_correctly_refetches_empire():
            query = """
              query EmpireRefetchQuery {
                node(id: "RmFjdGlvbjoy") {
                  id
                  ... on Faction {
                    name
                  }
                }
              }
            """
            expected = {"node": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}}
            result = await graphql(StarWarsSchema, query)
    >       assert result == (expected, None)
    E       AssertionError: assert ExecutionResult(data={'node': None}, errors=[GraphQLError('Support for returning GraphQLObjectType from resolve_type was removed in GraphQL-core 3.2, please return type name instead.', locations=[SourceLocation(line=3, column=9)], path=['node'])]) == ({'node': {'id': 'RmFjdGlvbjoy', 'name': 'Galactic Empire'}}, None)
    E         +ExecutionResult(data={'node': None}, errors=[GraphQLError('Support for returning GraphQLObjectType from resolve_type was removed in GraphQL-core 3.2, please return type name instead.', locations=[SourceLocation(line=3, column=9)], path=['node'])])
    E         -({'node': {'id': 'RmFjdGlvbjoy', 'name': 'Galactic Empire'}}, None)
    E         Full diff:
    E         - ({'node': {'id': 'RmFjdGlvbjoy', 'name': 'Galactic Empire'}}, None)
    E         + ExecutionResult(data={'node': None}, errors=[GraphQLError('Support for returning GraphQLObjectType from resolve_type was removed in GraphQL-core 3.2, please return type name instead.', locations=[SourceLocation(line=3, column=9)], path=['node'])])
    
    tests/test_star_wars_object_identification.py:73: AssertionError
    ________________________ test_correctly_refetches_xwing ________________________
    
        @mark.asyncio
        async def test_correctly_refetches_xwing():
            query = """
              query XWingRefetchQuery {
                node(id: "U2hpcDox") {
                  id
                  ... on Ship {
                    name
                  }
                }
              }
            """
            expected = {"node": {"id": "U2hpcDox", "name": "X-Wing"}}
            result = await graphql(StarWarsSchema, query)
    >       assert result == (expected, None)
    E       AssertionError: assert ExecutionResult(data={'node': None}, errors=[GraphQLError('Support for returning GraphQLObjectType from resolve_type was removed in GraphQL-core 3.2, please return type name instead.', locations=[SourceLocation(line=3, column=9)], path=['node'])]) == ({'node': {'id': 'U2hpcDox', 'name': 'X-Wing'}}, None)
    E         +ExecutionResult(data={'node': None}, errors=[GraphQLError('Support for returning GraphQLObjectType from resolve_type was removed in GraphQL-core 3.2, please return type name instead.', locations=[SourceLocation(line=3, column=9)], path=['node'])])
    E         -({'node': {'id': 'U2hpcDox', 'name': 'X-Wing'}}, None)
    E         Full diff:
    E         - ({'node': {'id': 'U2hpcDox', 'name': 'X-Wing'}}, None)
    E         + ExecutionResult(data={'node': None}, errors=[GraphQLError('Support for returning GraphQLObjectType from resolve_type was removed in GraphQL-core 3.2, please return type name instead.', locations=[SourceLocation(line=3, column=9)], path=['node'])])
    
    tests/test_star_wars_object_identification.py:90: AssertionError
    
    opened by mweinelt 5
  • Breaking change introduced with a patch by changing module names

    Breaking change introduced with a patch by changing module names

    Problem

    There has been introduced a breaking change since the version 3.1.1. Namely, the modules have been renamed to follow the snake_case naming convention in the commit f15bbb.

    This causes some runtime (import) errors after installing/updating dependencies:

    ModuleNotFoundError: No module named 'graphql_relay.connection.arrayconnection'
    

    in on of the 3rd parties that use this library.

    Yes, it uses from graphql_relay.connection.arrayconnection import ... instead of from graphql_relay import ....

    I have been able to fix this issue by adding graphql-relay as a project dependency and pinning the version to 3.1.0, but this is not the desired solution, as we do not use the library directly in the project.

    Proposed resolution:

    One solution would be to allow the old modules reside in the project and mark them as deprecated. Their sole purpose would be to import the contents of the renamed files and allow them to be imported using the old module name.

    I can create a Pull request with this patch if this is approved, or with any other proposed solution.

    opened by vladr11 4
  • Typing of array_slice as Sequence in connection methods

    Typing of array_slice as Sequence in connection methods

    I'm implementing graphql_relay in a GraphQL implementation to handle Connection responses by passing django.db.models.QuerySet objects to the array_slice argument of connection_from_array_slice(), much in the same way that Graphene does.

    Although I have assured myself that this works, the typing of array_slice as Sequence is a bit disconcerting, since QuerySet does not qualify as a Sequence instance. As far as I can tell, there isn't an appropriate standard type that would solve this, however. The best idea I have would be to define a custom Protocol, but that might pose compatibility challenges for older python installs.

    Is there any appetite to resolve this so that a class like QuerySet type-checks without having to cast or ignore it? Barring that, can it at least be stated that graphql_relay will continue to support QuerySet in this way? I assume that is the intention since Graphene relies on it.

    discussion investigate 
    opened by markedwards 4
  • Provide wheel distribution on PyPI

    Provide wheel distribution on PyPI

    Hi!

    Please could this package also by made available as a wheel on PyPI? https://pypi.python.org/pypi/graphql-relay/0.4.5

    Wheels have the following advantages:

    • Faster installation time
    • Allows the wheel cache to work even when using the new pip hash-checking mode
    • Allows tools to statically inspect the package metadata without having to execute untrusted code.
    • ...and more - see: http://pythonwheels.com

    This package is pure Python and supports both Python 2 and 3, so can be distributed as a "universal wheel": https://packaging.python.org/tutorials/distributing-packages/#wheels

    To save having to pass --universal to setup.py each time, the pref can be saved in setup.cfg:

    [bdist_wheel]
    universal=1
    

    The Python packaging guide recommends using Twine to upload packages: https://packaging.python.org/tutorials/distributing-packages/#uploading-your-project-to-pypi

    So with the setup.cfg as above, the steps for building/uploading a new release are then:

    $ pip install -U twine setuptools wheel
    $ rm -rf dist/ build/
    $ python setup.py sdist bdist_wheel
    $ twine upload dist/*
    

    The PyPI package listing will then display two entries for the new release, one with type "Source" and the other "Python Wheel".

    Many thanks :-)

    opened by edmorley 4
  • Re-add deprecated module 'arrayconnection' for backwards compatibility

    Re-add deprecated module 'arrayconnection' for backwards compatibility

    Adds back the arrayconnection module which simply exports the names from array_connection. This was necessary to maintain backwards compatibility with the previous versions.

    Note: this should be removed after a major version increment.

    opened by vladr11 3
  • Incorrect Optional typing on PageInfo hasPreviousPage and hasNextPage

    Incorrect Optional typing on PageInfo hasPreviousPage and hasNextPage

    The typing of PageInfo.hasPreviousPage and PageInfo.hasNextPage appears to be incorrectly indicated as Optional[bool] here.

    Is there a reason for this, or can it be fixed?

    opened by markedwards 3
  • Incude tests in Python package?

    Incude tests in Python package?

    Hi there,

    I'm building an RPM package for graphql-relay at https://build.opensuse.org, but it would be great, if the unittests were part of the actual Python package on PyPI, so that I could run them after the build process to verify the correct installation.

    Would you mind including them in the next release?

    opened by crazyscientist 3
  • Release notes needed (dependencies breaking snapshots)

    Release notes needed (dependencies breaking snapshots)

    Related: #15

    Graphene's 2.1.7 release on July 15th bumped graphql-relay 0.4.5 to 2.0.

    https://pypi.org/project/graphene/#history

    This is a patch-release that breaks SnapshotTest (mentioned in graphene's documentation: https://docs.graphene-python.org/en/latest/testing/)

    It's not clear what the changes / ramifications / intentions of 0.4.x -> 2.x of relay are. What's necessary in graphene 2.1.7 that https://github.com/graphql-python/graphene/pull/1032 fixed.

    documentation 
    opened by tony 3
  • n+1 pagination

    n+1 pagination

    Related to this issue

    Adds a n+1 strategy for paginating connections which make paginating a lot more performant avoiding the need to call on list() or length() on the iterator (which ends up consuming the entire iterator). We just get the desired amount of results and try to fetch one extra item. If there's a result it means there are more results, otherwise its the end of the iteration/pagination.

    The new method connection_from_list_slice_lazy is optional, so instead of replacing the original one, those who want to use it can override the connection field like this:

    class SmartConnectionField(ConnectionField):
        @classmethod
        def connection_resolver(cls, resolver, connection_type, root, info, **args):
            resolved = resolver(root, info, **args)
    
            if resolved is None:
                resolved = []
    
            connection = connection_from_list_slice_lazy(
                resolved,
                args,
                connection_type=connection_type,
                pageinfo_type=PageInfo,
                edge_type=connection_type.Edge,
            )
    
            connection.iterable = resolved
            return connection
    
    opened by sebastiandev 0
  • Why are nodes optional?

    Why are nodes optional?

    I was trying to dig through this to figure it out, and I apologize if there's a good reason for this.

    It seems to me that a paginated connection (e.g. DjangoConnectionField or DjangoFilterConnectionField) always has edges and nodes. What's the use case for these being nullable?

    The reason I bring this up is because, when using TypeScript with the generated schema, it results in lengthy checks like this:

                {edges.map(edge => (
                  <div>
                    {edge &&
                      edge.node &&
                      edge.node.id (
                        ...<JSX here>
    

    As far as I can tell this is set up here: https://github.com/graphql-python/graphql-relay-py/blob/master/graphql_relay/connection/connection.py#L44

    Could there be required on edges/nodes? At least as an option it'd be a breaking change. Right now I'm not sure how to override this.

    opened by pcraciunoiu 3
  • Adding support for limiting array length and updated next/previous logic

    Adding support for limiting array length and updated next/previous logic

    It is possible to trivially add support for limiting the size of a return object. This is important because people might ask for 10,000 items and we need to enforce a hard limit on the number of items.

    From my understanding, the relay spec has also been modified to allow better handling of the hasPreviousPage/hasNextPage logic, https://github.com/facebook/relay/pull/2079.

    Since we have just a list here, it is very simple to have more accurate hasPreviousPage and hasNextPage logic. Should make it easier to support windowed pagination.

    Seeing if this is something that would be considered before I tackle the tests.

    opened by wakemaster39 1
  • Paginate without requiring length

    Paginate without requiring length

    The current implementation requires you to pass the total length of the iterable, and this usually means making an extra query to find out the count. There's no need for this, since we can actually accomplish the same results by trying to fetch n+1 results, which will mean that there are more to be consumed, without the need for the real total count.

    This is the custom implementation we have applied to spare us the count query, which can be costly and adds an unnecessary overhead to every query. We only need the real total count, when that value is requested (using a custom CountableConnection)

    class CountableConnection(graphene.relay.Connection):
        class Meta:
            abstract = True
    
        total_count = graphene.Int()
    
        @staticmethod
        def resolve_total_count(root, info, *args, **kwargs):
            try:
                t_count = root.iterable.count()
            except:
                t_count = len(root.iterable)
            
            return t_count
    
    def connection_from_list_slice(list_slice, args=None, connection_type=None,
                                   edge_type=None, pageinfo_type=None):
        '''
        Given an iterator it consumes the needed amount based on the pagination
        params, and also tries to figure out if there are more results to be
        consumed. We do so by trying to fetch one more element than the specified
        amount, if we were able to fetch n+1 it means there are more to be consumed.
    
        This spares the caller passing the total count of results, which
        usually means making an extra query just to find out that number.
        '''
        from graphql_relay.utils import base64, unbase64, is_str
        from graphql_relay.connection.connectiontypes import Connection, PageInfo, Edge
    
        connection_type = connection_type or Connection
        edge_type = edge_type or Edge
        pageinfo_type = pageinfo_type or PageInfo
    
        args = args or {}
    
        before = args.get('before')
        after = args.get('after')
        first = args.get('first')
        last = args.get('last')
    
        if first:
            after = get_offset_with_default(after, -1) + 1
            _slice = list_slice[after:  max(after, 0) + first + 1]  # fetch n+1
    
            items = _slice[:-1]
            if len(items) < first:
                items = _slice[:]  # If there are not enough, get them all
    
            edges = [
                edge_type(
                    node=node,
                    cursor=offset_to_cursor(after + i)
                )
                for i, node in enumerate(items)
            ]
    
        elif last:
            if before:
                before = get_offset_with_default(before)
                _slice = list_slice[max(before-last-1, 0):before]  # fetch n+1
    
            else:
                # beware that not specifying before results in the need
                # to calculate the total amount
                _slice = list_slice[(last*-1)-1:]
    
                try:  
                    before = list_slice.count()
                except:
                    before = len(list_slice)
    
            items = _slice[1:]
            if len(items) < last:
                items = _slice[:]  # If there are not enough, get them all
    
            edges = [
                edge_type(
                    node=node,
                    cursor=offset_to_cursor(before - last -1 + i)
                )
                for i, node in enumerate(items)
            ]
    
        else:  # we are not allowing to pass after/before without first/last
            items = list_slice[:]
            edges = [
                edge_type(
                    node=node,
                    cursor=offset_to_cursor(i)
                )
                for i, node in enumerate(items)
            ]
    
        first_edge_cursor = edges[0].cursor if edges else None
        last_edge_cursor = edges[-1].cursor if edges else None
        
        has_previous_page = False
        if (isinstance(last, int) and len(_slice) > last) or after > 0:
            has_previous_page = True
    
        return connection_type(
            edges=edges,
            page_info=pageinfo_type(
                start_cursor=first_edge_cursor,
                end_cursor=last_edge_cursor,
                has_previous_page=has_previous_page,
                has_next_page=len(_slice) > first if isinstance(first, int) else False
            )
        )
    

    @syrusakbary Not sure if we want this as the default or at least have this as an optional stragey. If this sounds reasonable I can make a PR for this.

    opened by sebastiandev 7
  • Fix pagination indicators when using list slices

    Fix pagination indicators when using list slices

    According to the spec, hasNextPage and hasPreviousPage should be set regardless of the pagination direction if they can be efficiently computed.

    Fixes #12

    opened by sciyoshi 13
Releases(v3.1.5)
  • v3.1.5(Jan 28, 2022)

    Patch release of graphql-relay-py, based on graphql-relay-js 0.9.0, and compatible with graphql-core version 3.1.

    • Re-add deprecated module 'arrayconnection' for backwards compatibility (#45)
    • Export all useful names at the top level
    Source code(tar.gz)
    Source code(zip)
  • v3.1.4(Jan 22, 2022)

    Minor bugfix release of graphql-relay-py, based on graphql-relay-js 0.9.0, and compatible with graphql-core version 3.1.

    • Fixes compatibility with older versions of GraphQL-core 3.1 (#43)
    Source code(tar.gz)
    Source code(zip)
  • v3.1.3(Jan 22, 2022)

    Minor update of graphql-relay-py, based on graphql-relay-js 0.9.0, and compatible with graphql-core version 3.1.7.

    Changes:

    • from_global_id now works same as in relay-js (#39)
    Source code(tar.gz)
    Source code(zip)
  • v3.1.2(Jan 22, 2022)

    Minor update of graphql-relay-py, based on graphql-relay-js 0.8.0, and compatible with graphql-core version 3.1.7.

    Changes:

    • unbase64 now returns an empty string on errors (#39)
    • Use standard base64 implementation (5032f9355d53d830a02ab7bb4e27b7735ca3ef76)
    • array_connection returns all elements if cursors are on the outside (3ba3aa2bd6dc6306021f1cac17f7913c5f6d56e5)
    • Allow mutations to return mappings (c5d0407323145c23acdd0480f72fe91de9100808)
    Source code(tar.gz)
    Source code(zip)
  • v3.1.1(Jan 21, 2022)

    Minor update of graphql-relay-py, based on graphql-relay-js 0.7.0, and compatible with graphql-core version 3.1.7.

    Changes:

    • hasPrevious/NextPage should not be optional (#30)
    • Use same parameter names as in graphql/graphql_sync (67a89c6dfafc2fa17767b019a073e5a89c2f75fe)
    • Avoid null payload in mutation (41b8208254db358a4d890526b9b3e02afae8479b)
    • Remove non-null restriction on clientMutationId field definitions (67e8fcacb8f147008f81d8e3faee0c92579a99fe)
    • Consistently use snake_case for filenames (f15bbb99b92ec59d4ca7dc97e4c0dd6ad4bdcdc5)
    • Do not raise an error when no clientMutationId is provided (617bb04351b939a9f101b76a0127d098e94e92f1)
    • Added missing description for nodes field (36c0b1523976565eaa0b78fb6b051d07bfc26f59)
    • Added description to the pagination arguments (3be10e8194a13d1b817e59ed4d3f00568282759a)
    • Python 3.10 is now supported.
    Source code(tar.gz)
    Source code(zip)
  • v3.1.0(Feb 25, 2021)

    Minor update of graphql-relay-py, compatible with graphql-core version 3.1.

    Changes:

    • Implement SizedSliceable protocol (#31).
    • Define protocols for connection type overrides (#33).
    • Encode IDs using URL- and filesystem-safe alphabet (#35).
    • Python 3.9 is now supported.
    Source code(tar.gz)
    Source code(zip)
  • v3.0.0(Dec 30, 2019)

    This is the first version of graphql-relay-py that is compatible with graphql-core version 3.

    Changes:

    • Various changes to make this compatible with graphql-core version 3.
    • Version 3 of graphql-core and graphql-relay-py require Python 3.6, 3.7 and 3.8.
    • Support pyproject.toml and poetry.
    • The utils module is now a package.
    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Dec 30, 2019)

    This backward compatible release of graphql-relay-py supports graphql-core versions <2

    Changes:

    • Formal support of Python 3.6, 3.7 and 3.8, desupport of Python 3.3 and 3.4
    • Dependency of graphql-core has been capped to versions < 2
    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(Dec 30, 2019)

  • v2.0.0(Dec 30, 2019)

    This is the first version of graphql-relay-py that is compatible with graphql-core version 2.

    Changes:

    • Various minor changes to make this compatible with graphql-core version 2.
    • Dependency of graphql-core has been capped to versions < 3
    Source code(tar.gz)
    Source code(zip)
Owner
GraphQL Python
GraphQL Python
A Python 3.6+ port of the GraphQL.js reference implementation of GraphQL.

GraphQL-core 3 GraphQL-core 3 is a Python 3.6+ port of GraphQL.js, the JavaScript reference implementation for GraphQL, a query language for APIs crea

GraphQL Python 458 Dec 13, 2022
GraphQL security auditing script with a focus on performing batch GraphQL queries and mutations

BatchQL BatchQL is a GraphQL security auditing script with a focus on performing batch GraphQL queries and mutations. This script is not complex, and

Assetnote 267 Dec 24, 2022
This is a graphql api build using ariadne python that serves a graphql-endpoint at port 3002 to perform language translation and identification using deep learning in python pytorch.

Language Translation and Identification this machine/deep learning api that will be served as a graphql-api using ariadne, to perform the following ta

crispengari 2 Dec 30, 2021
A Django GraphQL Starter that uses graphene and graphene_django to interface GraphQL.

Django GraphQL Starter GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data... According to the doc

0101 Solutions 1 Jan 10, 2022
graphw00f is Server Engine Fingerprinting utility for software security professionals looking to learn more about what technology is behind a given GraphQL endpoint.

graphw00f - GraphQL Server Fingerprinting graphw00f (inspired by wafw00f) is the GraphQL fingerprinting tool for GQL endpoints. Table of Contents How

Dolev Farhi 282 Jan 4, 2023
Ariadne is a Python library for implementing GraphQL servers using schema-first approach.

Ariadne Ariadne is a Python library for implementing GraphQL servers. Schema-first: Ariadne enables Python developers to use schema-first approach to

Mirumee Labs 1.9k Jan 1, 2023
A new GraphQL library for Python 🍓

Strawberry GraphQL Python GraphQL library based on dataclasses Installation ( Quick Start ) The quick start method provides a server and CLI to get go

Strawberry GraphQL 2.8k Jan 1, 2023
Graphql-codegen library - a pure python implementation

turms DEVELOPMENT Inspiration Turms is a pure python implementation of the awesome graphql-codegen library, following a simliar extensible design. It

Johannes Roos 22 Dec 23, 2022
GraphQL framework for Python

Graphene ?? Join the community on Slack We are looking for contributors! Please check the ROADMAP to see how you can help ❤️ The below readme is the d

GraphQL Python 7.5k Jan 1, 2023
tartiflette-aiohttp is a wrapper of aiohttp which includes the Tartiflette GraphQL Engine, do not hesitate to take a look of the Tartiflette project.

tartiflette-aiohttp is a wrapper of aiohttp which includes the Tartiflette GraphQL Engine. You can take a look at the Tartiflette API documentation. U

tartiflette 60 Nov 8, 2022
ASGI support for the Tartiflette GraphQL engine

tartiflette-asgi is a wrapper that provides ASGI support for the Tartiflette Python GraphQL engine. It is ideal for serving a GraphQL API over HTTP, o

tartiflette 99 Dec 27, 2022
GraphQL is a query language and execution engine tied to any backend service.

GraphQL The GraphQL specification is edited in the markdown files found in /spec the latest release of which is published at https://graphql.github.io

GraphQL 14k Jan 1, 2023
GraphQL framework for Python

Graphene ?? Join the community on Slack We are looking for contributors! Please check the ROADMAP to see how you can help ❤️ The below readme is the d

GraphQL Python 7.5k Jan 1, 2023
Integrate GraphQL into your Django project.

Graphene-Django A Django integration for Graphene. ?? Join the community on Slack Documentation Visit the documentation to get started! Quickstart For

GraphQL Python 4k Dec 31, 2022
Adds GraphQL support to your Flask application.

Flask-GraphQL Adds GraphQL support to your Flask application. Usage Just use the GraphQLView view from flask_graphql from flask import Flask from flas

GraphQL Python 1.3k Dec 31, 2022
GraphQL Engine built with Python 3.6+ / asyncio

Tartiflette is a GraphQL Server implementation built with Python 3.6+. Summary Motivation Status Usage Installation Installation dependencies Tartifle

tartiflette 839 Dec 31, 2022
Django registration and authentication with GraphQL.

Django GraphQL Auth Django registration and authentication with GraphQL. Demo About Abstract all the basic logic of handling user accounts out of your

pedrobern 301 Dec 9, 2022
tartiflette-aiohttp is a wrapper of aiohttp which includes the Tartiflette GraphQL Engine, do not hesitate to take a look of the Tartiflette project.

tartiflette-aiohttp is a wrapper of aiohttp which includes the Tartiflette GraphQL Engine. You can take a look at the Tartiflette API documentation. U

tartiflette 60 Nov 8, 2022
(Now finding maintainer) 🐍A Pythonic way to provide JWT authentication for Flask-GraphQL

Flask-GraphQL-Auth What is Flask-GraphQL-Auth? Flask-GraphQL-Auth is JWT decorator for flask-graphql inspired from Flask-JWT-Extended. all you have to

Seonghyeon Kim 64 Feb 19, 2022