mockforge-graphql 0.1.3

GraphQL protocol support for MockForge
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# MockForge GraphQL

GraphQL protocol support for MockForge with schema-based query execution.

This crate provides comprehensive GraphQL mocking capabilities, allowing you to define GraphQL schemas and automatically generate realistic resolvers. Perfect for frontend development, API testing, and GraphQL client development.

## Features

- **Schema-Based Mocking**: Define GraphQL schemas and auto-generate resolvers
- **Query & Mutation Support**: Handle queries, mutations, and subscriptions
- **Full Type System**: Support for scalars, objects, interfaces, unions, enums
- **Introspection**: Built-in GraphQL introspection for tooling integration
- **GraphQL Playground**: Interactive web-based query interface
- **Latency Simulation**: Configurable response delays for realistic testing
- **Error Injection**: Simulate GraphQL errors and partial responses
- **Tracing Integration**: Distributed tracing support with OpenTelemetry

## Quick Start

### Basic GraphQL Server

```rust,no_run
use mockforge_graphql::start;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // Start GraphQL server on port 4000
    start(4000).await?;
    Ok(())
}
```

### Server with Custom Schema

```rust,no_run
use mockforge_graphql::{GraphQLSchema, GraphQLExecutor, create_graphql_router};
use mockforge_core::LatencyProfile;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // Load custom schema
    let schema = GraphQLSchema::from_file("schema.graphql").await?;

    // Configure latency simulation
    let latency = Some(LatencyProfile::fast());

    // Create and start server
    let router = create_graphql_router(latency).await?;
    // ... serve the router
}
```

## GraphQL Schema Definition

Define your GraphQL schema using standard GraphQL SDL (Schema Definition Language):

```graphql
type Query {
  user(id: ID!): User
  users(limit: Int = 10, offset: Int = 0): [User!]!
  posts(userId: ID): [Post!]!
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): Boolean!
}

type Subscription {
  userCreated: User!
  postAdded(userId: ID): Post!
}

type User {
  id: ID!
  name: String!
  email: String!
  avatar: String
  posts: [Post!]!
  createdAt: DateTime!
  updatedAt: DateTime!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  tags: [String!]!
  published: Boolean!
  createdAt: DateTime!
  updatedAt: DateTime!
}

input CreateUserInput {
  name: String!
  email: String!
  avatar: String
}

input UpdateUserInput {
  name: String
  email: String
  avatar: String
}

scalar DateTime

enum UserRole {
  ADMIN
  MODERATOR
  USER
}
```

## Automatic Resolver Generation

MockForge GraphQL automatically generates resolvers with realistic data based on field names and types:

### Query Examples

```bash
# Get single user
curl -X POST http://localhost:4000/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "{ user(id: \"123\") { id name email avatar } }"}'

# Get users with pagination
curl -X POST http://localhost:4000/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "{ users(limit: 5) { id name email posts { title } } }"}'
```

### Mutation Examples

```bash
# Create user
curl -X POST http://localhost:4000/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name email } }",
    "variables": {
      "input": {
        "name": "Alice Johnson",
        "email": "alice@example.com",
        "avatar": "https://example.com/avatar.jpg"
      }
    }
  }'

# Update user
curl -X POST http://localhost:4000/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation UpdateUser($id: ID!, $input: UpdateUserInput!) { updateUser(id: $id, input: $input) { id name email } }",
    "variables": {
      "id": "123",
      "input": { "name": "Alice Smith" }
    }
  }'
```

## GraphQL Playground

Access the interactive GraphQL Playground at `http://localhost:4000/playground` for:

- **Schema Exploration**: Browse types, fields, and relationships
- **Query Builder**: Auto-complete with syntax highlighting
- **Documentation**: Inline field and type documentation
- **History**: Save and replay previous queries
- **Response Viewer**: Formatted JSON responses with error highlighting

## Advanced Features

### Latency Simulation

Simulate realistic network conditions:

```rust,no_run
use mockforge_graphql::start_with_latency;
use mockforge_core::LatencyProfile;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // Simulate slow API
    let latency = LatencyProfile::slow(); // 300-800ms
    start_with_latency(4000, Some(latency)).await?;
    Ok(())
}
```

### Custom Latency Profiles

```rust,no_run
use mockforge_core::LatencyProfile;

// Fixed delay
let fixed_latency = LatencyProfile::with_fixed_delay(500); // 500ms

// Normal distribution
let normal_latency = LatencyProfile::with_normal_distribution(200, 50.0); // mean 200ms, std dev 50ms

// Custom range
let range_latency = LatencyProfile::with_range(100, 1000); // 100-1000ms
```

### Error Injection

Simulate GraphQL errors:

```rust,no_run
use mockforge_graphql::GraphQLExecutor;

// Configure error injection
let executor = GraphQLExecutor::new(schema)
    .with_error_rate(0.1) // 10% error rate
    .with_error_types(vec![
        "USER_NOT_FOUND",
        "VALIDATION_ERROR",
        "INTERNAL_SERVER_ERROR"
    ]);
```

### Tracing Integration

Enable distributed tracing:

```rust,no_run
use mockforge_graphql::graphql_tracing::{create_graphql_span, record_graphql_success};

// Create spans for monitoring
let span = create_graphql_span("query", "GetUser");

// Execute query...

// Record success
record_graphql_success(&span, 150); // 150ms duration
```

## Schema Registry

Manage multiple GraphQL schemas:

