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

[core] New plugin data API #2404

Open
EmrysMyrddin opened this issue Feb 5, 2025 · 0 comments
Open

[core] New plugin data API #2404

EmrysMyrddin opened this issue Feb 5, 2025 · 0 comments

Comments

@EmrysMyrddin
Copy link
Collaborator

EmrysMyrddin commented Feb 5, 2025

Description

This PR is a proposition to solve a long standing DX issue: sharing data between ALL hooks of a plugin.

Context

Today, the only relyable way to share data between the hooks of a plugin is to use WeakMap with the GraphQL context as key:

function usePlugin(): Plugin {
  new dataByContext = new WeakMap<any, { foo: string }>();
  return {
    onParse({ context }) {
      dataByContext.set(context, { foo: 'bar' );
    },
    onValidate({ context }) {
      const data = dataByContext(context);
      if(data) { // We even have to check for its existence, even it should always be defined.
        console.log(data)
      }
    }
}

In Yoga, it's even worse since in most Yoga specific hooks, the graphql context is not built yet, so the only thing shared by all hooks is the HTTP request:

function usePlugin(): YogaPlugin {
  new dataByRequest = new WeakMap<Request, { foo: string }>();
  return {
    onRequestParse({ request }) {
      dataByRequest.set(request, { foo: 'bar' );
    },
    onValidate({ context }) {
      const data = dataByRequest(context.request);
      if(data) { // We even have to check for its existence, even it should always be defined.
        console.log(data)
      }
    }
}

But this can be misleading: If batching is enabled, multiple different operation will share the same data. We currently have multiple plugins that are using this technique and that are open to bugs when batching is enabled.

Proposition

My proposal is to add a dedicated API directly in the core of Envelop, Yoga and Hive to have a streamlined way of sharing data between hooks. While the WeakMap trick works, it's really not very good DX, and is very error prone.

The API would be a data field that will be present in ALL hooks (for Envelop hooks, but also Yoga and Hive hooks).
To emphases the fact that there is potentially multiple level of data sharing (operation wide, http request wide, subgraph execution wide), this data object would contain a field for each scope:

type EnvelopData = {
  forOperation: object
}

type YogaData = EnvelopData & {
  forRequest: object
}

type HiveData = YogaData & {
  forSubgraphExecution: object
}

This data would then be available in all hooks, but depending of the hook, not all scopes will be available.

function myPlugin(): HivePlugin {
	return {
      onRequestParse({ data }) {
        // only data.forRequest is available
      },
      onExecute({ data }) {
        // both data.forRequest and data.forOperation are available
      },
      onSubgraphExecute({ data }) {
        // all scopes are available: data.forRequest, data.forOperation and data.forSubgraphExecution
      },
}

Of course, it will also imply adding generic types everywhere. And this is probably a bit tricky because it will imply adding more generics to existing Plugin type, which I'm not sure if it is non-breaking or not.

As a proof of concept, I've implemented the first step of this proposal: the Envelop scope (#2405)

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

No branches or pull requests

1 participant