Metadata-Version: 2.4
Name: lexigram-graphql
Version: 0.1.1
Summary: GraphQL support for Lexigram Framework - Strawberry, Apollo Federation, and subscriptions
Project-URL: Homepage, https://github.com/lexigram-dev/lexigram
Project-URL: Repository, https://github.com/lexigram-dev/lexigram
Project-URL: Documentation, https://docs.lexigram.dev/graphql
Project-URL: Issues, https://github.com/lexigram-dev/lexigram/issues
Project-URL: Changelog, https://github.com/lexigram-dev/lexigram/blob/main/CHANGELOG.md
Author-email: Lexigram Framework Team <team@lexigram.dev>
Maintainer-email: Lexigram Framework Team <team@lexigram.dev>
License: MIT
License-File: LICENSE
Keywords: api,async,federation,framework,graphql,schema,strawberry,subgraph
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: graphql-core>=3.2.0
Requires-Dist: lexigram-contracts>=0.1.0
Requires-Dist: lexigram>=0.1.1
Requires-Dist: strawberry-graphql>=0.235.0
Requires-Dist: typing-extensions>=4.5.0
Provides-Extra: all
Requires-Dist: lexigram-testing>=0.1.1; extra == 'all'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'all'
Requires-Dist: pytest-cov>=4.0.0; extra == 'all'
Requires-Dist: pytest-mock>=3.10.0; extra == 'all'
Requires-Dist: pytest>=8.0.0; extra == 'all'
Requires-Dist: starlette>=0.28.0; extra == 'all'
Requires-Dist: websockets>=12.0.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: starlette
Requires-Dist: starlette>=0.28.0; extra == 'starlette'
Provides-Extra: subscriptions
Requires-Dist: websockets>=12.0.0; extra == 'subscriptions'
Provides-Extra: test
Requires-Dist: lexigram-testing>=0.1.1; extra == 'test'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'test'
Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
Requires-Dist: pytest-mock>=3.10.0; extra == 'test'
Requires-Dist: pytest>=8.0.0; extra == 'test'
Description-Content-Type: text/markdown

# lexigram-graphql

GraphQL support for Lexigram Framework — Strawberry, Apollo Federation, and subscriptions.

---

## Overview

`lexigram-graphql` provides a complete GraphQL layer built on Strawberry, with field-level permissions, DataLoaderProtocol batching for N+1 query elimination, WebSocket subscriptions, depth/complexity limiting, persisted queries, and automatic playground disable in production. All services are wired through Lexigram's DI container.

---

## Install

```bash
uv add lexigram lexigram-web lexigram-graphql
# Optional extras
uv add "lexigram-graphql[starlette]"   # Starlette integration
uv add "lexigram-graphql[subscriptions]"  # WebSocket subscriptions
```

## Quick Start

```python
from lexigram import Application
from lexigram.di.module import Module, module
from lexigram.graphql import GraphQLModule
from lexigram.web import WebModule

import strawberry


@strawberry.type
class Query:
    @strawberry.field
    def hello(self) -> str:
        return "world"


@module(
    imports=[
        WebModule.configure(host="127.0.0.1", port=8000),
        GraphQLModule.configure(query_class=Query),
    ]
)
class AppModule(Module):
    pass


async def main() -> None:
    async with Application.boot(modules=[AppModule]) as app:
        web = await app.container.resolve(WebProvider)
        web.run_server(host="127.0.0.1", port=8000)


if __name__ == "__main__":
    import asyncio
    asyncio.run(main())
```

GraphQL endpoint: `http://127.0.0.1:8000/graphql`
Playground: `http://127.0.0.1:8000/graphql/playground` (development only)

## Configuration

> **Zero-config usage:** Call `GraphQLModule.configure()` with no arguments to use all defaults.

### Option 1 — YAML file

```yaml
# application.yaml
graphql:
  server:
    path: "/graphql"
    debug: false
  playground:
    enabled: true
  schema:
    max_depth: 10
    complexity_limit: 1000
```

### Option 2 — Profiles + Environment Variables *(recommended)*

```bash
export LEX_GRAPHQL__PATH=/api/graphql
export LEX_GRAPHQL__DEBUG=true
export LEX_GRAPHQL__DEPTH_LIMIT__MAX_DEPTH=8
```

### Option 3 — Python

