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

[RFC] Composable directives #683

Open
leoloso opened this issue Feb 5, 2020 · 3 comments
Open

[RFC] Composable directives #683

leoloso opened this issue Feb 5, 2020 · 3 comments

Comments

@leoloso
Copy link

@leoloso leoloso commented Feb 5, 2020

I have just submitted an RFC concerning composable fields (#682). Composable directives are similar: they would allow directives to modify the behaviour of, or prepare the context for, other directives.

Currently, several directives can modify a field, executed in a specific order:

query {
  field @directive1(arg1: value1) @directive2
}

However, we can't establish to execute a directive within another directive, because the syntax @ doesn't support it. Then, this RFC must also involve introducing a new way to define directives, such as using <...>:

query {
  field<directive1(arg1: value1)<directive2>>
}

In this case, the field is modified by a combination of directive1 and directive2, where directive1 wraps directive2 like this: it first prepares the context for directive2, then executes it, and finally does some clean up work.

How could this be useful? For instance, it can enable to use existing directives to modify the elements of an array, like in this example:

We have a directive translate, which translates a field of type string (run example in GraphiQL):

query {
  posts:posts(limit:10) {
    title
    translatedTitle:title@translate(from:"en",to:"es")
  }
}

Now, we have a field that resolves to an array of strings, like property tagNames:

query {
  posts:posts(limit:10) {
    tagNames
  }
}

We can't directly use directive translate, since it expects a string as input, not an array of strings. We could have this directive also be able to process an array of strings; however, this would go against type safety, and it would not be a long-term solution, because it would not work for yet other inputs (eg: an array of array of strings, a map, etc).

The solution, then, is to have a directive forEach that can iterate through all the elements of the array, and execute the directive translate on each of them. Because directives are composable, it can handle any input (eg: an array of array of strings, or a map) already on the query, without any custom server-side code.

Demonstration of the solution

This solution has been implemented for this GraphQL server. Because the GraphQL syntax doesn't support this feature, I have used an alternative syntax.

Example of a query with composable directives (view response):

/?
query=
  posts(limit:10).
    tagNames|
    tagNames@translatedTagNames<forEach<translate(from:"en", to:"es")>>
@michaelstaib

This comment has been minimized.

Copy link

@michaelstaib michaelstaib commented Feb 5, 2020

In my opinion you can already do what you want with the current syntax.

To me it appears that you want some kind of a pipelining here.

since in GraphQL the directive order is significant and we have repeatable directives you can actually define pipelines by just adding the directives in the correct order.

query {
  posts(limit:10) @http_get(url: "...") @json(path: "$.foo.bar") @translate(from:"en", to:"es") {
    text
  }
}

Also the syntax that you are using is looking more like a generic approach.

For me the syntax looks to complex and the functionality can easily be achieved otherwise.

@leoloso

This comment has been minimized.

Copy link
Author

@leoloso leoloso commented Feb 5, 2020

@michaelstaib I agree that the new <> syntax is not the best, but it enables to wrap elements within each other. That's why I chose it.... but if anybody suggests something better, it's welcome.

Concerning piping, I think you could achieve the same forEach behaviour I described, but in 3 steps instead of 2:

  1. Spread the array into a series of strings
  2. Translate each of them
  3. Join all the translated strings into an array again

Then it would be something like this:

query {
  posts {
    tagNames @spread @translate(from:"en",to:"es") @join
  }
}

This solution looks a bit hacky to me. Certainly not as simple or elegant as through composable directives.

Also, I don't think that piping can always make it. For instance, if we want to advance a pointer to a specific position in a multi-dimensional array, and only apply the operation on that item... through composable directives, though, it could be very easy to do

@sungam3r

This comment has been minimized.

Copy link
Contributor

@sungam3r sungam3r commented Feb 5, 2020

Completely agree with @michaelstaib

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

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.