ADR 0007: GraphQL HTTP Integration¶
Status: Accepted
Date: 2025-12-27
Deciders: AI Assistant
Type: Feature Integration
Context¶
Spikard provides a multi-language toolkit for building typed web services. GraphQL is a popular query language and runtime for APIs that complements REST and JSON-RPC. The spikard-graphql crate already provides a high-level GraphQL implementation with async-graphql integration, but it was not formally integrated with the spikard-http HTTP server.
This ADR documents how GraphQL support is integrated into Spikard's HTTP runtime while maintaining:
- Zero circular dependencies in the crate dependency graph
- Compatibility with tower-http middleware stack
- The Handler trait abstraction for language-agnostic routing
- Consistent error handling across platforms
Decision¶
We integrate GraphQL with spikard-http by:
-
Placing integration utilities in spikard-graphql - The routes module lives in spikard-graphql (which depends on spikard-http), not in spikard-http itself, avoiding circular dependencies.
-
Leveraging existing Handler trait - GraphQLHandler already implements Handler, enabling transparent integration with Spikard's HTTP server and middleware stack.
-
Providing route configuration builder - GraphQLRouteConfig in the routes module provides a declarative way to configure GraphQL endpoints.
-
Maintaining middleware compatibility - All tower-http middleware (compression, rate limiting, request IDs, etc.) applies transparently to GraphQL requests.
Consequences¶
Positive¶
- No circular dependencies - spikard-graphql can safely depend on spikard-http without creating dependency cycles
- Clean architecture - Integration code lives in the crate that owns the GraphQL implementation
- Handler reuse - GraphQLHandler implements Handler from day one (no wrapper needed)
- Middleware transparency - All HTTP middleware works with GraphQL routes automatically
- First-class citizen - GraphQL routes are registered exactly like REST routes via Router
- Type safety - GraphQLRouteConfig uses builder pattern for declarative configuration
- Language-agnostic - Integration strategy works for all language bindings (Python, Node, Ruby, PHP, WASM)
Negative¶
- Slight import discrepancy - Developers must import from spikard_graphql::routes instead of spikard_http::graphql
- Mitigation: Clear documentation and examples in each location
Neutral¶
- Optional dependency - While spikard-graphql doesn't depend on spikard-http optionally, routes are unconditionally available to users who depend on both
- This is acceptable because spikard-graphql requires spikard-http; no added optionality is needed
Implementation¶
Core Components¶
- GraphQLHandler (existing) - Implements Handler trait for GraphQL requests
- GraphQLRouteConfig (new) - Declarative configuration builder
- routes module (new) - Integration utilities and documentation
Request Flow¶
HTTP POST /graphql
↓
Tower-HTTP Middleware (compression, rate limiting, auth, etc.)
↓
Spikard Router.route_request()
↓
GraphQLHandler::call() (implements Handler)
↓
Parse GraphQL request (query, variables, operation name)
↓
GraphQLExecutor::execute()
↓
Format GraphQL response (data + errors)
↓
HTTP Response (200 OK with GraphQL JSON)
Example Integration¶
use spikard_graphql::{GraphQLHandler, GraphQLExecutor, QueryOnlyConfig, SchemaBuilder};
use spikard_http::{Handler, Route, Router, Server, ServerConfig};
use std::sync::Arc;
// Create schema
let schema = SchemaBuilder::new()
.query_only(QueryOnlyConfig::builder().build())
.build();
// Create handler
let executor = Arc::new(GraphQLExecutor::<Query, (), ()>::new(schema));
let handler = Arc::new(GraphQLHandler::new(executor));
// Register route
let route = Route::new(
"/graphql".to_string(),
spikard_http::Method::Post,
handler as Arc<dyn Handler>,
);
// Start server
let config = ServerConfig::builder().port(8000).build();
let app = Server::with_handlers_and_metadata(
config.clone(),
vec![(route, handler as Arc<dyn Handler>)],
vec![route_metadata],
)?;
Server::run_with_config(app, config).await?;
Error Handling¶
GraphQL errors follow the GraphQL specification while respecting HTTP conventions:
| Error Type | HTTP Status | Example |
|---|---|---|
| Invalid JSON | 400 Bad Request | Invalid request body |
| GraphQL validation | 200 OK | Invalid query syntax (per GraphQL spec) |
| Query execution | 200 OK | Field not found, type mismatch (per GraphQL spec) |
| Internal server error | 500 Internal Server Error | Unexpected panic or resource exhaustion |
The GraphQL response always includes an errors field when applicable, following the GraphQL over HTTP specification.
Testing¶
The integration is validated through:
- Unit tests in routes.rs for GraphQLRouteConfig
- Handler tests existing in handler.rs (unchanged)
- Integration examples in examples/graphql/
- Fixture-based tests in testing_data/graphql/ (following project convention)
Alternative Approaches Considered¶
1. Add GraphQL as optional feature in spikard-http¶
Rejected because:
- Creates optional dependency coupling
- spikard-http doesn't own GraphQL domain
- Adds bloat to spikard-http if feature is enabled
2. Create separate spikard-http-graphql crate¶
Rejected because:
- GraphQL already depends on spikard-http
- Would create unnecessary indirection
- Splits related functionality across crates
3. Keep everything in spikard-graphql (chosen)¶
Selected because:
- spikard-graphql already depends on spikard-http
- Clean separation: routing helpers in domain crate
- No circular dependencies
- Follows project layering pattern
Related Decisions¶
- ADR 0002: Runtime and Middleware - tower-http middleware stack
- ADR 0001: Architecture and Layering - Thin binding pattern
Future Enhancements¶
- GraphQL Playground UI - Optional endpoint at /graphql/ui (when GraphQLRouteConfig.enable_playground = true)
- Apollo Federation support - Via spikard-graphql federation feature
- Subscription support - WebSocket integration (already supported in spikard-http)
- Persisted queries - Query ID mapping for reduced payload size
- Complexity analysis - Configurable complexity limits per query
- Language-specific bindings - GraphQL route helpers for Python/Node/Ruby/PHP (thin adapters around Rust implementation)