Everything You Need to Know About WPGraphQL

Francis Agulto Avatar

·

This article will explore GraphQL and WPGraphQL, delving into their core principles, architecture, and how they contrast with the WordPress REST API. Whether you are a seasoned developer looking to understand this method of data retrieval or someone new to the field, this article will provide a comprehensive overview of GraphQL/WPGraphQL, its importance, and its applicability in headless web development.

What is GraphQL/WPGraphQL?

Let’s define GraphQL.  GraphQL is a query language.  And by query language, we mean a specific syntax we can use to query a server to request or mutate data. 

WPGraphQL on the other hand is a free, open-source plugin that turns your WordPress database into a GraphQL server.

Understanding the Graph in GraphQL/WPGraphQL

The “Graph” in GraphQL refers to the graph data structure which is a collection of nodes connected by edges.  This represents the relationship between different entities and how they are interconnected.  Here are the concepts:

Entities as Nodes: In GraphQL, the different entities or objects  (in the context of WordPress, this could be posts, pages, users, comments, etc.) are represented as nodes in the graph.

  • Usage: Nodes are used to encapsulate the data for a particular item and can be queried directly if an identifier (such as an ID) is available. In a connection, the nodes field is used to access the actual objects within the connection.

Example: If you have a Posts type, each title in the system would be a node.

Relationships as Edges: Edges represent the relationships between nodes. They connect nodes in the graph and can carry additional information about the relationship itself.

  • Usage: Edges are often used within connections to navigate between related nodes and can include information like cursors for pagination or other metadata about the relationship.

Example: If you’re querying for comments on a post, the edge might represent the connection between the post (node) and each comment (another node). The edge could carry information like the date the comment was added.

Schema Definition: The GraphQL schema defines the types of objects in the system and their relationships. This schema essentially describes the entire graph’s structure, including the nodes and the edges between them.  We will dive into this in the next section.

Querying the Graph: When a client sends a query to a GraphQL server, it’s essentially traversing the graph. The client can specify exactly what data it needs, including nested relationships, and the server will traverse the graph to retrieve that specific data.

Flexibility and Precision: Because of its graph-based nature, GraphQL allows clients to request exactly the data they need, even complex nested relationships, in a single query. This eliminates over-fetching (getting too much data) or under-fetching (getting too little data) that might occur in more rigid RESTful API structures.

Strongly Typed: The graph structure is strongly typed, meaning that every node and edge has a specific type, and the relationships are explicitly defined. This provides a clear contract between the client and the server, ensuring data consistency.

WPGraphQL contains these exact concepts within WordPress.   Let’s now visualize what these concepts look like in a diagram:

In the diagram above we have a visual representation of what the WPGraphQL data relationships are within the Posts type. Let’s go over what is happening in the diagram.

The posts object is a node which is the root query for retrieving a list of posts from WordPress.  In the posts object, it has a nodes field which allows us access to the “title” and “content” data.  These are leaf nodes and we will go over that in detail in the next section.

The categories type is a connection between the relationship of the posts type and categories which allows us to navigate from our posts to their associated categories.  Within categories, there are edges. 

Edges are the connections to individual categories and help define the relationship between a post and its categories.  The node inside each edge represents the individual category itself allowing you access to the properties of each category. In this diagram’s case, that property is “name”.

Now that we have a better understanding of what the Graph is, let’s take a look at the schemas and types in WPGraphQL.

Schema and Types

A schema defines the structure of the data and the operations that can be performed on that data. 

Type definitions are the different types of data we wanna expose in our graph. In the case of the WPGraphQL schema, common types in the graph are Posts, Pages, Media Items, Revisions, and Custom Post Types.  You can expose these types and see them with the Query Composer in GraphiQL IDE.

For example, the Post type has specific fields associated with it such as title and content. The post type is an entry point in WPGraphQL that you can enter to query it and ask for the fields related to it and other types related to it as well as their related fields.

Built into GraphQL and WPGraphQL are 5 basic scalar types. Here are the main primitive types that represent leaf values in WPGraphQL queries:

Scalar Types: These are primitive types that represent leaf values in a GraphQL query.

  • String (Textual Data)
  • Int (Whole Numbers)
  • Float (Decimal Numbers)
  • Boolean (Value of True or False)
  • ID (a key for data objects serialized as strings often used to fetch an object by its unique ID)

These scalar types can be used as fields in object types, as arguments to queries and mutations, and as return types from functions within your schema.

Scalar types in WPGraphQL/GraphQL are fundamental and enable a precise, strongly typed schema definition, allowing for clear contracts between clients and servers about the structure and type of data that can be sent and received.

