9.9 KiB
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
-
Main Service (
services/graphql.service.ts)- Core GraphQL execution engine
- Schema initialization and stitching
- Health monitoring
- Event handling for auto-resolution
-
Schema Utilities (
utils/graphql.schema.utils.ts)- Remote schema fetching with retry logic
- Schema validation
- Remote executor creation
-
Validation Utilities (
utils/graphql.validation.utils.ts)- Input validation
- Parameter sanitization
- Type checking
-
Error Handling (
utils/graphql.error.utils.ts)- Standardized error codes
- Error formatting and sanitization
- Error logging
-
Configuration (
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:
// 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:
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:
{
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:
{
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:
{
healthCheck: {
enabled: true,
interval: 60000 // 1 minute
}
}
Health Status:
{
"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:
{
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:
{
execution: {
timeout: 30000 // 30 seconds
}
}
Configuration
Environment Variables
# 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 for all configuration options.
API Actions
1. Execute GraphQL Query
broker.call("graphql.graphql", {
query: "query { comic(id: \"123\") { id title } }",
variables: {},
operationName: "GetComic"
});
2. Get Schema
broker.call("graphql.getSchema");
// Returns: { typeDefs: "...", hasRemoteSchema: true }
3. Health Check
broker.call("graphql.health");
// Returns health status
4. Refresh Remote Schema
broker.call("graphql.refreshRemoteSchema");
// Forces cache refresh
Events
1. metadata.imported
Triggered when metadata is imported from external sources.
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.
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)
{
"errors": [{
"message": "Invalid ID format",
"extensions": {
"code": "VALIDATION_ERROR",
"field": "id"
}
}]
}
Server Errors (5xx)
{
"errors": [{
"message": "Remote GraphQL service unavailable",
"extensions": {
"code": "SERVICE_UNAVAILABLE",
"context": "Remote schema fetch"
}
}]
}
Timeout Errors
{
"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:
- Check
METADATA_GRAPHQL_URLenvironment variable - Verify remote service is running
- Check network connectivity
- Review firewall rules
Fallback: Service continues with local schema only
Slow Queries
Problem: Queries taking too long
Solutions:
- Check slow query logs
- Optimize resolver implementations
- Add database indexes
- Implement field-level caching
- Increase timeout if necessary
Memory Issues
Problem: High memory usage
Solutions:
- Reduce cache TTL
- Disable remote schema caching
- Implement query complexity limits
- Add pagination to large queries
Schema Validation Errors
Problem: Schema validation fails
Solutions:
- Check typedef syntax
- Verify resolver implementations
- Ensure all types are defined
- Check for circular dependencies
Migration Guide
From Old Implementation
The refactored service maintains backward compatibility with the existing API:
- No breaking changes to GraphQL schema
- Same action names (
graphql.graphql,graphql.getSchema) - Same event handlers (
metadata.imported,comic.imported)
New Features
- Health endpoint:
broker.call("graphql.health") - Schema refresh:
broker.call("graphql.refreshRemoteSchema") - Enhanced error messages with error codes
- Performance logging for slow queries
Configuration Changes
Old configuration (environment variables only):
METADATA_GRAPHQL_URL=http://localhost:3080/metadata-graphql
New configuration (with defaults):
# 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
// 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
// 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
- Query Depth Limiting: Prevents deeply nested queries (DoS protection)
- Query Length Limiting: Prevents excessively large queries
- Input Sanitization: Removes control characters and validates formats
- Error Sanitization: Hides sensitive information in production
- Timeout Protection: Prevents long-running queries from blocking
Future Enhancements
- Query Complexity Analysis: Calculate and limit query complexity
- Rate Limiting: Per-client rate limiting
- Persisted Queries: Pre-approved query whitelist
- DataLoader Integration: Batch and cache database queries
- Subscription Support: Real-time updates via WebSocket
- Field-Level Caching: Cache individual field results
- Distributed Tracing: OpenTelemetry integration
Support
For issues or questions:
- Check this documentation
- Review error logs
- Check health endpoint
- Review configuration
- Open an issue on GitHub