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

Introduce support for the @oneOf GraphQL Directive #3050

Open
jasonbahl opened this issue Feb 20, 2024 · 2 comments
Open

Introduce support for the @oneOf GraphQL Directive #3050

jasonbahl opened this issue Feb 20, 2024 · 2 comments

Comments

@jasonbahl
Copy link
Collaborator

What problem does this address?

The @oneOf directive allows for polymorphism for input types, similar to how Interfaces and Unions work for Queries.

This is a feature that I've been wanting to support for years.

The GraphQL Spec has an RFC here: graphql/graphql-spec#825

GraphQL.js recently merged the implementation: graphql/graphql-js#3513

And GraphQL-PHP is accepting PRs that introduce the feature: webonyx/graphql-php#619 (comment)

Having formal support for oneOf input types will unlock a LOT of currently "blocked" features and allow us to improve a lot of areas of the Schema over time.

What is your proposed solution?

I propose that we contribute to the effort of adding @OneOf support to GraphQL-PHP.

Then, update WPGraphQL to use the version of graphql-php that supports the oneOf directive. (see notes in additional context below)

Then, start implementing features using the oneOf directive.

Some loose ideas on where we could benefit from oneOf inputs:

Refactor existing features

I think long-term the Schema could benefit from some refactoring to take advantage of the oneOf Input Types to clean up some tech debt, DRY up some code and make the Schema easier to understand and use for API consumers.

A lot of these could happen in backward compatible ways where we introduce new oneOf input types and fields, and deprecate existing fields (while still allowing them to function properly)

Some ideas:

Single Node fields

currently we can query a node by id like so:

node( id: $id ) { ... }

and a node by uri like so:

nodeByUri( uri: $uri ) { ... }

It would be nice to be able to query them from the same field like:

node( id: $id )
node( uri: $uri )

The oneOf directive would allow this as we could have:

input NodeInput @oneOf {
  id: ID!
  uri: String! (or URI! once Custom Scalars are implemented 😉) 
}

And this could be expanded to the other single node entry points like:

post( id: $id idType: DATABASE_ID )
post( id: $id idType: SLUG )
post( id: $id idType: URI )

This could be simplified to:

input PostBy @oneOf {
  databaseId: Int!
  id: ID!
  uri: URI!
  slug: String!
}

post( by: PostBy )

Which would then let us query like so:

post( by: { slug: $slug } ) { ... }
post( by: { id: $id } ) { ... }
post ( by: { uri: $uri } ) { ... }
post( by: { databaseId: $databaseId } ) { ... }

Mutations

Instead of a different mutations for each post type and term (taxonomy) type, we could simplify mutations like so:

(fyi, this is just pseudo ideas, not concrete implementation specifications)

input CreateContentNode @oneOf {
  post: CreatePostInput!
  page: CreatePageInput!
}

extend type RootMutation {
  createContentNode( input: CreateContentNodeInput! ): CreateContentNodePayload!
}

This would simplify the Schema quite a bit and likely DRY up some underlying execution logic as well.

We could do things like:

mutation CreateNodes {
  createPost: createContentNode( input: { post: { title: "Post Title" somePostField: "Some post field value" } } ) { node { ... } }
  createPage: createContentNode( input: { page: { title: Page Title" somePageField: "Some page field vale" } } ) { node { ... } }
}

Support for mutations on polymorphic lists (Blocks / ACF Flex Fields)

Right now, mutations on polymorphic list types such as Gutenberg Blocks and ACF Flex Fields are essentially impossible or at least VERY clunky without oneOf support.

With oneOf support, we could handle things like updating a list of blocks like so:

input UpdateEditorBlockInput @oneOf {
  coreParagraph: UpdateCoreParagraphInput!
  coreImage: UpdateCoreImageInput!
  coreHeading: UpdateCoreHeadingInput!
  ...  
}

extend RootMutation {
  updateEditorBlocks( input: [UpdateEditorBlockInput] ): [EditorBlocks]
}

Then this could be executed like so (pseudo):

mutation UpdateEditorBlocks {
  updateEditorBlocks( input: [
     {
        coreParagraph: { align: "right" }
     },
     {
       coreImage: { sourceUrl: "example.com/wp-content/uploads/img.png"  }
     }
  ] ) {
    editorBlocks {
      __typename
    }
  }
}

Improved Connection Filtering / Sorting

The oneOf directive will likely have impact on the initiative to improve connection filtering and sorting: #1385

What alternatives have you considered?

  • Not making use of oneOf directive and using workarounds like the combination of fields ( i.e. post(id: $id idType: $idType)).

  • waiting longer

Additional Context

PHP Version Support

If we update to latest GraphQL-PHP in order to take advantage of newer features like this, we would likely need to formally drop support of older versions of PHP as GraphQL-PHP supports php formally supports PHP 7.4+ and WPGraphQL currently supports PHP 7.1+.

Schema Breaking Changes

Refactoring parts of the Schema to make use of the oneOf directive MIGHT ultimately lead to breaking changes. I think a lot of things could be done in a backward compatible way though.

For example, instead of post( id: $id idType: $idType ) the oneOf directive could be used to allow the following:

postById: post( id: $id ) { ... }
postByUri: post( uri: $uri ) { ... }
postBySlug: post( slug: $slug ) { ... }

Other Breaking Changes

There might be some under the hood breaking changes as part of the upgrade that we would need to track and note for any release that included the upgrade.

GraphiQL IDE Support

If we were to support the oneOf input type, GraphiQL IDE would need to know how to interact with it. . .specifically the "Query Composer" feature that provides UI elements for users to input values. It would need to know how to properly show users a UI that conveys: Chose "one of" these possible inputs.

@jasonbahl jasonbahl pinned this issue Feb 20, 2024
@justlevine
Copy link
Collaborator

If we update to latest GraphQL-PHP in order to take advantage of newer features like this, we would likely need to formally drop support of older versions of PHP as GraphQL-PHP supports php formally supports PHP 7.4+ and WPGraphQL currently supports PHP 7.1+.

Keep in mind that this assumes that @oneOf gets merged into GraphQL-PHP 15.x, as v16 will drop PHP 7.4 support.

Not that there's anything necessarily wrong about dropping support for all EOL PHP versions, but if we don't want to be beholden to an upstream dependency's release cycle, maybe there's a way to register the (not-really-a) directive in our plugin.

@jasonbahl
Copy link
Collaborator Author

Not that there's anything necessarily wrong about dropping support for all EOL PHP versions

@justlevine based on the opt-in telemetry data we track, we have very few active installs on PHP versions older than 7.4. . .but the majority of active installs are on 7.4, so we'd definitely probably not want to go straight to GraphQL v16.

We should maybe consider a formal policy on PHP version support and devising ways to encourage folks that need to update to do so. Possibly some sort of "GraphQL Health Check" of sorts or something along those lines 🤷🏻‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: 🆕 New
Development

No branches or pull requests

2 participants