graphql / graphql-spec

GraphQL is a query language and execution engine tied to any backend service.
https://spec.graphql.org
14.31k stars 1.12k forks source link

Proposal: Client side schema, resolvers and mutations #274

Closed timbrandin closed 7 years ago

timbrandin commented 7 years ago

As mentioned in this thread: https://github.com/apollographql/apollo/issues/75#issuecomment-283102483

It would be terrific to be able to combine states in a client side graphql implementation. For example we often have situations where we depend on server side data and client side route information and other client side resources. It is really hard right now to combine them in a well documented and clear way, we see different solutions to this problem every day.

So our suggestion is to be able to have a client side version of graphql with the root query being named "client" instead of "query" and being able to pass down variables created by previous queries to other "query" or "client" implementations in a serial synchronous fashion.

Here's an example of what this could look in a query:

query getCards {
  $cards: cards {
    _id
    name
  }
}
client getWizardStep($route: Route, $location: Location) {
  stepState: calcWizardStep(route: $route, location: $location, $cards) {
    currentCard
    cardCount
  }
}
stubailo commented 7 years ago

I don't think this requires any new graphql stuff right? Could just use an @client directive. We almost implemented this in Apollo but it ended up not being a priority. I'd be happy to work together to make this happen!

timbrandin commented 7 years ago

Ah, so @stubailo you mean like this?

query getCards {
  cards @export(as: "cards") {
    _id
    name
  }
}
query @client getWizardStep($route: Route, $location: Location) {
  stepState: calcWizardStep(route: $route, location: $location, cards: $cards) {
    currentCard
    cardCount
  }
}

And until there's support for the export directive this could work?

import { compose, graphql } from 'react-apollo';
import gql from 'graphql-tag';
import WizardStepComponent from '.';

export default compose(
  grapqhql(gql`
    query getCards {
      cards
        _id
        name
      }
    }
  `),
  grapqhql(gql`
    query @client getWizardStep($route: Route, $location: Location, $cards: Cards) {
      stepState: calcWizardStep(route: $route, location: $location, cards: $cards) {
        currentCard
        cardCount
      }
    }
  `, { options: ({ data: { cards = [] } }) => ({ variables: { cards } }) }),
)(WizardStepComponent);
stubailo commented 7 years ago

Yep! I was actually thinking @client directives on fields in the same query would work as well!

@jnwng was interested in this as well! Here's the original PR that we didn't end up fully merging (that's where the current custom resolvers come from) I'd love to bring that back and make it a real thing! https://github.com/apollographql/apollo-client/pull/809

leebyron commented 7 years ago

I agree that directives should provide what you need, and no changes are necessary to graphql to do what you're looking to do. In fact you may be able to do this without any directives at all - we use client specific fields using the experimental extend syntax (which graphql.js supports) - we can know what portions of a request are client-only by comparing to the original server-only schema, filtering out anything the server wouldn't know about before sending requests. This has worked out well for us across a few platforms!

timbrandin commented 7 years ago

Interesting @leebyron, and I'm curious how this could be setup to work with Apollo Client @stubailo, is that maybe a new FR?