apollo_compiler/response.rs
1//! GraphQL [responses](https://spec.graphql.org/September2025/#sec-Response)
2//!
3//! This exists primarily to support [`introspection::partial_execute`].
4
5#[cfg(doc)]
6use crate::introspection;
7use crate::parser::LineColumn;
8use crate::parser::SourceMap;
9use crate::parser::SourceSpan;
10use serde::Deserialize;
11use serde::Serialize;
12/// Re-export of the version of the `serde_json_bytes` crate used for [`JsonValue`] and [`JsonMap`]
13pub use serde_json_bytes;
14
15/// A JSON-compatible dynamically-typed value.
16///
17/// Note: [`serde_json_bytes::Value`] is similar
18/// to [`serde_json::Value`][serde_json_bytes::serde_json::Value]
19/// but uses its reference-counted [`ByteString`][serde_json_bytes::ByteString]
20/// for string values and map keys.
21pub type JsonValue = serde_json_bytes::Value;
22
23/// A JSON-compatible object/map with string keys and dynamically-typed values.
24pub type JsonMap = serde_json_bytes::Map<serde_json_bytes::ByteString, JsonValue>;
25
26/// A [response](https://spec.graphql.org/September2025/#sec-Response-Format)
27/// to a GraphQL request that did not cause any [request error][crate::request::RequestError]
28/// and started [execution](https://spec.graphql.org/September2025/#sec-Execution)
29/// of selection sets and fields.
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31#[serde(deny_unknown_fields)]
32pub struct ExecutionResponse {
33 // A note in <https://spec.graphql.org/September2025/#sec-Execution-Result>
34 // suggests serializing this first
35 #[serde(skip_serializing_if = "Vec::is_empty")]
36 #[serde(default)]
37 pub errors: Vec<GraphQLError>,
38
39 pub data: Option<JsonMap>,
40}
41
42/// A serializable [error](https://spec.graphql.org/September2025/#sec-Errors.Error-Result-Format),
43/// as found in a GraphQL response.
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(deny_unknown_fields)]
46pub struct GraphQLError {
47 /// The error message.
48 pub message: String,
49
50 /// Locations in relevant to the error, if any.
51 #[serde(skip_serializing_if = "Vec::is_empty")]
52 #[serde(default)]
53 pub locations: Vec<LineColumn>,
54
55 /// If non-empty, the error is an [execution error]
56 /// for the particular field found at this path in [`ExecutionResponse::data`].
57 ///
58 /// [execution error]: https://spec.graphql.org/September2025/#sec-Errors.Execution-Errors
59 #[serde(skip_serializing_if = "Vec::is_empty")]
60 #[serde(default)]
61 pub path: Vec<ResponseDataPathSegment>,
62
63 /// Reserved for any additional information
64 #[serde(skip_serializing_if = "JsonMap::is_empty")]
65 #[serde(default)]
66 pub extensions: JsonMap,
67}
68
69/// A `Vec<ResponseDataPathSegment>` like in [`GraphQLError::path`]
70/// represents a [path](https://spec.graphql.org/September2025/#sec-Errors.Error-Result-Format)
71/// into [`ExecutionResponse::data`],
72/// starting at the root and indexing into increasingly nested JSON objects or arrays.
73///
74/// # Example
75///
76/// In a GraphQL response like this:
77///
78/// ```json
79/// {
80/// "data": {
81/// "players": [
82/// {"name": "Alice"},
83/// {"name": "Bob"}
84/// ]
85/// },
86/// "errors": [
87/// {
88/// "message": "Something went wrong",
89/// "path": ["players", 1, "name"]
90/// }
91/// ]
92/// }
93/// ```
94///
95/// The error path would have a Rust representation like
96/// `vec![Field("players"), ListIndex(1), Field("name")]`
97/// and designate the value `"name": "Bob"`.
98#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
99#[serde(untagged)]
100pub enum ResponseDataPathSegment {
101 /// The relevant key in an object value
102 Field(crate::Name),
103
104 /// The index of the relevant item in a list value
105 ListIndex(usize),
106}
107
108impl GraphQLError {
109 pub fn new(
110 message: impl Into<String>,
111 location: Option<SourceSpan>,
112 sources: &SourceMap,
113 ) -> Self {
114 Self {
115 message: message.into(),
116 locations: location
117 .into_iter()
118 .filter_map(|location| location.line_column(sources))
119 .collect(),
120 path: Default::default(),
121 extensions: Default::default(),
122 }
123 }
124}