teamwalnut / graphql-ppx

GraphQL language primitives for ReScript/ReasonML written in ReasonML
https://graphql-ppx.com
MIT License
257 stars 53 forks source link

Native clients #251

Closed gf3 closed 2 years ago

gf3 commented 3 years ago

Are there any examples of using graphql-ppx with a native client? I'm currently executing queries individually with cohttp, but I'm hoping perhaps someone has already built something a little more sophisticated.

jfrolich commented 3 years ago

I am not aware of native clients unfortunately :) Would be nice to have a basic client though. I build something REALLY basic in one of my reason-mobile experiments.

lepoetemaudit commented 3 years ago

OCaml, I'm afraid, but here is a working native client using Piaf (no proper error checking to speak of, I just wanted to see what was possible)

open Piaf
open Lwt.Syntax

module type QUERY = sig
  type t

  type t_variables

  module Raw : sig
    type t
    type t_variables
  end 

  val variablesToJson: Raw.t_variables -> Yojson.Basic.t
  val serializeVariables : t_variables -> Raw.t_variables

  val unsafe_fromJson : Yojson.Basic.t -> Raw.t
  val parse: Raw.t -> t
  val query : string
end

let make_request 
    (type vars ret) 
    (module Q: QUERY with type t_variables = vars and type t = ret) 
    (vars: vars) : ret Lwt.t =

  let vars = Q.serializeVariables vars |> Q.variablesToJson in
  let body = 
    `Assoc [ "query", `String Q.query; "variables", vars ]
    |> Yojson.Basic.to_string
    |> Body.of_string
  in
  let* response = 
    Client.Oneshot.request 
      ~body 
      ~meth: `POST 
      ~headers: ["Content-Type", "application/json"]
      (Uri.of_string "http://localhost:8380/api")
  in
  match response with
  | Error err -> failwith (Error.to_string err)
  | Ok r -> 
      let+ body = Body.to_string r.body in
      print_endline (Result.get_ok body);
      let json = Yojson.Basic.from_string (Result.get_ok body) in
      match json with
      | `Assoc ["data", data] ->
          Q.unsafe_fromJson data |> Q.parse
      | _ -> failwith "Bad graphql response"

Presuming you defined a query, e.g.:

module Ping = [%graphql {|
  query Ping($message: String!) { message: $message }
|}]

Usage just looks something like:

let result = Query.make_request (module Ping) { message = "Hello world" } in
...

A starting point! Not sure if I fully understood the generated modules but it seemed to 'just work' as I wrote it.

jfrolich commented 3 years ago

That looks amazing @lepoetemaudit! This is definitely correct. My approach was very similar when experimenting with it.