I'm using Reason-Apollo to parse a pretty nested GraphQL response from my server. I'm having trouble parsing the hairy tree of options returned from my GraphQL server (I'm using django-graphene).
Here is the GraphQL query and the Reason React module using Reason Apollo:
module GroupQuery = [%graphql {|
query GetChatGroup($chatGroupId: ID!){
chatGroup(id: $chatGroupId) {
id
users {
edges {
node {
id
name
isCurrentUser
}
}
}
messages {
edges {
node {
id
text
author {
name
abbreviation
photoUrl
isCurrentUser
}
}
}
}
}
}
|}];
/*eventually will be a reducerComponent*/
let component = ReasonReact.statelessComponent("RechatWindow");
module Query = RechatApollo.Instance.Query;
let parseMessages = chatGroup =>
switch chatGroup {
| Some(chatGroup) =>
switch chatGroup##messages {
| Some(messages) =>
let edges = messages##edges;
switch edges {
| Some(edges) =>
let parsedNodes =
Js.Array.map(
node =>
switch node {
| Some(node) =>
let id = node##id;
let text = node##text;
let author = node##author;
switch (id, text, author) {
| (Some(id), Some(text), Some(author)) =>
let name = author##name;
let abbrev = author##abbreviation;
let isCurrentUser = author##isCurrentUser;
switch (name, abbrev, isCurrentUser) {
| (Some(name), Some(abbrev), Some(isCurrentUser)) =>
id ++ " - " ++ text ++ " - " ++ name ++ " - " ++ abbrev ++ " - "
| _ => "Error retrieving message 3"
};
| _ => "Error retrieving message 2"
};
| _ => "Error retrieving message 1"
},
edges
);
parsedNodes;
| None => [||]
};
| None => [||]
};
| None => [||]
};
let make = (_children) => {
...component,
render: (_) => {
let unexpectedError = <div> (ReasonReact.stringToElement("There was an internal error")) </div>;
let groupQuery = GroupQuery.make(~chatGroupId="Q2hhdEdyb3VwVHlwZTox", ());
<Query query=groupQuery>
...((response, parse) => {
switch response {
| Loading => <div> (ReasonReact.stringToElement("Loading")) </div>
| Failed(error) => <div> (ReasonReact.stringToElement(error)) </div>
| Loaded(result) => {
let chatGroup = parse(result)##chatGroup;
let parsedMessages = parseMessages(chatGroup);
<ul>
(
ReasonReact.arrayToElement(
Array.map(message => <li> (ste(message)) </li>, parsedMessages)
)
)
</ul>;
}
}
})
</Query>
}
};
Here is the return data from the GraphQL query from GraphiQL:
{
"data": {
"chatGroup": {
"id": "Q2hhdEdyb3VwVHlwZTox",
"users": {
"edges": [
{
"node": {
"id": "VXNlclR5cGU6MzQ=",
"name": "User 1",
"isCurrentUser": false
}
},
{
"node": {
"id": "VXNlclR5cGU6MQ==",
"name": "User 2",
"isCurrentUser": true
}
}
]
},
"messages": {
"edges": [
{
"node": {
"id": "Q2hhdE1lc3NhZ2VUeXBlOjE=",
"text": "my first message",
"author": {
"name": "User 1",
"abbreviation": "U1",
"photoUrl": "",
"isCurrentUser": true
}
}
}, ...
I have a syntax error somewhere ...
137 ┆ | Loaded(result) => {
138 ┆ let chatGroup = parse(result)##chatGroup;
139 ┆ let parsedMessages = parseMessages(chatGroup);
140 ┆ <ul>
141 ┆ (
This has type:
option(Js.t({. id : string,
messages : option(Js.t({. edges : array(option(Js.t(
{. node :
option(
Js.t(
{. author :
Js.t(
{. abbreviation :
option(
string),
isCurrentUser :
option(
Js.boolean),
name :
option(
string),
photoUrl :
option(
string) }),
id :
string,
text :
string })) }))) })),
users : option(Js.t({. edges : array(option(Js.t({. node :
option(
Js.t(
{. id :
string,
isCurrentUser :
option(
Js.boolean),
name :
option(
string) })) }))) })) }))
But somewhere wanted:
option(Js.t({.. messages : option(Js.t({.. edges : option(Js.Array.t(
option(
Js.t({.. author :
option(
Js.t(
{.. abbreviation :
option(
string),
isCurrentUser :
option('a),
name :
option(
string) })),
id :
option(
string),
text :
option(
string) })))) })) }))
Types for method edges are incompatible
My immediate question: what is the error here?
On a deeper level, parsing all of these options to render the desired response seems like it would generally produce pretty unclear code. So what is the common paradigm around parsing options in JS when using ReasonML / OCaml? Is there an idiomatic way to get all of the options that will be there most of the time? Should I be creating an object type or a record type and parsing into those, and then rendering from the "known" object or record structures?
Or perhaps my graphql_schema.json
and endpoint needs to have more required options?
Also, I'm using Relay's GraphQL convention of having edges { node { ... node fields ... } }
, and it seems like if there are any edges then there should be at least one node. Is there any way to cut down on the option verbosity when using relay-style GraphQL?
bs-json
withreason-apollo
? – Paxwax