reinhardt-graphql 0.1.0-alpha.7

GraphQL API support for Reinhardt (facade crate)
Documentation
# reinhardt-graphql

GraphQL integration (facade crate)

## Overview

GraphQL API support with schema generation from models, query and mutation resolvers, and integration with the authentication and permission system. Provides a flexible alternative to REST APIs.

This is a facade crate that re-exports functionality from the following modules:
- **core**: Core GraphQL implementation
- **macros**: Procedural macros for GraphQL (when features enabled)

## Crate Structure

```
reinhardt-graphql/              (facade crate - public API)
├── crates/
│   ├── core/                   (core GraphQL implementation)
│   └── macros/                 (procedural macros)
```

Users should depend on `` `reinhardt-graphql` `` (this facade crate) for all GraphQL functionality.

## Features

### Implemented ✓

#### Core Type System

- **GraphQL Type Markers**: `GraphQLType` and `GraphQLField` traits for type-safe GraphQL type definitions
- **Error Handling**: Custom `GraphQLError` enum with Schema, Resolver, and NotFound variants
- **Base Resolver Trait**: Async `Resolver` trait with generic output types for flexible resolver implementation

#### Schema & Data Types

- **User Type**: Complete GraphQL object implementation with id, name, email, and active fields
- **User Storage**: Thread-safe in-memory storage using `Arc<RwLock<HashMap>>` for user data
  - `new()`: Create new storage instance
  - `add_user()`: Add or update user in storage
  - `get_user()`: Retrieve user by ID
  - `list_users()`: List all stored users
- **Input Types**: `CreateUserInput` for user creation mutations
- **Schema Builder**: `create_schema()` function to build GraphQL schema with data context

#### Query Operations

- **User Queries**:
  - `user(id: ID)`: Retrieve single user by ID
  - `users()`: List all users
  - `hello(name: Option<String>)`: Simple greeting query for testing
- **Context Integration**: Queries access UserStorage through GraphQL context

#### Mutation Operations

- **User Mutations**:
  - `createUser(input: CreateUserInput)`: Create new user with auto-generated UUID
  - `updateUserStatus(id: ID, active: bool)`: Update user active status
- **State Management**: Mutations persist changes to UserStorage

#### Subscription System

- **Event Types**: `UserEvent` enum supporting Created, Updated, and Deleted events
- **Event Broadcasting**: `EventBroadcaster` with tokio broadcast channel (capacity: 100)
  - `new()`: Create new broadcaster instance
  - `broadcast()`: Send events to all subscribers
  - `subscribe()`: Subscribe to event stream
- **Subscription Root**: `SubscriptionRoot` with filtered subscription streams
  - `userCreated()`: Stream of user creation events
  - `userUpdated()`: Stream of user update events
  - `userDeleted()`: Stream of user deletion events (returns ID only)
- **Async Streams**: Real-time event filtering using async-stream

#### Integration

- **async-graphql Integration**: Built on async-graphql framework for production-ready GraphQL server
- **Type Safety**: Full Rust type system integration with compile-time guarantees
- **Async/Await**: Complete async support with tokio runtime
- **Documentation**: Comprehensive doc comments with examples for all public APIs

#### gRPC Transport (Optional - `graphql-grpc` feature)

- **GraphQL over gRPC Service**: `GraphQLGrpcService` implementing gRPC protocol for GraphQL
  - `execute_query()`: Execute GraphQL queries via unary RPC
  - `execute_mutation()`: Execute GraphQL mutations via unary RPC
  - `execute_subscription()`: Execute GraphQL subscriptions via server streaming RPC
- **Protocol Buffers**: Complete proto definitions in `reinhardt-grpc` crate
  - `GraphQLRequest`: query, variables, operation_name
  - `GraphQLResponse`: data, errors, extensions
  - `SubscriptionEvent`: id, event_type, payload, timestamp
- **Request/Response Conversion**: Automatic conversion between gRPC and async-graphql types
- **Error Handling**: Full error information propagation (message, locations, path, extensions)
- **Performance**: Minimal overhead (5-21%, or 0.2-0.8 µs) compared to direct execution
- **Network Communication**: Full TCP/HTTP2 support via tonic
- **Streaming**: Efficient server-side streaming for real-time subscriptions

#### Dependency Injection (Optional - `di` feature)

- **`#[graphql_handler]` macro**: Attribute macro for resolvers with automatic dependency injection
- **`GraphQLContextExt` trait**: Extension trait for extracting DI context from GraphQL context
- **`SchemaBuilderExt` trait**: Convenience methods for adding DI context to schema
- **Cache Control**: Per-parameter cache control with `#[inject(cache = false)]`
- **Type Safety**: Full compile-time type checking for injected dependencies
- **REST Consistency**: Same DI patterns as REST handlers for unified developer experience

## Installation

Add `reinhardt` to your `Cargo.toml`:

```toml
[dependencies]
reinhardt = { version = "0.1.0-alpha.1", features = ["graphql"] }

# Or use a preset:
# reinhardt = { version = "0.1.0-alpha.1", features = ["standard"] }  # Recommended
# reinhardt = { version = "0.1.0-alpha.1", features = ["full"] }      # All features
```

Then import GraphQL features:

```rust
use reinhardt::graphql::{Schema, Query, Mutation};
use reinhardt::graphql::types::{UserStorage, UserEvent};
```

**Note:** GraphQL features are included in the `standard` and `full` feature presets.

### Optional Features

