andreas / ocaml-graphql-server

GraphQL servers in OCaml
MIT License
624 stars 60 forks source link

Unclear how to use pass data from one resolver to another #26

Closed sgrove closed 7 years ago

sgrove commented 7 years ago

For example, if I'm modeling a Tweet:

  1. On the Twitter-api side, each tweet has id, title, children_ids (simply a list of ids of reply-tweets) fields
  2. On the ocaml-graphql-server side, it has id, title, and replies (a recursive list of tweets) fields.
  3. I'll need to use an io_fields to make the call against the Twitter API (but I'll only want to make one call to retrieve both fields)
  4. After retrieving the concrete data from Twitter, in order to resolve replies, I need to see the result in order to make the subsequent calls and recursively fill out the replies. But I'm not sure how to get the list of children_ids from the result of the previous step.

So my questions:

  1. How can one field access the data of another (either sibling or parent)?
  2. What data are they able to access?
  3. What order are the fields resolved in?
  4. How can we define recursive Schemas (tweets that have children tweets)?

This is just an example (I'm not sure what Twitter's API actually looks like) I'm hoping to get some clarification on. It's been quite a bit of fun to poke at so far - thank you!

sgrove commented 7 years ago

I'll close this as I figured it out, and I'll submit a PR with a bit more detail on how the structure works in the README

andreas commented 7 years ago

Rather than address your question one by one, I hope the following example helps (I haven't try to compile this code and it blatantly disregards the issue you raised in #27):

(* Assumption *)
module TwitterAPI = sig
  type tweet = {
    id : int;
    title : string;
    children_ids : int list;
  }

  val get_tweet : int -> tweet option Lwt.t
end : struct
  (* some implementation *)
end

(* Tweet object definition *)
let rec tweet = Schema.(obj "Tweet"
  ~fields:[
    field "id"
      ~typ:(non_null int)
      ~args:Arg.[]
      ~resolver:(fun ctx tweet -> tweet.id)
    ;
    field "title"
      ~typ:(non_null string)
      ~args:Arg.[]
      ~resolver:(fun ctx tweet -> tweet.title)
    ;
    io_field "replies"
      ~typ:(non_null (list tweet))
      ~args:Arg.[]
      ~resolver:(fun ctx tweet ->
        Lwt_list.map_p TwitterAPI.get_tweet tweet.children_ids
      )
  ]
)

(* Schema definition *)
let schema = Schema.(schema
  ~fields:[
    io_field "tweet"
      ~typ:tweet
      ~args:Arg.[
        arg "tweet_id" ~typ:(non_null int)
      ]
      ~resolver:(fun ctx () tweet_id -> TwitterAPI.get_tweet tweet_id
  ]
)

Does that help? 😄

sgrove commented 7 years ago

Indeed, that's great, thanks!