Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

Use-cases and API wish-list #519

Closed
helfer opened this issue Mar 9, 2017 · 7 comments
Closed

Use-cases and API wish-list #519

helfer opened this issue Mar 9, 2017 · 7 comments

Comments

@helfer
Copy link
Contributor

helfer commented Mar 9, 2017

This is a discussion issue for documenting actual use-cases and suggestions for an improved API that people have.

Started because of this issue: #461

@wmertens @calebmer @stubailo @jbaxleyiii let's continue the discussion about API improvements here!

@jbaxleyiii
Copy link
Contributor

I've been thinking a lot about multiple clients and prefetching queries. These are our two largest needs for what's next. I'm hoping to have API designs soon for them. That and I REALLY want good flow support + the codegen support for flow

@stubailo
Copy link
Contributor

I think an easy way to use subscriptions from React would be high on my list.

@wmertens
Copy link
Contributor

wmertens commented Mar 10, 2017

Current use cases (excuse the slight frustration leaking through):

  • Straight queries based on component props
  • Re-run query based on component state. Currently forced to use a parent wrapper to provide that state as props.
  • Multiple mutation definitions on the same component. Forced to provide ugly second argument with the name (the same name that's already in the mutation).
  • Fetching more results when paging. Forced to write ugly functions with confusing arguments to update the cache. Would be fun if by default the paging cache update is "merge the results, and append any arrays you encounter". Maybe there should be an update: "merge"|"replace"|fn(prev) option for each query in the query (fn gets only the previous graphql result of the previous call, not the whole data thing), with "merge" the default.
  • Running many same queries with different props for looking at a table of results. Each table row has a component that has @graphql attached.

Not using fragments or unions. Ids are globally unique.

Comments from other issues:

  • Mutations should be named by their mutation name; mutate should be the fallback name. That way it's easy to attach several at once.
  • options is a very opaque object, and options.variables is so common that it should be separate. Also it's hard to remember which one is the function (options or variables and whether ownProps is just an argument or an attribute in an argument object.
  • React-apollo could help with common problems, like only performing a query if a value $foo is given. This can be done by automatically creating e.g. $fooNonNull if it is used in the query. That would mean not having to do fooNonNull: !!foo on every such query, just write query(foo: $foo) @include(if: $fooNonNull) and it would just work.
  • In the mutation updates, I seem to recall that some of the arguments get the data: ... wrapped around, but the previous queries just have the actual query data. I may be misremembering.
  • it was super confusing to read the example where a mutation was pre-bound by the props function to have the updateQueries set up correctly so the component only had to call this.props.mutation(inputs). Instead, I would expect that sort of setup to be part of the @graphql configuration of the mutation.
  • From a componentization point of view I think it might be good to have the queries and mutations defined in some central place, together with their props and options and whatnot. In my limited experience, using the same query means using the same other settings. So @graphql might take an object as its first argument that combines query and all the other options. This can be optional of course, to provide both use cases

In general, the API feels haphazard, and is not very orthogonal. It has some features that would be better done in separate libraries (skip, props), and defining queries turns ugly very quickly. @graphql(someQuery) is nice, but @graphql(someQuery, {options: (whichargs?) => ({variables: {...}})}) is decidedly not.

I really think that the concept is wonderful, and the execution can be improved to make it wonderful too. (The concept being "I need this slice of server state. I don't care how you get it.")

Some solutions:

  • How about @graphql(options) which adds a prop [options.name || options.query.name] to the component instead of data? That would already take care of a couple of pain points for me. The old @graphql(query, options) would map to @graphql({...options, query, name: options.name || 'data'}).
  • I don't care for the current options() attribute. You already change the apollo-client API, so why use this as the prime configuration? It would be better to provide an optimized-for-react API and then map that to apollo-client calls.

@choonkending
Copy link

Use case(s):

Consistent rendering and error handling between client and server

  • Original issue: Read more in the original issue getDataFromTree throws errors when any query fails #615

  • We check data.error in store both on client and server to:

    • Determine whether we should send back a 200, >= 400 error
    • Determine whether to render the screen with partial data or show an error screen
  • Our GraphQL server speaks to multiple APIs and returns a GraphQL response which is handled by react-apollo. Even if there is an error, if it is not a critical failure, we wish to render a page with partial data.

Current behaviour

Server-side Client-side
getDataFromTree throws an error if there is any error. We can access errors through the data.error property here
Handle errors using try catch Handle errors through accessing data.error. No try catch required

Suggestion

  • Store errors in cache on server and client
  • Do not throw an error when there is an issue. Allow consumers to access the errors through the store and handle it themselves.

Please let me know if you require more information or wish to change the format of the use case.

@HeatherZh
Copy link

I am working on a timeline based component that fetches data from GraphQL backend. When user clicks on previous or next button, I need to call fetchMore by providing a new time range. For any given time range, there is also possible pagination involved. To keep track of whether data is fully loaded or still needs fetchMore for pagination, I have a data structure, similar to the following

dataStatus = {{'04_2017': {loaded: false, pageInfo: ...},
'03_2017': {loaded: true, pageInfo: ...}
... }

and keep it updated when fetchMore is initially called and in a callback function in updateQuery.
As in

  loadMoreEvents: (newDateRange, newAfter, callback) => {
    return fetchMore({
      variables: {
        dateRange: newDateRange,
        after: newAfter
      },
      updateQuery: (previousResult, { fetchMoreResult, queryVariables }) => {
        // Note: bug or not queryVariables always return original variables, not variables used in fetchMore.
        const newNodes = fetchMoreResult.data.calendarReleases.nodes
        const pageInfo = fetchMoreResult.data.calendarReleases.pageInfo
        const hasMore = (newNodes.length === queryVariables.first)
        callback(newDateRange, hasMore, pageInfo)
        return {
          calendarReleases: {
            nodes: [...previousResult.calendarReleases.nodes, ...newNodes]
          }
        }
      }
    })
  }

Perhaps there are better ways to solve this type of timeline related queries. Would it be feasible to have Apollo keeps track of which data set is retrieved through which variables passed to fetchMore?

@stale
Copy link

stale bot commented Aug 22, 2017

This issue has been automatically labled because it has not had recent activity. If you have not received a response from anyone, please mention the repository maintainer (most likely @jbaxleyiii). It will be closed if no further activity occurs. Thank you for your contributions to React Apollo!

@stale stale bot closed this as completed Sep 5, 2017
@stale
Copy link

stale bot commented Sep 5, 2017

This issue has been automatically closed because it has not had recent activity after being marked as no recent activyt. If you belive this issue is still a problem or should be reopened, please reopen it! Thank you for your contributions to React Apollo!

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

No branches or pull requests

6 participants