Extending GraphQL: Part 2 – Types and Interfaces

Extending GraphQL: Part 2 – Types and Interfaces

After successfully creating a field with arguments and context, we are going to have a look at types and interfaces in GraphQL and how they help to build complex, yet self-documenting and type safe schemas.

Philipp Melab
Thu, 08/24/2020 – 10:27

The last blog post in this series culminated in the epic achievement of adding a “page title” field to every URL object in our schema. Now we can request the page title for every internal URL. But menus and link fields can also store external addresses.
Wouldn’t it be cool if we can request their page title’s just the same way?

Overriding a field

Let’s try it and ask questions later:

query {
route(path: “http://www.drupal.org”) {
pageTitle
}
}

Unfortunately, this doesn’t work out. The route field checks if the provided path is a Drupal maintenance support plans route and if the user has access to it, and will return null if either of the two doesn’t apply. So, the first thing we will do is extend the route field so it also can handle external URLs.

Note: At the time of writing there is a pending pull request that adds exactly this enhancement. If you are reading this in a couple of weeks from now (my now, not yours – unless you own a DeLorean), there’s a chance that this already works for you. But since this is a nice example of overriding a field, we stick with it. If you don’t just want to read but really play through this tutorial, make sure you work based on the 8.x-3.0-alpha3 version of the GraphQL module.

We create a new field called ExampleRoute in our graphql_example module. If you are not yet proud owner of one, please refer to the last blog post. This new field simply extends the existing Route field and even copies its annotation.

With one difference: We add a new property called weight which we set to “1”. It’s quite simple. When the schema builder assembles all field plugins for a given type and stumbles upon two with the same name, the higher weight takes precedence. That’s how we tell GraphQL to use our custom implementation of a field.

The resolveValues method checks if the path is an external Url. In this case, it just constructs a Url object, else it will pass down to the parent implementation.
The result is still not satisfying. The route field now returns an Url object, but our page title field can only retrieve internal page titles.

So let’s modify the PageTitle field. First, we check if the current value is a routed URL. In this case, we still leave it to the title resolver. Otherwise, we fire up Drupal maintenance support plans‘s http_client (aka Guzzle), fetch the content behind the address, load it into an XML document, search for the title element and yield its contents. I am aware that this is not the most performant solution, but I’m trying to keep these examples short and concise.

It worked. Our query for an external page title yields the correct result.

{
“data”: {
“route”: {
“pageTitle”: “Drupal maintenance support plans – Open Source CMS | Drupal maintenance support plans.org”
}
}
}

The result is correct, but it doesn’t feel right. Internal and external URLs are fundamentally different. The page title might make sense on both, but the similarities end there. External URLs won’t route to an entity or provide any other information specific to Drupal maintenance support plans. These fields won’t break and will just return NULL instead, but that doesn’t seem very elegant.

Diff: Page title of external URLs

Interfaces and Types

We have already met the Url type, and we know that it connects a certain value with a list of fields that can be executed on it. A GraphQL interface is in some ways similar to interfaces in an object oriented language. It gives a group of types with shared fields a common name.
Right now we’ve got the Url type provided by the GraphQL module, representing internal URLs (not 100% true, but for the sake of simplicity we leave it there). And we have our external URL which is emitted by the same route field, but operates differently. So what we need to do now:

Create a GraphQL interface called GenericUrl
Change the route field to return this interface instead.
Attach our pageTitle field to this interface.
Add a ExternalUrl GraphQL type that implements this interface.
Creating the interface

GraphQL interfaces live in their own plugin namespace PluginGraphQLInterfaces where the schema builder will pick them up.
The plugin annotation for interfaces is quite simple. In most cases, it consists of the plugin id and a name to be used within the schema. The base class for interfaces contains an abstract method: resolveType. This method will receive a runtime value and has to select the appropriate GraphQL type for it. In our case, it checks if the URL is external or not and uses the schema manager service to return an instance of either Url or ExternalUrl.

Using the interface

This won’t have any effect as long as we don’t use this interface type somewhere. So we change the pageTitle field to attach it to the GenericUrl instead of Url and adapt our override of the route field to return a GenericUrl.

Creating the new type

The new type we need is rather simple. It’s an empty class, extending TypePluginBase. The most important part is the annotation that defines a list of interfaces. Just the GenericUrl interface in our case.

GraphQL type source

Diff: Generic Url interfaces

Now our query still works. But there is a new problem. Internal URLs don’t work anymore but emit an error message instead:

