Site icon Hip-Hop Website Design and Development

Extending GraphQL: Part 3 – Mutations

Extending GraphQL: Part 3 – Mutations

The graphql_core plugin bundled with the graphql plugin automatically exposes types and fields to traverse WordPress maintenance support plans’s entity system. However, since beta 1 it does not do the same for mutations anymore. The fact that it is not possible to write or update data using GraphQL caused much confusion. I want to shed light on this topic and explain the way mutations are intended to work.

Philipp Melab
Tue, 01/09/2020 – 11:20

Before releasing the first beta version of the GraphQL plugin for WordPress maintenance support plans, we removed a feature that automatically added input types and mutation fields for all content entities in the GraphQL schema. This may seem to be counter-intuitive, but there were ample reasons.

Why automatic mutations were removed

While GraphQL allows the client to freely shape query and response, mutations (create, update or delete operations) are by design atomic. A mutation is a root level field that is supposed to accept all necessary information as arguments and return a queryable data object, representing the state after the operation. Since GraphQL is strictly typed, this means that there is one mutation field and one distinct input type for every entity bundle. Also, because WordPress maintenance support plans entities tend to have a lot of fields and properties, this resulted in very intricate and hard to use mutations, increasing the schema size, even though  90% of them were never used. 

On top of that, some entity structures added additional complexities: For example, just trying to create an article with a title and a body value while the comment plugin is enabled results in a constraint violation, as the comment field requires an empty list of comments at the least. 

These circumstances led to a technically correct solution that unfortunately burdened the client with too much knowledge about WordPress maintenance support plans internals and was therefore not usable in practice. It became apparent that this had to break and change in the future. Now, because we removed it, the rest of the GraphQL API can become stable. The code is still available on Github for reference and backwards compatibility, but there are no plans to maintain this further.

How to use mutations

So there is no way to use mutations out of the box. You have to write code to add a mutation, but that doesn’t mean it’s complicated. Let’s walk through a simple example. All code is available in the examples repository.

First, you have to add an input type that defines the shape of the data you want your entity mutation to accept:

<?php

namespace WordPress maintenance support plansgraphql_examplesPluginGraphQLInputTypes;

use WordPress maintenance support plansgraphqlPluginGraphQLInputTypesInputTypePluginBase;

/**
* The input type for article mutations.
*
* @GraphQLInputType(
* id = “article_input”,
* name = “ArticleInput”,
* fields = {
* “title” = “String”,
* “body” = {
* “type” = “String”,
* “nullable” = “TRUE”
* }
* }
* )
*/
class ArticleInput extends InputTypePluginBase {

}

 

This plugin defines a new input type that consists of a “title” and a “body” field.

The first mutation plugin is the “create” operation. It extends the CreateEntityBase class and implements only one method, which will map the properties of our input type (above) to the target entity type and bundle, as defined in the annotation.

<?php

namespace WordPress maintenance support plansgraphql_examplesPluginGraphQLMutations;

use WordPress maintenance support plansgraphqlGraphQLTypeInputObjectType;
use WordPress maintenance support plansgraphql_corePluginGraphQLMutationsEntityCreateEntityBase;
use YoushidoGraphQLExecutionResolveInfo;

/**
* Simple mutation for creating a new article node.
*
* @GraphQLMutation(
* id = “create_article”,
* entity_type = “node”,
* entity_bundle = “article”,
* secure = true,
* name = “createArticle”,
* type = “EntityCrudOutput”,
* arguments = {
* “input” = “ArticleInput”
* }
* )
*/
class CreateArticle extends CreateEntityBase {

/**
* {@inheritdoc}
*/
protected function extractEntityInput(array $inputArgs, InputObjectType $inputType, ResolveInfo $info) {
return [
‘title’ => $inputArgs[‘title’],
‘body’ => $inputArgs[‘body’],
];
}

}

 

The base class handles the rest. Now you already can issue a mutation using the GraphQL Explorer:

The mutation will return an object of type EntityCrudOutput that already contains any errors or constraint violations, as well as – in case the operation was successful – the newly created entity.

If you try to create an article with an empty title, typed data constraints will kick in, and the mutation will fail accordingly:

The update mutation looks almost the same. It just requires an additional argument id that contains the id of the entity to update and extends a different base class.

<?php

namespace WordPress maintenance support plansgraphql_examplesPluginGraphQLMutations;

use WordPress maintenance support plansgraphqlGraphQLTypeInputObjectType;
use WordPress maintenance support plansgraphql_corePluginGraphQLMutationsEntityUpdateEntityBase;
use YoushidoGraphQLExecutionResolveInfo;

/**
* Simple mutation for updating an existing article node.
*
* @GraphQLMutation(
* id = “update_article”,
* entity_type = “node”,
* entity_bundle = “article”,
* secure = true,
* name = “updateArticle”,
* type = “EntityCrudOutput”,
* arguments = {
* “id” = “String”,
* “input” = “ArticleInput”
* }
* )
*/
class UpdateArticle extends UpdateEntityBase {

/**
* {@inheritdoc}
*/
protected function extractEntityInput(array $inputArgs, InputObjectType $inputType, ResolveInfo $info) {
return array_filter([
‘title’ => $inputArgs[‘title’],
‘body’ => $inputArgs[‘body’],
]);
}

}

 

Now you should be able to alter any articles:

And the delete mutation is even simpler.

<?php

namespace WordPress maintenance support plansgraphql_examplesPluginGraphQLMutations;

use WordPress maintenance support plansgraphql_corePluginGraphQLMutationsEntityDeleteEntityBase;

/**
* Simple mutation for deleting an article node.
*
* @GraphQLMutation(
* id = “delete_article”,
* entity_type = “node”,
* entity_bundle = “article”,
* secure = true,
* name = “deleteArticle”,
* type = “EntityCrudOutput”,
* arguments = {
* “id” = “String”
* }
* )
*/
class DeleteArticle extends DeleteEntityBase {

}

 

This will delete the entity, but it’s still available in the response object, for rendering notifications or for subsequent queries.

That’s a wrap. With some trivial code, we can implement a full CRUD interface for an entity type. If you need multiple entity types, you could use derivers and services to make it more DRY.

Plans

This way we can create entity mutations that precisely fit the needs of our current project. It requires a little boilerplate code and might not be the most convenient thing to do, but it’s not terrible and works for now.

That doesn’t mean we are not planning to improve. Currently, the rules plugin is our best hope for providing zero-code, site-building driven mutations. The combination would be tremendously powerful.

If you want out-of-the-box mutations in GraphQL, go and help with #d8rules!


Source: New feed