1use axum::Router;
3use axum::http::StatusCode;
4use axum::response::{IntoResponse, Response};
5use axum::routing::post;
6use serde::Deserialize;
7use serde_json::{Map, Value};
8use std::collections::HashMap;
9use std::sync::Arc;
10
11mod parser;
12mod validation;
13
14pub use parser::{FieldNode, Operation, SelectionSet};
15pub use validation::QueryLimits;
16
17type Resolver = Arc<
18 dyn Fn(
19 serde_json::Value,
20 ) -> std::pin::Pin<
21 Box<dyn std::future::Future<Output = Result<serde_json::Value, String>> + Send>,
22 > + Send
23 + Sync,
24>;
25
26#[derive(Debug, Clone)]
28pub struct FieldRequest {
29 pub args: Map<String, Value>,
30 pub variables: Value,
31 pub selection: Option<SelectionSet>,
32}
33
34#[async_trait::async_trait]
39pub trait GraphQLField: Send + Sync {
40 async fn resolve(&self, req: FieldRequest) -> Result<Value, String>;
41}
42
43enum FieldHandler {
44 Legacy(Resolver),
45 Rich(Arc<dyn GraphQLField>),
46}
47
48pub struct GraphQLSchema {
49 query_resolvers: HashMap<String, FieldHandler>,
50 mutation_resolvers: HashMap<String, FieldHandler>,
51 limits: QueryLimits,
52}
53
54impl GraphQLSchema {
55 pub fn new() -> Self {
56 Self {
57 query_resolvers: HashMap::new(),
58 mutation_resolvers: HashMap::new(),
59 limits: QueryLimits::default(),
60 }
61 }
62
63 pub fn with_limits(mut self, limits: QueryLimits) -> Self {
65 self.limits = limits;
66 self
67 }
68
69 pub fn query(mut self, name: impl Into<String>, r: Resolver) -> Self {
70 self.query_resolvers
71 .insert(name.into(), FieldHandler::Legacy(r));
72 self
73 }
74
75 pub fn mutation(mut self, name: impl Into<String>, r: Resolver) -> Self {
76 self.mutation_resolvers
77 .insert(name.into(), FieldHandler::Legacy(r));
78 self
79 }
80
81 pub fn query_fn(
82 self,
83 name: impl Into<String>,
84 f: impl Fn(
85 serde_json::Value,
86 ) -> std::pin::Pin<
87 Box<dyn std::future::Future<Output = Result<serde_json::Value, String>> + Send>,
88 > + Send
89 + Sync
90 + 'static,
91 ) -> Self {
92 self.query(name, Arc::new(f))
93 }
94
95 pub fn query_field(mut self, name: impl Into<String>, f: impl GraphQLField + 'static) -> Self {
97 self.query_resolvers
98 .insert(name.into(), FieldHandler::Rich(Arc::new(f)));
99 self
100 }
101
102 pub fn mutation_field(
104 mut self,
105 name: impl Into<String>,
106 f: impl GraphQLField + 'static,
107 ) -> Self {
108 self.mutation_resolvers
109 .insert(name.into(), FieldHandler::Rich(Arc::new(f)));
110 self
111 }
112}
113
114impl Default for GraphQLSchema {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120#[derive(Debug, Deserialize)]
121struct GqlReq {
122 query: String,
123 #[serde(default)]
124 variables: serde_json::Value,
125}
126
127pub fn graphql_router(schema: GraphQLSchema) -> Router {
128 let schema = Arc::new(schema);
129
130 async fn handler(axum::Json(req): axum::Json<GqlReq>, schema: Arc<GraphQLSchema>) -> Response {
131 match execute(&schema, &req.query, &req.variables).await {
132 Ok(data) => axum::Json(serde_json::json!({"data": data})).into_response(),
133 Err(errors) => (
134 StatusCode::BAD_REQUEST,
135 axum::Json(serde_json::json!({"errors": errors})),
136 )
137 .into_response(),
138 }
139 }
140
141 let s = Arc::clone(&schema);
142 Router::new().route("/graphql", post(move |body| handler(body, Arc::clone(&s))))
143}
144
145async fn execute(
146 schema: &GraphQLSchema,
147 query: &str,
148 variables: &serde_json::Value,
149) -> Result<serde_json::Value, Vec<String>> {
150 let trimmed = query.trim();
151 let mut errors = Vec::new();
152
153 let field = match parser::parse_query(trimmed, variables) {
154 Ok(f) => f,
155 Err(e) => {
156 errors.push(e);
157 return Err(errors);
158 }
159 };
160
161 if let Err(e) = validation::validate(&field, &schema.limits) {
162 errors.push(e);
163 return Err(errors);
164 }
165
166 let (resolvers, field_name) = if field.operation == Operation::Mutation {
167 (&schema.mutation_resolvers, &field.name)
168 } else {
169 (&schema.query_resolvers, &field.name)
170 };
171
172 match resolvers.get(field_name) {
173 Some(FieldHandler::Legacy(resolver)) => {
174 let vars = match merge_args(variables, &field.args) {
175 Ok(v) => v,
176 Err(e) => {
177 errors.push(e);
178 return Err(errors);
179 }
180 };
181 match resolver(vars).await {
182 Ok(data) => {
183 let mut result = serde_json::Map::new();
184 result.insert(field.name.clone(), data);
185 return Ok(serde_json::Value::Object(result));
186 }
187 Err(e) => errors.push(e),
188 }
189 }
190 Some(FieldHandler::Rich(r)) => {
191 let req = FieldRequest {
192 args: field.args,
193 variables: variables.clone(),
194 selection: field.selection,
195 };
196 match r.resolve(req).await {
197 Ok(data) => {
198 let mut result = serde_json::Map::new();
199 result.insert(field.name.clone(), data);
200 return Ok(serde_json::Value::Object(result));
201 }
202 Err(e) => errors.push(e),
203 }
204 }
205 None => errors.push(format!("unknown field: {field_name}")),
206 }
207
208 Err(errors)
209}
210
211fn merge_args(variables: &Value, args: &Map<String, Value>) -> Result<Value, String> {
214 if args.is_empty() {
215 return Ok(variables.clone());
216 }
217 match variables {
218 Value::Object(m) => {
219 let mut merged = m.clone();
220 for (k, v) in args {
221 merged.insert(k.clone(), v.clone());
222 }
223 Ok(Value::Object(merged))
224 }
225 Value::Null => Ok(Value::Object(args.clone())),
226 _ => Err("variables must be a JSON object when field arguments are present".into()),
227 }
228}
229
230#[cfg(test)]
231mod tests;