Here is an example of leaf nodes or scalar types that don’t have any subselection of fields. It is the final level of a query where the actual data is fetched in your WPGraphQL Query:

 {
  posts {
    id         // Leaf node
    title      // Leaf node
    author {
      name     // Leaf node
      age      // Leaf node
    }
    comments {
      content  // Leaf node
    }
  }
}

Code language: JavaScript (javascript)

You can think of the WPGraphQL schema and type definitions like a map to structure the graph.  

The resolver functions handle the queries based on schema and type.  Let’s go over resolvers in the next section.

Resolver Functions

Resolver functions in WPGraphQL, as in other GraphQL implementations, are functions that handle the process of fetching and returning the actual data for a particular field in a GraphQL query. When a client sends a query to the server, these functions are called to resolve the data for the requested fields.

Here’s a high-level overview of resolver functions in WPGraphQL:

Data Fetching & Manipulation: Resolver functions in WPGraphQL serve as the bridge between GraphQL queries and the underlying WordPress database. They fetch or manipulate the data as requested by the GraphQL query.

Type-Specific Logic: These functions define how specific GraphQL types (like Posts, Users, or Custom Fields) are resolved. When a field of a specific type is requested, the corresponding resolver function is invoked.

Argument Handling: Resolver functions can handle various query arguments to filter, sort, or paginate data. They translate these GraphQL query options into corresponding WordPress queries.

Authentication and Authorization: Resolvers can include logic to check whether the current user is authorized to access or mutate the data, making them critical for data security.

Extensibility: WPGraphQL resolvers are customizable and extendable, allowing developers to modify the default data-fetching behavior or add custom logic to meet specific business needs.

If you want in-depth details about resolvers and how to write them, please visit GraphQL and WPGraphQL docs.

Query Basics

A query in GraphQL is a request made by a client to fetch data from a server. It’s one of the fundamental operations in GraphQL and is used to request only the data that a client needs, in the specific shape and structure desired.

Here is an example of a query in WPGraphQL designed to fetch a post by its slug. It retrieves specific details about the post, including its title, date, content, featured image URL, and the author’s first and last names.

query GetPostByURI($id: ID!) {
  post(id: $id, idType: SLUG) {
    title
    date
    content
    featuredImage {
      node {
        sourceUrl
      }
    }
    author {
      node {
        firstName
        lastName
      }
    }
  }
}

Code language: PHP (php)

Let’s break down the above query and its components.

1. Operation Type and Name:

query: Indicates that this is a read-only operation to fetch data.- GetPostByURI: The operator name of the query, useful for logging, debugging, and identifying the query’s purpose.

2. Variables:

($id: ID!): Defines a variable called $id of type  ID  and marks it as non-null (indicated by the `!`). This variable will be used as an input argument in the query.

3. Root Field:

post: The root field that specifies the main entity being queried.

id: $id: This uses the $id variable as the value for the id argument, representing the unique identifier (in this case, the slug) for the post.- idType: SLUG: Specifies that the id argument should be interpreted as a slug, allowing for lookup by this particular type of identifier.

4. Selection Set for post:

–  title: Requests the title of the post.

–  date: Requests the date of the post.

–  content: Requests the content of the post.

5. Nested Fields for  featuredImage:

–  featuredImage: Requests the featured image of the post.

–  node: Accesses the actual image object, as featuredImage may be a connection to other objects.

–  sourceUrl: Requests the URL of the featured image.

6. Nested Fields for  author:

–  author: Requests the author’s information related to the post.

–  node: Accesses the actual author object, as author maybe a connection to other objects.

–  firstName: Requests the first name of the author.

 lastName: Requests the last name of the author.

When we request this data on our GraphiQL IDE, we get this data back:

The structure of this query, with its root field, variables, and nested selection sets, shows that clients can request exactly the information they need, down to specific nested fields within related objects, all in a single request.

In the next section, we will go over using variables in your queries.

Query Variables

Query variables in GraphQL/WPGraphQL are named variables that you can include in your query, and their values can be changed without altering the query itself.

This feature allows you to create more dynamic and reusable queries. They provide a way to parameterize your queries, making them more flexible and maintainable.

They are particularly useful when you want to pass dynamic values into a query, such as filtering criteria, pagination controls, or identifiers for specific objects.  You define query variables at the beginning of your query, and then you can use them within the query by prefixing the variable name with a $ symbol.

Here’s an example of a WPGraphQL query that uses query variables to fetch posts of a specific post ID:

query GetPostByURI($id: ID!) {
  post(id: $id, idType: DATABASE_ID) {
    title
    content
    id
    date
    author {
      node {
        firstName
        lastName
      }
    }
  }
}

Code language: PHP (php)

In this example, $ID is a query variable that you can use to filter the posts by a specific ID.

