Reason-apollo: Unable to parse Array with options


#1

I have the following Graphql Container:

let ste = ReasonReact.string;
type listMatches = {
  /** Foo */
  date: option(string),
  homeTeam: option({. "name": option(string)}),
  awayTeam: option({. "name": option(string)}),
  homeScore: option(int),
  awayScore: option(int),
  goals:
    option(
      Js.Array.t(
        option({
          .
          "scorer": option({. "name": option(string)}),
          "time": option(string),
        }),
      ),
    ),
};

module GetMatches = [%graphql
  {|
    query getMatches($id: Int!){
        matches(id: $id){
            date
            homeTeam {name}
            awayTeam {name}
            homeScore
            awayScore
            goals {
              scorer {
                name
              }
              time
            }
          }
    }
  |}
];

module GetMatchesQuery = ReasonApollo.CreateQuery(GetMatches);

let component = ReasonReact.statelessComponent("Matches");

let make = _children => {
  ...component,
  render: _self => {
    let matchParam = GetMatches.make(~id=300331511, ());
    <GetMatchesQuery variables=matchParam##variables>
      ...{
           ({result}) =>
             <div>
               {
                 switch (result) {
                 | Error(e) =>
                   Js.log(e);
                   "Something Went Wrong" |> ste;
                 | Loading => "Loading" |> ste
                 | Data(response) =>
                   switch (response##matches) {
                   | None => "No Person Data" |> ste
                   | Some(person) => <div> {person##date |> ste} </div>
                   }
                 }
               }
             </div>
         }
    </GetMatchesQuery>;
  },
};

But I keep getting this error:

Error: This expression has type
         Js.Array.t(option({. "awayScore": option(int),
                             "awayTeam": option({. "name": option(string)}),
                             "date": option(string),
                             "goals": option(Js.Array.t(option({. "scorer": 
                                                                 option(
                                                                 {. "name": 
                                                                   option(
                                                                   string)}),
                                                                 "time": 
                                                                 option(
                                                                 string)}))),
                             "homeScore": option(int),
                             "homeTeam": option({. "name": option(string)})}))
           =
           array(option({. "awayScore": option(int),
                          "awayTeam": option({. "name": option(string)}),
                          "date": option(string),
                          "goals": option(Js.Array.t(option({. "scorer": 
                                                              option(
                                                              {. "name": 
                                                                option(
                                                                string)}),
                                                              "time": 
                                                              option(
                                                              string)}))),
                          "homeScore": option(int),
                          "homeTeam": option({. "name": option(string)})}))
       but an expression was expected of type Js.t('a)
[merlin]

I’ve tried passing the response to an array but the error is the same…how am I supposed to parse this and display it?


#2

Looks like you’re treating matches like it’s a person object, when in fact it’s a list of optional person objects.

switch (response##matches) {
 | None => "No Person Data" |> ste
 | Some(matches) =>
    let realMatches = matches->Belt.List.keepMap(match => match);
    realMatches->Belt.List.map(person => <div> {person##date |> ste} </div>)
}

#3

How is your graphql schema setup ? I’m not saying it’s optimal, but you could always try to declare the data in your schema as non-null. Again, perhaps not what you want or optimal, but it should remove all the option types.

for example: type listMatches = { date: string! <-- this should return a string type that is non-null }


#4

My Js.log(response##matches) has the following:


#5

I’m using this graphql https://worldcup-2018.now.sh I did a “yarn send-introspection-query https://worldcup-2018.now.sh


#6

I changed it to use Belt.Array.keepMap because instead of List because it kept throwing an error:

let realMatches =
                       matches->Belt.Array.keepMap(match => match);
                     realMatches
                     ->Belt.Array.map(matchInfo =>
                         <div> {matchInfo##date |> ste} </div>
                       );

Now the error is:

Error: This expression has type string => ReasonReact.reactElement
       but an expression was expected of type option(string) => 'a
       Type string is not compatible with type option(string)

#7

Open the graphql at https://worldcup-2018.now.sh and take a look at the schema definition for the matches query.

type Match {
id: Int! <----- notice the "!"
description: string <— this can be an option type
}


#8

@hackersapien you figure this out yet?


#9

@hackersapien You might want to try and parse the response as a record using the @bsRecord directive. https://github.com/apollographql/reason-apollo#use-bsrecord-on-response-object


#10

To use @bsRecord won’t I need to model the whole response as record first?


#11

@hackersapien You don’s necessarily need to model the whole response but you can if you want.


#12

So I’ve tried doing this:

module GetMatches = [%graphql
  {|
    query getMatches($id: Int!){
        matches(id: $id) @bsRecord {
            date
            homeTeam {name}
            awayTeam {name}
            homeScore
            awayScore
            goals {
              scorer {
                name
              }
              time
            }
          }
    }
  |}
];

But I get an error:

The record field date can’t be found.

  If it's defined in another module or file, bring it into scope by:
  - Annotating it with said module name: let baby = {MyModule.age: 3}
  - Or specifying its type: let baby: MyModule.person = {age: 3}

Not sure what I’m doing wrong and the error message isn’t helping.Are there any solid examples of @bsRecord being used?


#13

@hackersapien When I said you don’t have to model the whole response, what I was saying was that you can model part of the response. For example with homeTeam:

type homeTeamType = {
  name: option(string)
}

type homeTeam = option(homeTeamType)

query getMatches($id: Int!){
        matches(id: $id){
            date
            homeTeam @bsRecord
            awayTeam {name}
            homeScore
            awayScore
            goals {
              scorer {
                name
              }
              time
            }
          }
    }

But if you want to model the entire type you can do that too. But you need a model type of some sort in order for @bsRecord to know how to transform it.

Also check out the link here: Reason-apollo: Unable to parse Array with options


#14

mentally exhausting trying to get this to work…


#15

@hackersapien It might be better to pair program to help you with this. You can reach out to me if you would like to do that. adam.recvlohe@gmail.com