```toml
# With dependency injection
reinhardt = { version = "0.1.0-alpha.1", features = ["graphql", "di"] }

# With gRPC transport
reinhardt = { version = "0.1.0-alpha.1", features = ["graphql", "grpc"] }
```

## Examples

### Basic GraphQL Usage

```rust
use async_graphql::{EmptySubscription, Schema};
use reinhardt::graphql::schema::{Mutation, Query, UserStorage};

#[tokio::main]
async fn main() {
    let storage = UserStorage::new();
    let schema = Schema::build(Query, Mutation, EmptySubscription)
        .data(storage)
        .finish();

    let query = r#"{ hello(name: "World") }"#;
    let result = schema.execute(query).await;
    println!("{}", result.data);
}
```

### Dependency Injection

Enable the `di` feature to use dependency injection in GraphQL resolvers:

```rust
use async_graphql::{Context, Object, Result, ID, Schema, EmptyMutation, EmptySubscription};
use reinhardt::graphql::{graphql_handler, SchemaBuilderExt};
use reinhardt_di::InjectionContext;
use std::sync::Arc;

// Define your resolvers
pub struct Query;

#[Object]
impl Query {
    async fn user(&self, ctx: &Context<'_>, id: ID) -> Result<User> {
        user_impl(ctx, id).await
    }

    async fn users(&self, ctx: &Context<'_>, limit: Option<i32>) -> Result<Vec<User>> {
        users_impl(ctx, limit).await
    }
}

// Use #[graphql_handler] for DI
#[graphql_handler]
async fn user_impl(
    ctx: &Context<'_>,
    id: ID,
    #[inject] db: DatabaseConnection,  // Auto-injected
    #[inject] cache: RedisCache,       // Auto-injected
) -> Result<User> {
    // Check cache first
    if let Some(user) = cache.get(&id).await {
        return Ok(user);
    }

    // Fetch from database
    let user = db.fetch_user(&id).await?;

    // Update cache
    cache.set(&id, &user).await;

    Ok(user)
}

#[graphql_handler]
async fn users_impl(
    ctx: &Context<'_>,
    limit: Option<i32>,
    #[inject] db: DatabaseConnection,
) -> Result<Vec<User>> {
    let limit = limit.unwrap_or(100);
    let users = db.fetch_users(limit).await?;
    Ok(users)
}

// Build schema with DI context
#[tokio::main]
async fn main() {
    let injection_ctx = Arc::new(InjectionContext::new());

    // Register dependencies
    // injection_ctx.register(DatabaseConnection::new(...));
    // injection_ctx.register(RedisCache::new(...));

    let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
        .with_di_context(injection_ctx)  // Helper method from SchemaBuilderExt
        .finish();

    // Execute query
    let query = r#"{ user(id: "123") { id name email } }"#;
    let result = schema.execute(query).await;
    println!("{}", result.data);
}
```

#### Cache Control

Control dependency caching per parameter:

```rust
#[graphql_handler]
async fn handler(
    ctx: &Context<'_>,
    id: ID,
    #[inject] cached_db: DatabaseConnection,          // Cached (default)
    #[inject(cache = false)] fresh_db: DatabaseConnection,  // Not cached
) -> Result<User> {
    // ...
}
```

### GraphQL over gRPC Server

```rust
use async_graphql::{EmptySubscription, Schema};
use reinhardt::graphql::grpc_service::GraphQLGrpcService;
use reinhardt::graphql::schema::{Mutation, Query, UserStorage};
use reinhardt::grpc::proto::graphql::graph_ql_service_server::GraphQlServiceServer;
use tonic::transport::Server;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let storage = UserStorage::new();
    let schema = Schema::build(Query, Mutation, EmptySubscription)
        .data(storage)
        .finish();

    let service = GraphQLGrpcService::new(schema);
    let grpc_service = GraphQlServiceServer::new(service);

    Server::builder()
        .add_service(grpc_service)
        .serve("127.0.0.1:50051".parse()?)
        .await?;

    Ok(())
}
```

### GraphQL over gRPC Client

```rust
use reinhardt::grpc::proto::graphql::{
    graph_ql_service_client::GraphQlServiceClient,
    GraphQlRequest,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GraphQlServiceClient::connect("http://127.0.0.1:50051").await?;

    let request = tonic::Request::new(GraphQlRequest {
        query: r#"{ hello(name: "gRPC") }"#.to_string(),
        variables: None,
        operation_name: None,
    });

    let response = client.execute_query(request).await?;
    println!("{:?}", response.into_inner());

    Ok(())
}
```

### Running Examples

```bash
# Start gRPC server
cargo run --package reinhardt-graphql --features graphql-grpc --example grpc_server

# In another terminal, run client
cargo run --package reinhardt-graphql --features graphql-grpc --example grpc_client
```

## Testing

```bash
# All tests
cargo test --package reinhardt-graphql --features graphql-grpc

# Integration tests
cargo test --package reinhardt-graphql --features graphql-grpc --test grpc_integration_tests

# Subscription streaming tests
cargo test --package reinhardt-graphql --features graphql-grpc --test grpc_subscription_tests

# E2E tests with real network
cargo test --package reinhardt-graphql --features graphql-grpc --test grpc_e2e_tests

# Performance benchmarks
cargo bench --package reinhardt-graphql --features graphql-grpc
```

## Performance

See [PERFORMANCE.md](PERFORMANCE.md) for detailed benchmarks.

**Summary:**

- Direct GraphQL: ~3-4 µs per query
- gRPC GraphQL: ~4-5 µs per query
- Overhead: 5-21% (+0.2-0.8 µs) for gRPC serialization
- Both approaches are highly performant for real-world applications