When you execute this query, you will also provide a JSON object that supplies the actual values for the variables like so:

 {
     "categoryId": "1"
   }

Code language: JSON / JSON with Comments (json)

Notice that in the example, the variable $categoryId is defined with a type ID!. This type specification ensures that the variable must be provided and must be of the ID type.

Variables are optional and there are certain use cases when you should use them for best practices.  You can find more information here on the Apollo GraphQL documentation.

Related data in WPGraphQL refers to the relationships between different types within the WordPress data model. These relationships need to be considered and handled appropriately to fetch and return the related data as part of a query with resolver functions.

Let’s break down the concepts of how related data works within WPGraphQL:

Field Resolvers

Each field in a WPGraphQL query can have its own resolver function. When dealing with related data, the resolver for a particular field might need to fetch additional data from a related type.

Root and Parent Values

In a resolver function, the root argument represents the parent object, containing the resolved values for the parent fields. It can be used to access data needed to fetch related data.

Example: Posts and Author

Consider a query that fetches posts and their authors. The resolver for the author field within a post type would need to consider the author ID stored within the post to fetch the related author data.

Context and Authentication

Depending on the relationships and the data you are trying to fetch, you may need to consider contextual information and authentication. Some related data might only be available to authenticated users or under certain conditions.

Nesting and Recursion

Related data can also lead to nested relationships. For example, a post might be related to an author, and that author might be related to a company. Resolvers need to handle these relationships appropriately, potentially involving recursion or additional resolvers for nested fields.

The WPGraphQL plugin handles these relationships and related data under the hood by default.  You can check out the WPGraphQL documentation for a detailed look at related data.

Performance Considerations

Fetching related data can lead to performance challenges, especially with deep or complex relationships. It’s essential to consider performance optimization techniques.  Let’s go over a few of them.

Persisted Queries:

A persisted query in WPGraphQL is a query that has been saved on the WordPress server side for repeated use. Essentially, the query is stored in the WordPress database and it is identified by a unique hash or ID. Instead of sending the full text of the query over the network each time it is executed, clients can send just this ID, and the server will execute the corresponding query. 

Since the query is pre-stored on the server, the server can pre-optimize the query, by preparing the database, caching, or other optimization strategies, resulting in faster query execution times. 

The WPGraphQL Smart Cache plugin has a feature built-in to support persisted queries that you can read more about in-depth here.

Caching:

They say two of the hardest things in software development are naming things and cache invalidation.  When optimizing for performance and considering caching layers, the WPGraphQL, as mentioned in the previous paragraph, the Smart Cache plugin supports and handles cache and caching invalidation for performant, fast data that is accurate.  

Pagination:

For queries that could potentially return large datasets, pagination can be used to load data incrementally. This not only improves the user experience but also reduces the initial load on the server.  WPGraphQL supports cursor-based pagination out of the box.

There are other techniques for performance considerations and optimization such as DataLoader, Query limiting,  and monitoring and logging software like New Relic or Sentry, which you can dive into deeper within their linked respective documentation.

By integrating these optimization techniques, developers can construct more efficient queries, thereby improving the performance and scalability of applications that use WPGraphQL for data fetching and manipulation.

Mutations (add, delete, and update)

Mutations in GraphQL/WPGraphQL allow you to modify data, including adding, updating, and deleting content. They are defined operations that describe how the data should be changed, and they work through a well-defined contract between the client and the server.

Let’s go over how mutations work for different operations in WPGraphQL:

Adding Data

You can use mutations to create new content, such as a new post. Here’s an example of a mutation to create a post:


mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    post {
      id
      title
      content
    }
  }
}


Code language: PHP (php)

You would pass the input variables, containing the details of the post when executing this mutation:

{
  "input": {
    "title": "New Post",
    "content": "Content of the post",
    "status": "DRAFT"
  }
}

Code language: JSON / JSON with Comments (json)

Updating Data

Mutations also allow you to modify existing content. For example, to update a post, you would use a mutation like this:

mutation UpdatePost($input: UpdatePostInput!) {
  updatePost(input: $input) {
    post {
      id
      title
      content
    }
  }
}

Code language: PHP (php)

The input variables would include the ID of the post you want to update, along with the new values for the fields you want to change:

{
  "input": {
    "id": "cG9zdDoxNQ==",
    "title": "Updated Title",
    "content": "Updated content of the post"
  }
}

Code language: JSON / JSON with Comments (json)

Deleting Data

To delete content, you would use a mutation designed for that purpose. For example, to delete a post:

mutation DeletePost($input: DeletePostInput!) {
  deletePost(input: $input) {
    deletedId
  }
}

Code language: PHP (php)

