{"id":769,"library":"graphql-relay","title":"Relay library for graphql-core","description":"graphql-relay-py is the Python port of the JavaScript Relay library for GraphQL, designed to facilitate the creation of Relay-compliant GraphQL servers using graphql-core. It provides utilities for global object identification (Nodes) and cursor-based pagination (Connections). The library maintains a regular release cadence, often aligning with updates to graphql-core and the upstream graphql-relay-js specification. The current version is 3.2.0.","status":"active","version":"3.2.0","language":"python","source_language":"en","source_url":"https://github.com/graphql-python/graphql-relay-py","tags":["graphql","relay","api","pagination","node"],"install":[{"cmd":"pip install graphql-relay graphql-core","lang":"bash","label":"Install with graphql-core"}],"dependencies":[{"reason":"This library is built on top of graphql-core and requires it for core GraphQL functionality. Version 3.x of graphql-relay-py is compatible with graphql-core version 3.x.","package":"graphql-core","optional":false}],"imports":[{"symbol":"connection_args","correct":"from graphql_relay import connection_args"},{"symbol":"connection_definitions","correct":"from graphql_relay import connection_definitions"},{"symbol":"connection_from_array","correct":"from graphql_relay import connection_from_array"},{"symbol":"from_global_id","correct":"from graphql_relay import from_global_id"},{"symbol":"to_global_id","correct":"from graphql_relay import to_global_id"},{"symbol":"global_id_field","correct":"from graphql_relay import global_id_field"},{"symbol":"node_definitions","correct":"from graphql_relay import node_definitions"},{"note":"The `arrayconnection` module was temporarily removed/renamed to `array_connection` in `v3.1.1` and later re-added for backward compatibility in `v3.1.5`. Direct imports from `graphql_relay` for core connection functions like `connection_from_array` are generally preferred.","wrong":"from graphql_relay.connection.arrayconnection import connection_from_array","symbol":"arrayconnection","correct":"from graphql_relay.connection.arrayconnection import connection_from_array_slice"}],"quickstart":{"code":"import graphene\nfrom graphql.execution.execute import execute\nfrom graphql.language.parser import parse\nfrom graphql_relay import node_definitions, global_id_field, connection_definitions, connection_from_array, connection_args, from_global_id\n\nclass User:\n    def __init__(self, id, name):\n        self.id = id\n        self.name = name\n\nusers_data = {\n    \"1\": User(\"1\", \"Alice\"),\n    \"2\": User(\"2\", \"Bob\"),\n}\n\ndef get_user(id):\n    return users_data.get(id)\n\ndef get_object_from_global_id(global_id, info):\n    type_name, id = from_global_id(global_id)\n    if type_name == 'User':\n        return get_user(id)\n    return None\n\nnode_interface, node_field = node_definitions(\n    get_object_from_global_id, lambda obj, info: UserNode if isinstance(obj, User) else None\n)\n\nclass UserNode(graphene.ObjectType):\n    class Meta:\n        name = 'User'\n        interfaces = (node_interface,)\n\n    id = global_id_field('User')\n    name = graphene.String()\n\nUserConnection = connection_definitions(UserNode, 'UserConnection').connection_type\n\nclass Query(graphene.ObjectType):\n    node = node_field\n    users = graphene.Field(\n        UserConnection,\n        args=connection_args,\n        resolve=lambda obj, info, **args:\n            connection_from_array(list(users_data.values()), args)\n    )\n\nschema = graphene.Schema(query=Query)\n\n# Example Usage:\nquery_str = '''\n    query {\n      users (first: 1) {\n        edges {\n          node {\n            id\n            name\n          }\n        }\n      }\n      node(id: \"VXNlcjox\") {\n        id\n        ... on User {\n          name\n        }\n      }\n    }\n'''\n\n# Assuming \"VXNlcjox\" is the global ID for User:1 (to_global_id('User', '1'))\n# Use 'User:1' -> base64 -> 'VXNlcjox'\n\nresult = execute(schema, parse(query_str))\nprint(result.data)\n","lang":"python","description":"This quickstart demonstrates how to set up a basic Relay-compliant GraphQL schema using `graphql-relay` with `graphene`. It includes defining a Node interface for global object identification and a Connection for pagination. It showcases `node_definitions`, `global_id_field`, `connection_definitions`, `connection_from_array`, and `connection_args`."},"warnings":[{"fix":"Migrate your graphql-core dependency to v3.x and update your graphql-relay-py imports and API calls as necessary. Review the graphql-core and graphql-relay-py v3 release notes for specific changes. This also requires Python 3.6+.","message":"Version 3.0.0 introduced significant breaking changes to align with graphql-core version 3.x. Code written for graphql-relay-py v2.x (compatible with graphql-core v2.x) will not work with v3.x without migration.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Ensure that your `graphql-relay-py` version matches the expected `graphql-core` major version. For new projects, use `graphql-relay-py` v3.x with `graphql-core` v3.x.","message":"Versions 2.0.0 and 0.5.0 introduced breaking changes relating to compatibility with specific major versions of `graphql-core`. `v2.0.0` was compatible with `graphql-core < 3`, and `v0.5.0` with `graphql-core < 2`.","severity":"breaking","affected_versions":"2.0.0, 0.5.0"},{"fix":"For new code, prefer importing connection-related utilities like `connection_from_array` directly from the top-level `graphql_relay` package. If you must use `arrayconnection`, ensure your version is `3.1.5` or later, or adapt to `array_connection` if on affected versions.","message":"The internal `arrayconnection` module was temporarily renamed to `array_connection` in `v3.1.1` as part of a `snake_case` consistency drive, which could break direct imports. It was later re-added for backward compatibility in `v3.1.5`.","severity":"gotcha","affected_versions":"3.1.1 - 3.1.4"},{"fix":"Update your code to handle `unbase64` returning an empty string for invalid inputs if you were previously relying on exceptions for error handling. Review `from_global_id` usage, especially if parsing IDs from external sources.","message":"The behavior of `unbase64` changed in `v3.1.2` to return an empty string on errors instead of potentially raising an exception, and `from_global_id` behavior was adjusted in `v3.1.3` to match `relay-js`.","severity":"gotcha","affected_versions":">=3.1.2"},{"fix":"Ensure 'graphene' is installed in your Python environment, e.g., by running `pip install graphene`.","message":"The 'graphene' package is a core dependency and must be installed. This error occurs when 'graphene' is not found in the Python environment.","severity":"breaking","affected_versions":"all"},{"fix":"Ensure that the `graphene` package is installed in your environment. You can typically install it using `pip install graphene`.","message":"The 'graphene' package was not found. This indicates that `graphene` is not installed in the environment where the script is being executed.","severity":"breaking","affected_versions":"*"}],"env_vars":null,"last_verified":"2026-05-12T18:49:52.750Z","next_check":"2026-06-27T00:00:00.000Z","problems":[{"fix":"Ensure 'graphql-core' is installed: `pip install graphql-core`.","cause":"The 'graphql-core' library, a core dependency for 'graphql-relay', is not installed in the Python environment, or there's an issue with the Python path.","error":"ModuleNotFoundError: No module named 'graphql'"},{"fix":"Check the resolver functions (especially the `get_node_from_global_id` function passed to `node_definitions`) to ensure they correctly retrieve and return the expected object for a given ID, and that all necessary data is available and not `None` before attempting to access its attributes.","cause":"This often occurs in a Relay context when a resolver (e.g., within `node_definitions` or for a field derived from `global_id_field`) returns `None` where a GraphQL type expects a non-null object, or when `from_global_id` cannot resolve a valid object.","error":"AttributeError: 'NoneType' object has no attribute '...'"},{"fix":"Ensure that the `to_global_id` and `from_global_id` functions (or their equivalents if manually implemented) correctly generate unique IDs by incorporating both the type name and the object's local ID, preventing collisions between different types that might share the same local ID values.","cause":"This client-side Relay error indicates that the GraphQL server is returning the same global ID for two different types of objects, violating Relay's requirement for globally unique IDs across all types.","error":"RelayResponseNormalizer: Invalid record '...'. Expected __typename to be consistent, but the record was assigned conflicting types Foo and Bar. The GraphQL server likely violated the globally unique ID requirement by returning the same ID for different objects."},{"fix":"Define the `id` field on types implementing `Node` using `graphql_relay.global_id_field()` and ensure it correctly maps to the object's unique identifier. The `id` field should always have the `ID!` GraphQL type.","cause":"A GraphQL type that implements the Relay `Node` interface has an `id` field defined with an incorrect type (e.g., `String` instead of `ID!`) or the `global_id_field` is not properly used to expose the `id` field as a globally unique identifier.","error":"Error: \"Node\" expects field \"id\" (or similar: Disallowed type `String` of field `id` on parent type `Foo` cannot be used by Relay to identify entities.)"},{"fix":"Ensure that `graphql-relay` and `graphql-core` are compatible versions. Often, updating both libraries to their latest compatible versions (`pip install --upgrade graphql-relay graphql-core`) resolves such issues.","cause":"This error typically points to an incompatibility between the installed versions of `graphql-relay` and `graphql-core`, where a function signature or module export has changed between versions, especially when upgrading one library without the other.","error":"TypeError: (0 , _graphql.resolveObjMapThunk) is not a function"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":0,"quickstart_tag":"stale","pypi_latest":"3.2.0","cli_name":"","cli_version":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","installed_version":null,"pypi_latest":"3.2.0","is_stale":null,"results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"graphql-relay","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.21,"mem_mb":6.4,"disk_size":"19.9M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"graphql-relay","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.22,"mem_mb":6.4,"disk_size":"19.9M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"graphql-relay","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":null,"install_time_s":1.7,"import_time_s":0.15,"mem_mb":6.4,"disk_size":"20M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"graphql-relay","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":6.4,"disk_size":"20M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"graphql-relay","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.33,"mem_mb":7.8,"disk_size":"22.1M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"graphql-relay","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.36,"mem_mb":7.8,"disk_size":"22.1M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"graphql-relay","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":null,"install_time_s":1.8,"import_time_s":0.29,"mem_mb":7.8,"disk_size":"23M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"graphql-relay","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.46,"mem_mb":7.8,"disk_size":"23M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"graphql-relay","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.54,"mem_mb":8.4,"disk_size":"13.9M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"graphql-relay","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.56,"mem_mb":8.4,"disk_size":"13.9M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"graphql-relay","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":null,"install_time_s":1.6,"import_time_s":0.54,"mem_mb":8.4,"disk_size":"14M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"graphql-relay","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.5,"mem_mb":8.4,"disk_size":"14M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"graphql-relay","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.53,"mem_mb":8.9,"disk_size":"13.6M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"graphql-relay","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.56,"mem_mb":8.9,"disk_size":"13.5M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"graphql-relay","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":null,"install_time_s":1.6,"import_time_s":0.48,"mem_mb":8.9,"disk_size":"14M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"graphql-relay","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.51,"mem_mb":8.9,"disk_size":"14M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"graphql-relay","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.19,"mem_mb":6.4,"disk_size":"19.7M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"graphql-relay","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.21,"mem_mb":6.4,"disk_size":"19.7M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"graphql-relay","exit_code":0,"wheel_type":"wheel","failure_reason":null,"import_side_effects":null,"install_time_s":2.1,"import_time_s":0.2,"mem_mb":6.4,"disk_size":"20M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"graphql-relay","exit_code":0,"wheel_type":null,"failure_reason":null,"import_side_effects":null,"install_time_s":null,"import_time_s":0.18,"mem_mb":6.4,"disk_size":"20M"}]},"quickstart_checks":{"last_tested":"2026-04-24","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","exit_code":1},{"runtime":"python:3.10-slim","exit_code":1},{"runtime":"python:3.11-alpine","exit_code":1},{"runtime":"python:3.11-slim","exit_code":1},{"runtime":"python:3.12-alpine","exit_code":1},{"runtime":"python:3.12-slim","exit_code":1},{"runtime":"python:3.13-alpine","exit_code":1},{"runtime":"python:3.13-slim","exit_code":1},{"runtime":"python:3.9-alpine","exit_code":1},{"runtime":"python:3.9-slim","exit_code":1}]}}