Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recursive args #134

Closed
dwwoelfel opened this issue Jan 23, 2019 · 3 comments · Fixed by #199
Closed

Recursive args #134

dwwoelfel opened this issue Jan 23, 2019 · 3 comments · Fixed by #199

Comments

@dwwoelfel
Copy link
Contributor

dwwoelfel commented Jan 23, 2019

Support recursive args. They come in very handy when you're trying to allow people to write boolean logic, like "createdAt > '2018-12-31' and priority = 1".

Both Hasura and Postgraphile use recursive args to allow the user to construct custom postgres queries.

Example of code (in Reason) that currently throws a Lazy.Undefined error at runtime:

let rec recursiveArg =
  lazy
    Schema.Arg.(
      obj(
        "recursiveArg",
        ~fields=[
          arg("name", ~typ=string),
          arg("age", ~typ=int),
          arg("and", ~typ=Lazy.force(recursiveArg)),
        ],
        ~coerce=(name, age, and_) =>
        (
          switch (name) {
          | None => ""
          | Some(name) => name ++ " "
          }
        )
        ++ (
          switch (age) {
          | None => ""
          | Some(age) => string_of_int(age) ++ " "
          }
        )
        ++ (
          switch (and_) {
          | None => ""
          | Some(and_) => "and " ++ and_
          }
        )
      )
    );
@andreas
Copy link
Owner

andreas commented Jan 26, 2019

Interesting use case! Recursive input types are indeed not supported right now. I've implemented the most straight forward approach here, which follows the way fields work for regular objects:

https://github.com/andreas/ocaml-graphql-server/tree/graphql/recursive-input-arguments

Does that fix it for you?


I've thought more broadly about recursive types (whether input or regular) -- here's a bit of a brain dump. I find there are 3 scenarios for recursive (input) types:

  1. Non-recursive
  2. Self-recursive
  3. Mutually recursive

The current approach for recursive (input) types favors scenario (2), while making (1) a little more inconvenient and (3) quite cumbersome (as I'm sure you already know, given you have a very complex and mutually recursive schema!).

I've considered differentiating between constructing non-recursive types and recursive types (whether self-recursive or mutually recursive). This should make scenario (1) as simple as possible, while making (2) and (3) better too -- the downside being that there's now two ways to construct an object.

Example:

open Graphql_lwt

(* non-recursive *)
let foo = Schema.(obj "Foo"
  ~fields:[
    (* Ahh -- a simple list! No functions in sight *)
  ]
)

(* recursive *)
let commit, tree = Schema.(fix (fun recursive ->
  recursive.obj "Commit"
    ~fields:(fun (commit, tree) -> [
      field "tree"
        ~typ:(non_null tree)
        ~args:[]
        ~resolve:(fun _ c -> (* ... *))
    ],
  recursive.obj "Tree"
    ~fields:(fun (commit, tree) -> [
      field "commit"
        ~typ:(non_null commit)
        ~args:[]
        ~resolve:(fun _ t -> (* ... *))
    ]
))

The new function Schema.fix gives you a "builder" value (here called recursive, not sure what a good name is) for constructing a fixpoint (scenario 2 and 3).

Further, Schema.obj is changed to just requiring a simple list of fields (scenario 1):

    val obj : ?doc:string ->
              string ->
-             fields:(('a option arg_typ) -> ('a, 'b) arg_list) ->
+             fields:('a, 'b) arg_list ->
              coerce:'b ->
              'a option arg_typ

I imagine the same approach could be used for input types.

@dwwoelfel
Copy link
Contributor Author

We're using your recursive-input-arguments changes in production on OneGraph now, and it has been working really well for us.

@anmonteiro
Copy link
Contributor

I really like the fix approach here. I think this could be a really nice quality of life improvement, and would make constructing schemas much simpler for the 80% cases where users don't have recursive (self or mutually) objects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants