n1ru4l / envelop

Envelop is a lightweight library allowing developers to easily develop, share, collaborate and extend their GraphQL execution layer. Envelop is the missing GraphQL plugin system.
https://envelop.dev
MIT License
788 stars 128 forks source link

Argument definition level cache control #2299

Open cdubz opened 1 month ago

cdubz commented 1 month ago

Is your feature request related to a problem? Please describe.

I have a couple of fields with optional input parameters that impact whether we want to cache the result. Basically, they use a user context if the input is provided, and we don't want to cache those results. Example:

type Show {
  id
  title
  featuredVideo(userId: UUID): Video
}

type Video {
  id
  title
}

type Query {
  show(showId: UUID!): Show
}

For this query, we want to cache the result:

show(showId: "20bfcb4c-f8a5-4b86-8c9f-b734721222b9") {
  id
  title
  featuredVideo {
    id
    title
  }
}

But for this query we do not want to cache the result:

show(showId: "20bfcb4c-f8a5-4b86-8c9f-b734721222b9") {
  id
  title
  featuredVideo(userId: "9f5d4cd7-22c1-4046-b1b6-7f72c9796db9") {
    id
    title
  }
}

For now we are just doing this to prevent caching if the field is requested at all (as we don't use sessions):

type Show {
  id
  title
  featuredVideo(userId: UUID): Video @cacheControl(scope: PRIVATE)
}

type Video {
  id
  title
}

type Query {
  show(showId: UUID!): Show
}

Describe the solution you'd like

We could solve this use case if we had a way to apply cache control on ARGUMENT_DEFINITION. E.g. --

type Show {
  id
  title
  featuredVideo(
    userId: UUID @cacheControl(scope: PRIVATE)
  ): Video
}

type Video {
  id
  title
}

type Query {
  show(showId: UUID!): Show
}

I'm relatively new to working with GraphQL and I gather that schema coordinates are not really a clearly defined thing. I found this proposed syntax that could apply for this use case: Type.field(argName:).

Describe alternatives you've considered

I have tested using enabled to achieve this by looking at the entire query, but the signature is not async (enabled?: (request: Request) => boolean;) so I'm not able to get the query from the request body (though this doesn't seem like a great solution, anyway).