graphql-core is a Python port of GraphQL.js kept in step with the reference implementation
A Python 3 port of the GraphQL.js reference implementation of GraphQL.
At a glance
- What is it?
- GraphQL-core 3 is a dependency free Python library that reimplements the GraphQL.js reference implementation, covering schema construction, query parsing, validation and execution with sync and async entry points, and it tracks GraphQL.js releases closely enough that a major version bump upstream becomes a minor version bump here. It suits a Python backend that has to speak GraphQL, and it is a protocol library rather than a web framework.
- Who is it for?
- Adopt graphql-core if you are writing a Python service that must implement a GraphQL API and you want the behaviour of the reference implementation rather than a framework's interpretation of it, because the port replicates GraphQL.js's own test suite and carries no runtime dependencies.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A port that tracks the reference implementation
GraphQL-core 3 is a Python 3.10 or newer port of GraphQL.js, the JavaScript reference implementation for GraphQL, a query language for APIs created by Facebook. The relationship is closer than a reimplementation usually is, and the README is explicit about the goal: GraphQL-core aims to reproduce the code of the reference implementation GraphQL.js in Python as closely as possible while staying up to date with the latest development of GraphQL.js. The current stable version 3.3.0 is up to date with GraphQL.js version 17.0.2 and supports Python versions 3.10 to 3.15. That tracking has a direct consequence for how you depend on it. The README notes that for various reasons GraphQL-core does not use SemVer like GraphQL.js, and that changes in the major version of GraphQL.js are reflected in the minor version of GraphQL-core instead, which means there can be breaking changes in the API when the minor version changes, and only patch releases are fully backward compatible. The recommendation is to use something like ~= 3.3.0 as the version specifier. The project is MIT licensed, the repository is not archived, and the last push was on 2026-10-19, with releases v3.3.0 and v3.2.13 on 2026-09-27 and a v3.3.0rc1 candidate in August 2026.
The test suite is the compatibility argument
The strongest claim in the README is about testing: an extensive test suite with over 3000 unit tests and 100 percent coverage replicates the complete test suite of GraphQL.js, ensuring that the port is reliable and compatible with GraphQL.js. That is a specific and checkable claim rather than a general assurance, and it changes how you should evaluate the library. You are not trusting a Python interpretation of the GraphQL specification; you are trusting a translation of an implementation that is already tested against its own suite. The repository layout backs this up, with a src/ directory for the package, a tests/ directory that the README points at repeatedly, and the suggestion that a good way to get started is to walk through the GraphQL specification README and the corresponding tests in parallel. The tooling in pyproject.toml reflects a serious test setup, with pytest, pytest-benchmark, pytest-cov, pytest-describe, pytest-timeout and pytest-codspeed in the test group, plus tox for environment management, ruff and mypy in the lint group, and sphinx in the doc group. The pyproject.toml also records the package itself has an empty dependencies list, which is worth noticing.
Installing from PyPI
The package is on PyPI and installs with pip.
python -m pip install graphql-coreIf you prefer uv, the same package is available through it.
uv pip install graphql-coreThere is nothing else to install, because the dependencies list in pyproject.toml is empty. The distribution imports as graphql, so the package name on PyPI, graphql-core, and the import name, graphql, differ, which is a small trap when you write requirements files by hand. The supported Python range is 3.10 to 3.15 according to the classifiers, and the metadata declares requires-python of 3.10 or newer. If you need GraphQL.js 16 compatibility or support for Python 3.7 to 3.9, the README points you at the latest version 3.2.13 from the 3.2 branch.
Building a schema and the resolver signature
GraphQL-core provides two important capabilities, building a type schema and serving queries against that type schema. A minimal schema looks like this.
from graphql import (
GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString)
schema = GraphQLSchema(
query=GraphQLObjectType(
name='RootQueryType',
fields={
'hello': GraphQLField(
GraphQLString,
resolve=lambda obj, info: 'world')
}))The resolve function can return a value, a co-routine object or a list of these. It takes two positional arguments, the first providing the root or the resolved parent field, and the second providing a GraphQLResolveInfo object with information about the execution state of the query, including a context attribute holding per-request state such as authentication information or a database session. Any GraphQL arguments are passed to the resolve functions as individual keyword arguments. Two differences from GraphQL.js are worth memorising, because they are where ported code usually breaks. The signature of the resolver functions is a bit different in GraphQL.js, where the context is passed separately and arguments are passed as a single object. And GraphQL fields must be passed as a GraphQLField object explicitly, with GraphQL arguments passed as GraphQLArgument objects.
Executing queries synchronously
Serving a query is a single call. The graphql_sync function assumes that all resolvers return values synchronously.
from graphql import graphql_sync
source = '{ hello }'
print(graphql_sync(schema, source))It first ensures the query is syntactically and semantically valid before executing it, reporting errors otherwise. A successful run prints an ExecutionResult with the data and no errors.
ExecutionResult(data={'hello': 'world'}, errors=None)Querying a field that does not exist produces an error rather than an exception, with the source location attached.
ExecutionResult(data=None, errors=[GraphQLError(
"Cannot query field 'BoyHowdy' on type 'RootQueryType'.",
locations=[SourceLocation(line=1, column=3)])])That behaviour is the reason the validation step matters. graphql_sync checks the query against the schema before a single resolver runs, so a malformed field name costs nothing and produces a structured error the caller can serialise into a GraphQL response, rather than a Python exception from deep inside your resolver.
Asynchronous resolvers with asyncio
By using coroutines as resolvers, you can also create results in an asynchronous fashion with the graphql function, and the README's example makes the shape explicit.
import asyncio
from graphql import (
graphql, GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString)
async def resolve_hello(obj, info):
await asyncio.sleep(3)
return 'world'
schema = GraphQLSchema(
query=GraphQLObjectType(
name='RootQueryType',
fields={
'hello': GraphQLField(
GraphQLString,
resolve=resolve_hello)
}))
async def main():
query = '{ hello }'
print('Fetching the result...')
result = await graphql(schema, query)
print(result)
asyncio.run(main())The same schema definition serves both modes, which is the useful part. You decide per field whether the resolver blocks or awaits, and the library handles the execution accordingly. The asyncio dependency in the test group, anyio, and the coroutine support in the resolve contract are what make that work.
Where graphql-core is the wrong choice
Four limits are worth stating. The first is that this is a protocol library and nothing more. It does not provide an HTTP server, a routing layer, a request lifecycle or a web framework integration, so exposing the schema over HTTP requires a separate web framework, and you inherit that framework's conventions, error handling and release cycle as well as this one. The second is the version coupling, and it is the most likely source of pain in a long-lived project. Because a GraphQL.js major version maps to a GraphQL-core minor version and a minor bump can break the API, a team that pins loosely will eventually hit a breaking change in a dependency upgrade with no major version bump to warn them, which is exactly why the README recommends a ~= style specifier. The third is the Python version floor. GraphQL-core 3 requires 3.10 or newer and supports up to 3.15, so a platform on 3.9 or older is limited to the 3.2 branch, which also means the 3.2 branch will age more slowly than the main line. The fourth is that the port reproduces GraphQL.js closely on purpose, which means GraphQL-core inherits GraphQL.js's design decisions rather than offering Python-native ones, so a team that wants idiomatic Python data modelling and type hints at the schema level will find the ergonomics a translation rather than a redesign. If your team wants a more Python-native feel, a different GraphQL library may suit better, at the cost of losing graphql-core's direct guarantee of matching the reference implementation's behaviour, which is the trade this project exists to make.
Editorial conclusion
Adopt graphql-core if you are writing a Python service that must implement a GraphQL API and you want the behaviour of the reference implementation rather than a framework's interpretation of it, because the port replicates GraphQL.js's own test suite and carries no runtime dependencies. Do not adopt it if you want a web framework, since it does not provide a server, routing or a request lifecycle, and do not adopt it if you are on Python 3.9 or older, because the 3.x line requires 3.10 or newer. Verify two things before you pin a version: which GraphQL.js release you need to match, since 3.3.0 tracks GraphQL.js 17.0.2 while the 3.2 branch covers GraphQL.js 16 and older Python versions, and how you will constrain the version, because the project does not follow SemVer and the README recommends a specifier such as ~= 3.3.0 since a minor bump can carry breaking API changes. The practical first step is to pip install graphql-core, build a one field schema with GraphQLSchema and GraphQLField, and run one query through graphql_sync to confirm the resolver signature matches your expectations.
Frequently asked questions
What is graphql-core?
It is a Python 3.10 or newer port of GraphQL.js, the JavaScript reference implementation for GraphQL, providing schema construction and query execution. The README states that its test suite of over 3000 unit tests with 100 percent coverage replicates the complete GraphQL.js test suite.
How do I install graphql-core?
Install it from PyPI with python -m pip install graphql-core, or uv pip install graphql-core. The package imports as graphql, and the pyproject.toml declares an empty dependencies list, so nothing else is pulled in.
Which GraphQL.js version does graphql-core 3.3.0 match?
The README states that the stable version 3.3.0 of GraphQL-core is up to date with GraphQL.js version 17.0.2 and supports Python 3.10 to 3.15. For GraphQL.js 16 compatibility or Python 3.7 to 3.9 support, the 3.2 branch is the option.
How do I run a query with graphql-core?
Call graphql_sync with the schema and the query string. It validates the query for syntax and semantics before executing it and returns an ExecutionResult carrying either data or a list of GraphQLError objects with source locations. Use the graphql function with coroutine resolvers for asynchronous execution.
Should I pin the graphql-core version?
Yes. The README says GraphQL-core does not use SemVer like GraphQL.js, so a GraphQL.js major version change shows up as a GraphQL-core minor version change, which can bring breaking API changes, and only patch releases are fully backward compatible. The recommendation is a specifier such as ~= 3.3.0.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/graphql-python-graphql-core)Was this useful?