Skip to content
This repository has been archived by the owner on Jul 6, 2020. It is now read-only.

Pass custom variables to update and optimistic #146

Closed
ed-zm opened this issue Dec 23, 2019 · 2 comments · Fixed by urql-graphql/urql#629
Closed

Pass custom variables to update and optimistic #146

ed-zm opened this issue Dec 23, 2019 · 2 comments · Fixed by urql-graphql/urql#629

Comments

@ed-zm
Copy link

ed-zm commented Dec 23, 2019

I am dealing with querying from store a query that has different variables(and can vary) than the mutation performed. I am migrating fro apollo where we can use optimistic and update as second parameter when executing a mutation so we can pass different variables that are available in the same context. That doesn't seem to be an option in urql, so in that way we have to recreate all the updates and optimistic when setting up the client, which only receives 3 parameters, the current variables of the mutation, the cache and info object. let's get some example:

this is the query:

import gql from "graphql-tag";

export const CalendarQuery = gql`
  query calendar($sessionId: String, $from: Long!, $to: Long!, $timezone: String!, $focusedGroup: String) {
    calendar(sessionId: $sessionId, from: $from, to: $to, timezone: $timezone, focusedGroup: $focusedGroup) {
      practices {
        id
        durationMinutes
        label
        location
        teamName
        rgb
        start
        attendance {
          athleteName
          athleteFirstName
          athleteLastName
          _id: athleteGuid
          attendance
        }
      }
      events {
        id
        durationMinutes
        label
        location
        teamName
        rgb
        start
        attendance {
          _id: athleteGuid
          attendance
        }
      }
      games {
        id
        durationMinutes
        label
        location
        teamName
        rgb
        start
        attendance {
          _id: athleteGuid
          attendance
        }
      }
      workouts {
        id
        durationMinutes
        label
        location
        teamName
        rgb
        start
      }
    }
  }
`;

notice that to and from can vary as user wants to fetch sessions in a certain timespan.

now, I want to update or delete a session:

const UPDATE_SESSION = gql`
  mutation($sessionId: ID!, $timezone: String!, $language: String, $input: SessionInput!) {
    updateSession(sessionId: $sessionId, timezone: $timezone, language: $language, input: $input) {
      ... on Practice {
        id
        start
        durationMinutes
      }
      ... on Event {
        id
        start
        durationMinutes
      }
      ... on Game {
        id
        start
        durationMinutes
      }
      ... on Workout {
        id
        start
        durationMinutes
      }
    }
  }
`;

I execute my mutation:

await updateSession({
              sessionId: user.sessionId,
              timezone: "GMT",
              language: "en",
              input: { id: session.id, start: date.valueOf() }
            })

now I am going to set up my optimistic response callback:

updateSession: ({ input, ...variables }, cache, info) => {
      cache.updateQuery({ query: CalendarQuery, variables }, (data: any) => {

First Issue: I am not able to query the calendar from store as I dont have the correct variables.

Second Issue: I couldnt find any simple way to pass variables to that callback. I tried adding some extra variables in the execute mutation:

await updateSession({
              sessionId: user.sessionId,
              timezone: "GMT",
              language: "en",
              calendar,
              __typename: session.__typename,
              input: { id: session.id, start: date.valueOf() }
            })
updateSession: ({ input, calendar, __typename, ...variables }, cache, info) => {
      return { ...input, __typename };
    }

(see calendar as a custom variable which is not used in the mutation itself)

Seems also urql cleans the variables object and only pass the variables that are actually used in the mutation/query.

So, I am just looking for a solution, workaround or a method where I can pass variables to the update/optimistic callbacks so it can work for queries that has dinamic variables(as above) and not fixed ones that can be hardcoded.

Thanks in advance.

@kitten
Copy link
Member

kitten commented Dec 28, 2019

Hiya 👋 Excellent question! It did occur to me that we’ll need this eventually, and we just weren’t sure of how to make this work cleanly.

Having variables be defined on the query means that they’re type-cast and safe to pass. GraphQL code generators would also generate types for them, which means that they can be seamlessly defined and passed from the JS-side.

The question is, how do we pass extra data? We could either clean the variables after they’ve gone through Graphcache. That would mean however that those variables wouldn’t be documented, sanitised, or typed, in any case. The same goes for passing them to a directive.

We could also make it easier to pass extra context data into urql, but that would complicate our API and would still not be type-safe.

So basically we’re just trying to figure out what an elegant solution could look like that would:

  • Prevent extra data from making it to the API (Like a directive that accepts variables)
  • Allows extra data to be embedded in the variables and be typed
  • Allows optimistic updates and update resolvers to receive the data alongside all normal variables without having to know which variables are “client-side only” and which are important to the API.

Hope that makes sense 🙌 I’ll try to keep this issue updated with some more ideas

@kitten
Copy link
Member

kitten commented Dec 28, 2019

One idea that may work and comes to mind is defining local variables with a directive on the mutation, e.g.

mutation ($id: ID!, $extra: String!) @cache(filter: [“extra”]) {
  addItem(id: $id) {
    id
  }
}

The directive would be called @cache to make it more generic sounding, so it can be reused for future features.

The filter argument tells us which variables aren’t to be sent to the server.

The disadvantage here is that it’s easy but in Graphcache we’d have to alter the query to remove the filtered variables from the definition (that’s kind of annoying) and also filter them from the variables object itself.

The resolver would then be able to access the data like so however:

updates: {
  addItem({ id }, cache, { variables: { extra } }) {
    // ...
  }
}

Which is rather nice!

Optimally we’d somehow attach the extra data to the arguments of the field though, so it’s easier to type them, but that wouldn’t play nicely with the field’s definition 🤔

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

Successfully merging a pull request may close this issue.

2 participants