GraphQL HTTP
📚 Documentation | 📦 PyPI | 🔧 GitHub
A lightweight, production-ready HTTP server for GraphQL APIs built on top of Starlette/FastAPI. This server provides a simple yet powerful way to serve GraphQL schemas over HTTP with built-in support for authentication, CORS, GraphiQL integration, and more.
Features
- 🚀 High Performance: Built on Starlette/ASGI for excellent async performance
- 🔐 JWT Authentication: Built-in JWT authentication with JWKS support
- 🌐 CORS Support: Configurable CORS middleware for cross-origin requests
- 🎨 GraphiQL Integration: Interactive GraphQL IDE for development
- 📊 Health Checks: Built-in health check endpoints
- 🔄 Batch Queries: Support for batched GraphQL operations
- 📡 Subscriptions: GraphQL subscriptions streamed over Server-Sent Events (graphql-sse compatible)
Installation
uv add graphql_http
Or with pip:
pip install graphql_http
Quick Start
Basic Usage
from graphql import GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString
from graphql_http import GraphQLHTTP
# Define your GraphQL schema
schema = GraphQLSchema(
query=GraphQLObjectType(
name="Query",
fields={
"hello": GraphQLField(
GraphQLString,
resolve=lambda obj, info: "Hello, World!"
)
}
)
)
# Create the HTTP server
app = GraphQLHTTP(schema=schema)
# Run the server
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
Using with graphql-api
For building GraphQL schemas, use graphql-api:
from graphql_api import GraphQLAPI
from graphql_http import GraphQLHTTP
api = GraphQLAPI()
@api.type(is_root_type=True)
class Query:
@api.field
def hello(self, name: str = "World") -> str:
return f"Hello, {name}!"
server = GraphQLHTTP.from_api(api)
server.run()
Subscriptions (Server-Sent Events)
GraphQL subscriptions are served over Server-Sent Events, following the
GraphQL over SSE protocol
in distinct connections mode: each operation gets its own SSE connection, results
stream as next events, and a final complete event signals the end of the stream.
Any request whose Accept header includes text/event-stream (with a non-zero
q-value) is answered over SSE — subscriptions stream one next event per result,
while queries and mutations respond with a single next followed by complete.
A valid subscription sent without Accept: text/event-stream is rejected with
406 Not Acceptable.
Defining a subscription with graphql-api
Any field returning an AsyncGenerator becomes a subscription:
import asyncio
from typing import AsyncGenerator
from graphql_api import GraphQLAPI
from graphql_http import GraphQLHTTP
api = GraphQLAPI()
@api.type(is_root_type=True)
class Root:
@api.field
def hello(self) -> str:
return "world"
@api.field
async def countdown(self, start: int = 3) -> AsyncGenerator[int, None]:
while start >= 0:
yield start
start -= 1
await asyncio.sleep(1)
server = GraphQLHTTP.from_api(api)
server.run()
Consuming with curl
curl -N \
-H "Accept: text/event-stream" \
-H "Content-Type: application/json" \
-d '{"query": "subscription { countdown(start: 3) }"}' \
http://localhost:5000/graphql
event: next
data: {"data":{"countdown":3}}
event: next
data: {"data":{"countdown":2}}
event: next
data: {"data":{"countdown":1}}
event: next
data: {"data":{"countdown":0}}
event: complete
data:
Consuming with the graphql-sse JavaScript client
The graphql-sse client uses distinct
connections mode by default (singleConnection: false):
import { createClient } from 'graphql-sse';
const client = createClient({
url: 'http://localhost:5000/graphql',
});
const unsubscribe = client.subscribe(
{ query: 'subscription { countdown(start: 3) }' },
{
next: (result) => console.log(result), // { data: { countdown: 3 } } ...
error: (error) => console.error(error),
complete: () => console.log('done'),
},
);
Behavior notes
- Auth: SSE requests go through the same JWT/auth enforcement as regular requests. Authentication failures are returned as HTTP-level JSON errors (401/403) before any stream is opened. The introspection auth-bypass never applies to subscription operations.
- Errors: per the graphql-sse protocol, errors raised before execution
starts — missing query, parse errors, validation errors, and
subscribe()failures — are delivered over the accepted200 text/event-streamresponse as anextevent carrying the errors, followed bycomplete(a400would leave e.g. a browserEventSourcewith no error detail). Resolver errors during the stream ride insidenextpayloads as standard GraphQL errors; a fatal source error emits a finalnextcarrying the error, thencomplete. - Middleware & execution context: subscription events resolve through the
same
middlewarechain andexecution_context_classas queries and mutations, so field-authorization or error-masking middleware applies to streamed results too. - Keep-alive: while a stream is idle the server emits
: pingSSE comments every 15 seconds so intermediary proxies don't drop the connection. Configure withGraphQLHTTP(..., sse_keepalive_interval=30.0)(must be positive;Nonedisables pings). - Concurrency limit: at most
sse_max_streamssubscription streams (default 100) may be open concurrently per server; further subscription requests are rejected with429 Too Many Requestsuntil a slot frees up. Passsse_max_streams=Noneto remove the limit. - Disconnects: when the client closes the connection the underlying
subscription generator is closed promptly — including
await-based cleanup in itsfinallyblock — with no leaked tasks.
Related Projects
- graphql-api - Build GraphQL schemas with decorators
- graphql-db - SQLAlchemy integration for database-backed APIs
- graphql-mcp - Expose GraphQL as MCP tools
See the documentation for configuration, authentication, and advanced features.
Documentation
Visit the official documentation for comprehensive guides, examples, and API reference.
Key Topics
- Getting Started - Quick introduction and basic usage
- Configuration - Configure your HTTP server
- Authentication - JWT and auth setup
- Testing - Test your GraphQL endpoints
- Examples - Real-world usage examples
- API Reference - Complete API documentation
License
MIT License - see LICENSE file for details.
Release files for graphql-http 2.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| graphql_http-2.4.0.tar.gz | 211.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| graphql_http-2.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 311.1 kB
Release files / graphql_http-2.4.0.tar.gz
| Download URL | graphql_http-2.4.0.tar.gz |
|---|---|
| Size | 211.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
16ad162615ddde47bec8164f06b0010d5db03a8c24a981d6f76ce83d2f27ec4d
|
|
BLAKE2b-256 checksum How to use checksums |
9eb178264e5d22aeb95370fcbd6c0405e11198a2aa20ac49ec1406671ef408fa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / graphql_http-2.4.0-py3-none-any.whl
| Download URL | graphql_http-2.4.0-py3-none-any.whl |
|---|---|
| Size | 99.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d98eeabe4d76d36532f5fdf051e7b4a494b7e486a1a4babfc2d5cc033167cdec
|
|
BLAKE2b-256 checksum How to use checksums |
a5469812c19be78f985266674866b7a8516a2e3c0e06b7722865fc118af733d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|