Skip to main content

oxidite_graphql/
lib.rs

1//! GraphQL integration for Oxidite
2//!
3//! Provides GraphQL API capabilities with automatic schema generation
4
5pub mod schema;
6pub mod resolver;
7pub mod context;
8
9pub use schema::GraphQLSchema;
10pub use context::Context;
11
12use oxidite_core::{Router, Result};
13use juniper::RootNode;
14use http_body_util::BodyExt;
15
16/// GraphQL handler for Oxidite
17pub struct GraphQLHandler {
18    schema: std::sync::Arc<RootNode<'static, schema::QueryRoot, schema::MutationRoot, juniper::EmptySubscription<Context>>>,
19}
20
21impl GraphQLHandler {
22    pub fn new(schema: RootNode<'static, schema::QueryRoot, schema::MutationRoot, juniper::EmptySubscription<Context>>) -> Self {
23        Self {
24            schema: std::sync::Arc::new(schema),
25        }
26    }
27
28    /// Mount GraphQL endpoint to router
29    pub fn mount(&self, router: &mut Router) -> Result<()> {
30        let schema = self.schema.clone();
31        
32        // POST endpoint for GraphQL queries
33        router.post("/graphql", move |req: oxidite_core::OxiditeRequest| {
34            let schema = schema.clone();
35            async move {
36                // Read request body
37                let body_bytes = req.into_body()
38                    .collect()
39                    .await
40                    .map_err(|e| oxidite_core::Error::BadRequest(format!("Failed to read body: {}", e)))?
41                    .to_bytes();
42                
43                // Parse GraphQL request
44                let graphql_request: juniper::http::GraphQLRequest = serde_json::from_slice(&body_bytes)
45                    .map_err(|e| oxidite_core::Error::BadRequest(format!("Invalid GraphQL request: {}", e)))?;
46                
47                // Create context
48                let context = Context::new();
49                
50                // Execute query
51                let response = graphql_request.execute_sync(&schema, &context);
52                
53                // Return JSON response
54                Ok(oxidite_core::OxiditeResponse::json(response))
55            }
56        });
57        
58        // GET endpoint for GraphQL playground
59        let schema_clone = self.schema.clone();
60        router.get("/graphql", move |_req: oxidite_core::OxiditeRequest| {
61            async move {
62                let html = r#"<!DOCTYPE html>
63<html lang="en">
64<head>
65    <meta charset="UTF-8">
66    <meta name="viewport" content="width=device-width, initial-scale=1.0">
67    <title>GraphQL Playground</title>
68    <style>
69        body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
70        #playground { height: 100vh; }
71    </style>
72    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/graphql-playground-react/build/static/css/index.css" />
73</head>
74<body>
75    <div id="playground"></div>
76    <script src="https://cdn.jsdelivr.net/npm/graphql-playground-react/build/static/js/middleware.js"></script>
77    <script>
78        GraphQLPlayground.init(document.getElementById('playground'), {
79            endpoint: '/graphql'
80        })
81    </script>
82</body>
83</html>"#;
84                Ok(oxidite_core::OxiditeResponse::html(html))
85            }
86        });
87        
88        Ok(())
89    }
90}
91
92/// Create a default GraphQL handler
93pub fn create_handler() -> GraphQLHandler {
94    GraphQLHandler::new(schema::create_schema())
95}
96