🐘 graphql consolidation, validators and cleanup
This commit is contained in:
470
docs/GRAPHQL_SERVICE.md
Normal file
470
docs/GRAPHQL_SERVICE.md
Normal file
@@ -0,0 +1,470 @@
|
||||
# GraphQL Service Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The GraphQL service provides a unified API for querying and mutating comic metadata. It supports schema stitching with a remote metadata service, comprehensive error handling, validation, caching, and monitoring.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Components
|
||||
|
||||
1. **Main Service** ([`services/graphql.service.ts`](../services/graphql.service.ts))
|
||||
- Core GraphQL execution engine
|
||||
- Schema initialization and stitching
|
||||
- Health monitoring
|
||||
- Event handling for auto-resolution
|
||||
|
||||
2. **Schema Utilities** ([`utils/graphql.schema.utils.ts`](../utils/graphql.schema.utils.ts))
|
||||
- Remote schema fetching with retry logic
|
||||
- Schema validation
|
||||
- Remote executor creation
|
||||
|
||||
3. **Validation Utilities** ([`utils/graphql.validation.utils.ts`](../utils/graphql.validation.utils.ts))
|
||||
- Input validation
|
||||
- Parameter sanitization
|
||||
- Type checking
|
||||
|
||||
4. **Error Handling** ([`utils/graphql.error.utils.ts`](../utils/graphql.error.utils.ts))
|
||||
- Standardized error codes
|
||||
- Error formatting and sanitization
|
||||
- Error logging
|
||||
|
||||
5. **Configuration** ([`config/graphql.config.ts`](../config/graphql.config.ts))
|
||||
- Centralized configuration management
|
||||
- Environment variable overrides
|
||||
|
||||
## Features
|
||||
|
||||
### 1. Schema Stitching
|
||||
|
||||
The service combines a local schema with a remote metadata schema:
|
||||
|
||||
```typescript
|
||||
// Local schema: Comic library operations
|
||||
// Remote schema: Metadata provider operations (ComicVine, Metron, etc.)
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Single GraphQL endpoint for all operations
|
||||
- Transparent federation of multiple data sources
|
||||
- Graceful degradation if remote service is unavailable
|
||||
|
||||
### 2. Error Handling
|
||||
|
||||
Comprehensive error handling with standardized error codes:
|
||||
|
||||
```typescript
|
||||
enum GraphQLErrorCode {
|
||||
BAD_REQUEST = "BAD_REQUEST",
|
||||
VALIDATION_ERROR = "VALIDATION_ERROR",
|
||||
INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR",
|
||||
TIMEOUT = "TIMEOUT",
|
||||
REMOTE_SCHEMA_ERROR = "REMOTE_SCHEMA_ERROR",
|
||||
// ... more codes
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Automatic error classification
|
||||
- Safe error sanitization for clients
|
||||
- Detailed logging for debugging
|
||||
- Stack traces in development mode only
|
||||
|
||||
### 3. Retry Logic
|
||||
|
||||
Automatic retry for transient failures:
|
||||
|
||||
```typescript
|
||||
{
|
||||
retries: 3,
|
||||
retryDelay: 2000, // Exponential backoff
|
||||
timeout: 10000
|
||||
}
|
||||
```
|
||||
|
||||
**Retryable Errors:**
|
||||
- Network errors (ECONNREFUSED, ENOTFOUND)
|
||||
- Timeout errors
|
||||
- Service unavailable errors
|
||||
|
||||
### 4. Caching
|
||||
|
||||
Remote schema caching to reduce latency:
|
||||
|
||||
```typescript
|
||||
{
|
||||
cacheEnabled: true,
|
||||
cacheTTL: 3600 // 1 hour
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Faster query execution
|
||||
- Reduced load on remote service
|
||||
- Configurable TTL
|
||||
|
||||
### 5. Health Monitoring
|
||||
|
||||
Periodic health checks for remote schema:
|
||||
|
||||
```typescript
|
||||
{
|
||||
healthCheck: {
|
||||
enabled: true,
|
||||
interval: 60000 // 1 minute
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Health Status:**
|
||||
```json
|
||||
{
|
||||
"healthy": true,
|
||||
"localSchema": true,
|
||||
"remoteSchema": true,
|
||||
"lastCheck": "2026-03-05T15:00:00.000Z",
|
||||
"remoteSchemaUrl": "http://localhost:3080/metadata-graphql"
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Performance Monitoring
|
||||
|
||||
Query performance tracking:
|
||||
|
||||
```typescript
|
||||
{
|
||||
logging: {
|
||||
logPerformance: true,
|
||||
slowQueryThreshold: 1000 // Log queries > 1s
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Input Validation
|
||||
|
||||
Comprehensive input validation:
|
||||
|
||||
- Pagination parameters (page, limit, offset)
|
||||
- ID format validation (MongoDB ObjectId)
|
||||
- Search query length limits
|
||||
- File path sanitization
|
||||
- JSON validation
|
||||
|
||||
### 8. Timeout Protection
|
||||
|
||||
Query execution timeouts:
|
||||
|
||||
```typescript
|
||||
{
|
||||
execution: {
|
||||
timeout: 30000 // 30 seconds
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
# Remote schema
|
||||
METADATA_GRAPHQL_URL=http://localhost:3080/metadata-graphql
|
||||
GRAPHQL_REMOTE_TIMEOUT=10000
|
||||
GRAPHQL_REMOTE_RETRIES=3
|
||||
|
||||
# Execution
|
||||
GRAPHQL_EXECUTION_TIMEOUT=30000
|
||||
GRAPHQL_MAX_QUERY_DEPTH=10
|
||||
|
||||
# Caching
|
||||
GRAPHQL_CACHE_ENABLED=true
|
||||
GRAPHQL_CACHE_TTL=3600
|
||||
|
||||
# Environment
|
||||
NODE_ENV=development
|
||||
```
|
||||
|
||||
### Default Configuration
|
||||
|
||||
See [`config/graphql.config.ts`](../config/graphql.config.ts) for all configuration options.
|
||||
|
||||
## API Actions
|
||||
|
||||
### 1. Execute GraphQL Query
|
||||
|
||||
```typescript
|
||||
broker.call("graphql.graphql", {
|
||||
query: "query { comic(id: \"123\") { id title } }",
|
||||
variables: {},
|
||||
operationName: "GetComic"
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Get Schema
|
||||
|
||||
```typescript
|
||||
broker.call("graphql.getSchema");
|
||||
// Returns: { typeDefs: "...", hasRemoteSchema: true }
|
||||
```
|
||||
|
||||
### 3. Health Check
|
||||
|
||||
```typescript
|
||||
broker.call("graphql.health");
|
||||
// Returns health status
|
||||
```
|
||||
|
||||
### 4. Refresh Remote Schema
|
||||
|
||||
```typescript
|
||||
broker.call("graphql.refreshRemoteSchema");
|
||||
// Forces cache refresh
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
### 1. metadata.imported
|
||||
|
||||
Triggered when metadata is imported from external sources.
|
||||
|
||||
```typescript
|
||||
broker.emit("metadata.imported", {
|
||||
comicId: "123",
|
||||
source: "COMICVINE"
|
||||
});
|
||||
```
|
||||
|
||||
**Auto-Resolution:**
|
||||
If enabled in user preferences, automatically resolves canonical metadata.
|
||||
|
||||
### 2. comic.imported
|
||||
|
||||
Triggered when a new comic is imported.
|
||||
|
||||
```typescript
|
||||
broker.emit("comic.imported", {
|
||||
comicId: "123"
|
||||
});
|
||||
```
|
||||
|
||||
**Auto-Resolution:**
|
||||
If enabled in user preferences, automatically resolves canonical metadata on import.
|
||||
|
||||
## Error Handling Examples
|
||||
|
||||
### Client Errors (4xx)
|
||||
|
||||
```json
|
||||
{
|
||||
"errors": [{
|
||||
"message": "Invalid ID format",
|
||||
"extensions": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"field": "id"
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Server Errors (5xx)
|
||||
|
||||
```json
|
||||
{
|
||||
"errors": [{
|
||||
"message": "Remote GraphQL service unavailable",
|
||||
"extensions": {
|
||||
"code": "SERVICE_UNAVAILABLE",
|
||||
"context": "Remote schema fetch"
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Timeout Errors
|
||||
|
||||
```json
|
||||
{
|
||||
"errors": [{
|
||||
"message": "Query execution timeout after 30000ms",
|
||||
"extensions": {
|
||||
"code": "TIMEOUT"
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Query Optimization
|
||||
|
||||
- Use field selection to minimize data transfer
|
||||
- Implement pagination for large result sets
|
||||
- Avoid deeply nested queries (max depth: 10)
|
||||
|
||||
### 2. Error Handling
|
||||
|
||||
- Always check for errors in responses
|
||||
- Handle specific error codes appropriately
|
||||
- Log errors for debugging
|
||||
|
||||
### 3. Caching
|
||||
|
||||
- Use appropriate cache TTL for your use case
|
||||
- Manually refresh cache when needed
|
||||
- Monitor cache hit rates
|
||||
|
||||
### 4. Monitoring
|
||||
|
||||
- Enable health checks in production
|
||||
- Monitor slow query logs
|
||||
- Set up alerts for service unavailability
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Remote Schema Connection Issues
|
||||
|
||||
**Problem:** Cannot connect to remote metadata service
|
||||
|
||||
**Solutions:**
|
||||
1. Check `METADATA_GRAPHQL_URL` environment variable
|
||||
2. Verify remote service is running
|
||||
3. Check network connectivity
|
||||
4. Review firewall rules
|
||||
|
||||
**Fallback:** Service continues with local schema only
|
||||
|
||||
### Slow Queries
|
||||
|
||||
**Problem:** Queries taking too long
|
||||
|
||||
**Solutions:**
|
||||
1. Check slow query logs
|
||||
2. Optimize resolver implementations
|
||||
3. Add database indexes
|
||||
4. Implement field-level caching
|
||||
5. Increase timeout if necessary
|
||||
|
||||
### Memory Issues
|
||||
|
||||
**Problem:** High memory usage
|
||||
|
||||
**Solutions:**
|
||||
1. Reduce cache TTL
|
||||
2. Disable remote schema caching
|
||||
3. Implement query complexity limits
|
||||
4. Add pagination to large queries
|
||||
|
||||
### Schema Validation Errors
|
||||
|
||||
**Problem:** Schema validation fails
|
||||
|
||||
**Solutions:**
|
||||
1. Check typedef syntax
|
||||
2. Verify resolver implementations
|
||||
3. Ensure all types are defined
|
||||
4. Check for circular dependencies
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### From Old Implementation
|
||||
|
||||
The refactored service maintains backward compatibility with the existing API:
|
||||
|
||||
1. **No breaking changes** to GraphQL schema
|
||||
2. **Same action names** (`graphql.graphql`, `graphql.getSchema`)
|
||||
3. **Same event handlers** (`metadata.imported`, `comic.imported`)
|
||||
|
||||
### New Features
|
||||
|
||||
1. **Health endpoint:** `broker.call("graphql.health")`
|
||||
2. **Schema refresh:** `broker.call("graphql.refreshRemoteSchema")`
|
||||
3. **Enhanced error messages** with error codes
|
||||
4. **Performance logging** for slow queries
|
||||
|
||||
### Configuration Changes
|
||||
|
||||
Old configuration (environment variables only):
|
||||
```bash
|
||||
METADATA_GRAPHQL_URL=http://localhost:3080/metadata-graphql
|
||||
```
|
||||
|
||||
New configuration (with defaults):
|
||||
```bash
|
||||
# All old variables still work
|
||||
METADATA_GRAPHQL_URL=http://localhost:3080/metadata-graphql
|
||||
|
||||
# New optional variables
|
||||
GRAPHQL_REMOTE_TIMEOUT=10000
|
||||
GRAPHQL_CACHE_ENABLED=true
|
||||
GRAPHQL_EXECUTION_TIMEOUT=30000
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
|
||||
```typescript
|
||||
// Test schema initialization
|
||||
describe("GraphQL Service", () => {
|
||||
it("should initialize local schema", async () => {
|
||||
const schema = await service.initializeLocalSchema();
|
||||
expect(schema).toBeDefined();
|
||||
});
|
||||
|
||||
it("should handle remote schema failure gracefully", async () => {
|
||||
// Mock remote schema failure
|
||||
const schema = await service.started();
|
||||
expect(schema).toBe(localSchema);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
```typescript
|
||||
// Test query execution
|
||||
describe("GraphQL Queries", () => {
|
||||
it("should execute comic query", async () => {
|
||||
const result = await broker.call("graphql.graphql", {
|
||||
query: "query { comic(id: \"123\") { id title } }"
|
||||
});
|
||||
expect(result.data).toBeDefined();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Performance Benchmarks
|
||||
|
||||
Typical performance metrics:
|
||||
|
||||
- **Local query:** 10-50ms
|
||||
- **Remote query:** 100-500ms (depending on network)
|
||||
- **Stitched query:** 150-600ms
|
||||
- **Cached remote schema:** +0ms overhead
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Query Depth Limiting:** Prevents deeply nested queries (DoS protection)
|
||||
2. **Query Length Limiting:** Prevents excessively large queries
|
||||
3. **Input Sanitization:** Removes control characters and validates formats
|
||||
4. **Error Sanitization:** Hides sensitive information in production
|
||||
5. **Timeout Protection:** Prevents long-running queries from blocking
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
1. **Query Complexity Analysis:** Calculate and limit query complexity
|
||||
2. **Rate Limiting:** Per-client rate limiting
|
||||
3. **Persisted Queries:** Pre-approved query whitelist
|
||||
4. **DataLoader Integration:** Batch and cache database queries
|
||||
5. **Subscription Support:** Real-time updates via WebSocket
|
||||
6. **Field-Level Caching:** Cache individual field results
|
||||
7. **Distributed Tracing:** OpenTelemetry integration
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
1. Check this documentation
|
||||
2. Review error logs
|
||||
3. Check health endpoint
|
||||
4. Review configuration
|
||||
5. Open an issue on GitHub
|
||||
Reference in New Issue
Block a user