```python
from lexigram.graphql import GraphQLModule
from lexigram.graphql.config import GraphQLConfig

config = GraphQLConfig.development()
GraphQLModule.configure(config=config, query_class=Query)
```

### Config reference

| Field | Default | Env var | Description |
|-------|---------|---------|-------------|
| `path` | `"/graphql"` | `LEX_GRAPHQL__PATH` | GraphQL HTTP endpoint |
| `debug` | `false` | `LEX_GRAPHQL__DEBUG` | Propagates to `errors.debug_mode` |
| `introspection.enabled` | `true` | `LEX_GRAPHQL__INTROSPECTION__ENABLED` | Disabled automatically in production |
| `playground.enabled` | `true` | `LEX_GRAPHQL__PLAYGROUND__ENABLED` | Disabled automatically in production |
| `playground.path` | `"/graphql/playground"` | `LEX_GRAPHQL__PLAYGROUND__PATH` | Playground URL |
| `subscriptions.enabled` | `true` | `LEX_GRAPHQL__SUBSCRIPTIONS__ENABLED` | WebSocket subscriptions |
| `subscriptions.path` | `"/graphql/ws"` | `LEX_GRAPHQL__SUBSCRIPTIONS__PATH` | WebSocket endpoint |
| `depth_limit.enabled` | `true` | `LEX_GRAPHQL__DEPTH_LIMIT__ENABLED` | Enable query depth limiting |
| `depth_limit.max_depth` | `10` | `LEX_GRAPHQL__DEPTH_LIMIT__MAX_DEPTH` | Maximum allowed query depth |
| `complexity.enabled` | `true` | `LEX_GRAPHQL__COMPLEXITY__ENABLED` | Enable complexity scoring |
| `complexity.max_complexity` | `1000` | `LEX_GRAPHQL__COMPLEXITY__MAX_COMPLEXITY` | Maximum complexity score |
| `dataloader.enabled` | `true` | `LEX_GRAPHQL__DATALOADER__ENABLED` | Enable DataLoaderProtocol integration |
| `dataloader.batch_delay_ms` | `2.0` | `LEX_GRAPHQL__DATALOADER__BATCH_DELAY_MS` | Batch accumulation delay (ms) |
| `persisted_queries.enabled` | `true` | `LEX_GRAPHQL__PERSISTED_QUERIES__ENABLED` | Automatic persisted queries (APQ) |
| `cache.enabled` | `true` | `LEX_GRAPHQL__CACHE__ENABLED` | Response caching |
| `errors.mask_errors` | `true` | `LEX_GRAPHQL__ERRORS__MASK_ERRORS` | Mask internal errors in responses |
| `schema_baseline_path` | `null` | `LEX_GRAPHQL__SCHEMA_BASELINE_PATH` | SDL file for breaking-change detection |

## Module Factory Methods

| Method | Description |
|--------|-------------|
| `GraphQLModule.configure(...)` | Configure with explicit config and query class |
| `GraphQLModule.stub()` | Minimal config for testing |

## Key Features

- **Strawberry GraphQL** — schema-first GraphQL with type annotations
- **DataLoaderProtocol batching** — eliminates N+1 queries with per-request caching
- **WebSocket subscriptions** — `graphql-transport-ws` protocol support
- **Depth and complexity limiting** — prevents malicious or expensive queries
- **Field-level permissions** — `AbstractPermission` subclassing for fine-grained access
- **Automatic persisted queries (APQ)** — reduces payload size for repeated queries
- **Schema baseline checking** — detect breaking changes at boot via SDL comparison

## Testing

```python
from lexigram import Application
from lexigram.graphql import GraphQLModule

async with Application.boot(modules=[GraphQLModule.stub()]) as app:
    executor = await app.container.resolve(GraphQLExecutorProtocol)
    assert executor is not None
```

## Key Source Files

| File | What it contains |
|------|----------------|
| `src/lexigram/graphql/module.py` | `GraphQLModule.configure()`, `.stub()` |
| `src/lexigram/graphql/config.py` | `GraphQLConfig` and all sub-configs |
| `src/lexigram/graphql/di/provider.py` | `GraphQLProvider` boot and registration |
| `src/lexigram/graphql/security/permissions.py` | `AbstractPermission` base class |
| `src/lexigram/graphql/dataloader/loader.py` | `DataLoaderProtocol` implementation |
| `src/lexigram/graphql/decorators.py` | `@retry_resolver`, `@log_resolver` |