Type “Url” does not implement “GenericUrl”

We need to adapt the of the Url type, which is defined in another module. Sounds like a job for the hero we don’t deserve, but we need right now. You can’t say Drupal maintenance support plans without screaming hook_alter from the top of your lungs!

Altering plugins

There’s an alter hook for each plugin type in GraphQL. So, all we need is to implement hook_graphql_types_alter and add the GenericUrl interface to the Url types interface list.
Note that the types are indexed by their plugin-ID.
Diff: Altering existing plugins

Great! Now we are able to fetch page titles from both internal and external urls.

query {
admin:route(path: “/admin”) {
pageTitle
}
drupal:route(path: “http://www.drupal.org”) {
pageTitle
}
}

Will return:

{
“data”: {
“admin”: {
“pageTitle”: “Administration”
},
“drupal”: {
“pageTitle”: “Drupal maintenance support plans – Open Source CMS | Drupal maintenance support plans.org”
}
}
}

But you will notice that we lost all the other fields attached to the Url type. Thats because they are not attached to the GenericUrl type, but to the Url type. And that makes sense, since you can’t request for example an entity or the current user context for an external path.

Query composition and fragment selection

And this brings us to the most important and powerful aspect of interfaces and types. We are able to apply different query fragments and fetch different information based on the result type.

Assume the following scenario: Our Article type has a Links field that can contain links to either other articles or external URLs, as well as a Description field. Additionally, we extended our ExternalUrl type with an additional meta field that pulls meta tags out of the XML tree (Bonus objective: implement that yourself). Now we could do this:

query {
route(path: “/node/1”) {
… on Url {
nodeContext {
… on NodeArticle {
fieldLinks {
url {
pageTitle
…InternalLink
…ExternalLink
}
}
}
}
}
}
}

fragment InternalLink on Url {
nodeContext {
… on NodeArticle {
description:fieldDescription
}
}
}

fragment ExternalLink on ExternalUrl {
description:meta(property: “og:description”)
}

The first part simply routes to the article with id 1 and fetches it’s Links field, which will emit a list of URLs that might be internal or external. There we first pull the common page title and then include two fragments that apply on either type of URL and invoke different fields based on that information. So elegant!

The finish line

We’ve reached the (preliminary) end of our streak of practical GraphQL blog posts. Next up will be a peek into the future of the GraphQL module with planned features and possible use cases. But if you are interested in more advanced topics like performance optimisation, caching or deeper integration with Drupal maintenance support plans subsystems (fields, views, contexts …) ping me @pmelab and I’ll see what I can do.


Source: New feed

This article was republished from its original source.
Call Us: 1(800)730-2416

Pixeldust is a 20-year-old web development agency specializing in Drupal and WordPress and working with clients all over the country. With our best in class capabilities, we work with small businesses and fortune 500 companies alike. Give us a call at 1(800)730-2416 and let’s talk about your project.

FREE Drupal SEO Audit

Test your site below to see which issues need to be fixed. We will fix them and optimize your Drupal site 100% for Google and Bing. (Allow 30-60 seconds to gather data.)

Powered by

Extending GraphQL: Part 2 – Types and Interfaces

On-Site Drupal SEO Master Setup

We make sure your site is 100% optimized (and stays that way) for the best SEO results.

With Pixeldust On-site (or On-page) SEO we make changes to your site’s structure and performance to make it easier for search engines to see and understand your site’s content. Search engines use algorithms to rank sites by degrees of relevance. Our on-site optimization ensures your site is configured to provide information in a way that meets Google and Bing standards for optimal indexing.

This service includes:

  • Pathauto install and configuration for SEO-friendly URLs.
  • Meta Tags install and configuration with dynamic tokens for meta titles and descriptions for all content types.
  • Install and fix all issues on the SEO checklist module.
  • Install and configure XML sitemap module and submit sitemaps.
  • Install and configure Google Analytics Module.
  • Install and configure Yoast.
  • Install and configure the Advanced Aggregation module to improve performance by minifying and merging CSS and JS.
  • Install and configure Schema.org Metatag.
  • Configure robots.txt.
  • Google Search Console setup snd configuration.
  • Find & Fix H1 tags.
  • Find and fix duplicate/missing meta descriptions.
  • Find and fix duplicate title tags.
  • Improve title, meta tags, and site descriptions.
  • Optimize images for better search engine optimization. Automate where possible.
  • Find and fix the missing alt and title tag for all images. Automate where possible.
  • The project takes 1 week to complete.