The input variables would include the ID of the post you want to delete:

{
  "input": {
    "id": "cG9zdDoxNQ=="
  }
}

Code language: JSON / JSON with Comments (json)

Permissions and Authentication

It’s essential to consider permissions and authentication when working with mutations, as they often modify sensitive data. WPGraphQL integrates with WordPress’s permission system, ensuring that only authorized users can perform specific mutations.

The REST API and GraphQL: A Quick Comparative Study

Having touched upon the fundamentals of GraphQL and WPGraphQL, let’s dive into how they contrast with the well-known REST API.

The REST API represents an architectural style, a set of guidelines for designing networked applications. GraphQL, by contrast, is a query language complete with its syntax and rules. This makes GraphQL more specialized and purpose-built for data querying and manipulation.

Both REST and GraphQL operate over HTTP, but the latter brings its own query language to the table. This language equips developers with finer-grained control over data operations.

Requesting Data in REST 

In REST, we use predefined HTTP endpoints to fetch or modify data. For instance:

https://yourwebsite.com/wp-json/wp/v2/posts

This URL represents a REST endpoint that returns a list of posts from a WordPress database. You could also POST data to this URL to create a new post.

To fetch a specific post by its ID, the endpoint might look like this:

https://yourwebsite.com/wp-json/wp/v2/posts/123

And for fetching related, nested data like author details or categories, a GET request to additional query parameters would be added like so:

https://yourwebsite/wp-json/wp/v2/posts?_embed=author,categories

However, what if you wanted to request a page’s data and then the 5 most recent posts from a category?  This will lead to the issue of multiple requests to obtain the data needed because you can’t get it in one single request with the REST API. This is known as “under-fetching.”

Multiple Request with REST and Under-Fetching 

In order to request page data and the 5 most recent posts from a category of food, for example, we would need to send multiple requests to retrieve the data from these endpoints:

Retrieve the page data:

GET /wp-json/wp/v2/pages/{page_id}

Then retrieve the first 5 posts from the “food category”

GET /wp-json/wp/v2/posts?categories={category_id}&per_page=5

This can get cumbersome as you have a site that is scaling and requires complexity in retrieving data.  WPGraphQL/GraphQL solves this problem with one endpoint.

GraphQL: A Solution to Multiple Request Issues

GraphQL was designed to tackle this problem. Instead of multiple endpoints, GraphQL employs a single endpoint to handle various queries:

https://yoursite.wpengine.com/graphql

In the previous example via REST, we needed to hit endpoints with multiple requests. In GraphQL, when we request the page data, and the first 5 posts from the food category, we can do it in one single query to that endpoint, like so:

query GetPageDataAndRecentPosts {
  page(id: "your-page-id") {
    id
    title
    content
    # Add any other fields you need from the page
  }
  category(id: "your-category-id") {
    posts(first: 5) {
      nodes {
        id
        title
        content
        # Add any other fields you need from the posts
      }
    }
  }
}

Code language: PHP (php)

This endpoint can be queried to fetch exactly what you need—no more, no less.

Requesting Specific Data and Over-Fetching

In the REST API, if you want to request specific fields from a collection, you can do so with the _fields parameter.  In this example, we ask for the title, content, and date fields:

https://yourwebsite.com/wp-json/wp/v2/posts?_fields=title,content,date

The limitation here is when you have embedded or related data, such as author information or categories, within a resource like a post. In the REST API, you can’t easily specify which fields you want for embedded resources. 

For example, if a post has an embedded author, you can’t use _fields to request only certain fields of the embedded author’s data. This means you may end up fetching more data than you actually need, which can lead to over-fetching.

This not only inflates the payload size, leading to increased latency and bandwidth usage, but it also imposes a burden on server resources and complicates client-side code. In some instances, over-fetching may even expose sensitive information.

Constructing queries with the _fields parameter can become complex and error-prone, especially when you want to include both top-level fields and embedded data. You need to manually specify which fields to include for each resource, and as the query becomes more complex, it can become challenging to manage and locate errors.

In contrast, WPGraphQL/GraphQL allows you to specify exactly which fields you want for each resource in a single query. You can traverse nested data structures and request only the fields you need at any level. This makes GraphQL more flexible and efficient in controlling the data you receive, especially when dealing with embedded or related data.  The GraphiQL IDE allows you a visual to not only construct the queries from a schema but also handle errors.

Conclusion

I hope this article provided a better understanding of GraphQL and WPGraphQL as a whole. As always, super stoked to hear your feedback and any questions you might have on headless WordPress. Hit us up on our Discord channel and try WPGraphQL for yourself today!

The WPGraphQL Slack channel is also an excellent place to join to hit us up on all things WPGraphQL!