```rust,no_run
use mockforge_graphql::GraphQLSchemaRegistry;

// Create registry
let registry = GraphQLSchemaRegistry::new();

// Register schemas
registry.register_schema("v1", schema_v1).await?;
registry.register_schema("v2", schema_v2).await?;

// Switch between versions
registry.set_active_schema("v2").await?;
```

## Integration with MockForge

MockForge GraphQL integrates seamlessly with the broader MockForge ecosystem:

- **MockForge Core**: Shared configuration and latency profiles
- **MockForge CLI**: Command-line GraphQL server management
- **MockForge Data**: Enhanced data generation for GraphQL responses
- **MockForge Observability**: Metrics and tracing integration

## Configuration

### Server Configuration

```rust,no_run
use mockforge_graphql::GraphQLExecutor;
use mockforge_core::LatencyProfile;

// Configure executor
let executor = GraphQLExecutor::new(schema)
    .with_latency_profile(LatencyProfile::normal())
    .with_max_query_depth(10)
    .with_max_query_complexity(1000)
    .with_introspection_enabled(true)
    .with_playground_enabled(true);
```

### Environment Variables

```bash
# Server configuration
export GRAPHQL_PORT=4000
export GRAPHQL_ENABLE_PLAYGROUND=true
export GRAPHQL_ENABLE_INTROSPECTION=true

# Latency simulation
export GRAPHQL_LATENCY_PROFILE=normal
export GRAPHQL_LATENCY_FIXED_MS=200

# Error injection
export GRAPHQL_ERROR_RATE=0.05
export GRAPHQL_ERROR_TYPES="VALIDATION_ERROR,INTERNAL_ERROR"
```

## Testing GraphQL APIs

Use MockForge GraphQL for comprehensive testing:

### Unit Testing

```rust,no_run
use mockforge_graphql::GraphQLExecutor;

#[tokio::test]
async fn test_user_query() {
    let schema = GraphQLSchema::from_string(SCHEMA).await.unwrap();
    let executor = GraphQLExecutor::new(schema);

    let query = r#"
        query GetUser($id: ID!) {
            user(id: $id) {
                id
                name
                email
            }
        }
    "#;

    let variables = serde_json::json!({ "id": "123" });
    let result = executor.execute(query, Some(variables)).await.unwrap();

    assert!(result.errors.is_empty());
    assert!(result.data.is_object());
}
```

### Integration Testing

```rust,no_run
use reqwest::Client;

#[tokio::test]
async fn test_graphql_endpoint() {
    let client = Client::new();

    let query = serde_json::json!({
        "query": "{ users { id name } }",
        "variables": null
    });

    let response = client
        .post("http://localhost:4000/graphql")
        .json(&query)
        .send()
        .await
        .unwrap();

    assert_eq!(response.status(), 200);

    let result: serde_json::Value = response.json().await.unwrap();
    assert!(result.get("data").is_some());
}
```

## Performance Considerations

- **Schema Complexity**: Large schemas may impact startup time
- **Query Depth**: Limit maximum query depth to prevent abuse
- **Caching**: Enable response caching for repeated queries
- **Connection Pooling**: Use connection pooling for database resolvers

## Examples

### Complete Server Setup

```rust,no_run
use axum::{routing::get, Router};
use mockforge_graphql::{create_graphql_router, GraphQLSchema};
use mockforge_core::LatencyProfile;
use std::net::SocketAddr;
use tower_http::cors::CorsLayer;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // Load schema
    let schema = GraphQLSchema::from_file("schema.graphql").await?;

    // Configure latency
    let latency = Some(LatencyProfile::normal());

    // Create GraphQL router
    let graphql_router = create_graphql_router(latency).await?;

    // Add CORS and other middleware
    let app = Router::new()
        .merge(graphql_router)
        .layer(CorsLayer::permissive());

    // Start server
    let addr = SocketAddr::from(([127, 0, 0, 1], 4000));
    println!("🚀 GraphQL server running at http://{}", addr);
    println!("📖 GraphQL Playground at http://{}/playground", addr);

    axum::serve(tokio::net::TcpListener::bind(addr).await?, app).await?;

    Ok(())
}
```

### Custom Resolvers

```rust,no_run
use async_graphql::*;
use mockforge_graphql::GraphQLSchema;

// Define custom resolvers
struct Query;

#[Object]
impl Query {
    async fn custom_user(&self, ctx: &Context<'_>, id: ID) -> Result<User> {
        // Custom logic here
        Ok(User {
            id,
            name: "Custom User".to_string(),
            email: "custom@example.com".to_string(),
        })
    }
}

// Register custom resolvers
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
    .data(custom_data)
    .finish();
```

## Troubleshooting

### Common Issues

**Schema validation errors:**
- Check GraphQL syntax in your schema files
- Ensure all referenced types are defined
- Validate field names and type references

**Query execution errors:**
- Verify query syntax
- Check variable types match schema
- Ensure query depth doesn't exceed limits

**Performance issues:**
- Profile query execution times
- Check for N+1 query problems
- Optimize resolver implementations

## Related Crates

- [`mockforge-core`]https://docs.rs/mockforge-core: Core mocking functionality
- [`mockforge-data`]https://docs.rs/mockforge-data: Synthetic data generation
- [`async-graphql`]https://docs.rs/async-graphql: Underlying GraphQL implementation

## License

Licensed under MIT OR Apache-2.0