⭐ If you would like to buy me a coffee, well thank you very much that is mega kind! : https://www.buymeacoffee.com/honeyvig Hire a web Developer and Designer to upgrade and boost your online presence with cutting edge Technologies
Showing posts with label GraphQL. Show all posts
Showing posts with label GraphQL. Show all posts

Thursday, April 25, 2024

How To Work With GraphQL In WordPress In 2024

 What options do we have for integrating GraphQL with WordPress in 2024? Leonardo Losoviz describes what developments have taken place in this space during the last three years.

Three years ago, I published “Making GraphQL Work In WordPress,” where I compared the two leading GraphQL servers available for WordPress at the time: WPGraphQL and Gato GraphQL. In the article, I aimed to delineate the scenarios best suited for each.

Full disclosure: I created Gato GraphQL, originally known as GraphQL API for WordPress, as referenced in the article.

A lot of new developments have happened in this space since my article was published, and it’s a good time to consider what’s changed and how it impacts the way we work with GraphQL data in WordPress today.

This time, though, let’s focus less on when to choose one of the two available servers and more on the developments that have taken place and how both plugins and headless WordPress, in general, have been affected.

Headless Is The Future Of WordPress (And Shall Always Be)

There is no going around it: Headless is the future of WordPress! At least, that is what we have been reading in posts and tutorials for the last eight or so years. Being Argentinian, this reminds me of an old joke that goes, “Brazil is the country of the future and shall always be!” The future is both imminent and far away.

Truth is, WordPress sites that actually make use of headless capabilities — via GraphQL or the WP REST API — represent no more than a small sliver of the overall WordPress market. WPEngine may have the most extensive research into headless usage in its “The State of Headless” report. Still, it’s already a few years old and focused more on both the general headless movement (not just WordPress) and the context of enterprise organizations. But the future of WordPress, according to the report, is written in the clouds:

“Headless is emphatically here, and with the rapid rise in enterprise adoption from 2019 (53%) to 2021 (64%), it’s likely to become the industry standard for large-scale organizations focused on building and maintaining a powerful, connected digital footprint. […] Because it’s already the most popular CMS in the world, used by many of the world’s largest sites, and because it’s highly compatible as a headless CMS, bringing flexibility, extensibility, and tons of features that content creators love, WordPress is a natural fit for headless configurations.”

Just a year ago, a Reddit user informally polled people in r/WordPress, and while it’s far from scientific, the results are about as reliable as the conjecture before it:

106 are using it, 410 are not, and 361 do not know how to do it.
Informal Reddit poll results. (Large preview)

Headless may very well be the future of WordPress, but the proof has yet to make its way into everyday developer stacks. It could very well be that general interest and curiosity are driving the future more than tangible works, as another of WPEngine’s articles from the same year as the bespoke report suggests when identifying “Headless WordPress” as a hot search term. This could just as well be a lot more smoke than fire.

That’s why I believe that “headless” is not yet a true alternative to a traditional WordPress stack that relies on the WordPress front-end architecture. I see it more as another approach, or flavor, to building websites in general and a niche one at that.

That was all true merely three years ago and is still true today.

WPEngine “Owns” Headless WordPress

It’s no coincidence that we’re referencing WPEngine when discussing headless WordPress because the hosting company is heavily betting on it becoming the de facto approach to WordPress development.

Take, for instance, WPEngine’s launch of Faust.js, a headless framework with WPGraphQL as its foundation. Faust.js is an opinionated framework that allows developers to use WordPress as the back-end content management system and Next.js to render the front-end side of things. Among other features, Faust.js replicates the WordPress template system for Next.js, making the configuration to render posts and pages from WordPress data a lot easier out of the box.

WPEngine is well-suited for this task, as it can offer hosting for both Node.js and WordPress as a single solution via its Atlas platform. WPEngine also bought the popular Advanced Custom Fields (ACF) plugin that helps define relationships among entities in the WordPress data model. Add to that the fact that WPEngine has taken over the Headless WordPress Discord server, with discussions centered around WPGraphQL, Faust, Atlas, and ACF. It could very well be named the WPEngine-Powered Headless WordPress server instead.

But WPEngine’s agenda and dominance in the space is not the point; it’s more that they have a lot of skin in the game as far as anticipating a headless WordPress future. Even more so now than three years ago.

GraphQL API for WordPress → Gato GraphQL

I created a plugin several years ago called GraphQL API for WordPress to help support headless WordPress development. It converts data pulled from the WordPress REST API into structured GraphQL data for more efficient and flexible queries based on the content managed and stored in WordPress.

More recently, I released a significantly updated version of the plugin, so updated that I chose to rename it to Gato GraphQL, and it is now freely available in the WordPress Plugin Directory. It’s a freemium offering like many WordPress plugin pricing models. The free, open-source version in the plugin directory provides the GraphQL server, maps the WordPress data model into the GraphQL schema, and provides several useful features, including custom endpoints and persisted queries. The paid commercial add-on extends the plugin by supporting multiple query executions, automation, and an HTTP client to interact with external services, among other advanced features.

I know this sounds a lot like a product pitch but stick with me because there’s a point to the decision I made to revamp my existing GraphQL plugin and introduce a slew of premium services as features. It fits with my belief that

WordPress is becoming more and more open to giving WordPress developers and site owners a lot more room for innovation to work collaboratively and manage content in new and exciting ways both in and out of WordPress.

JavaScript Frameworks & Headless WordPress

Gatsby was perhaps the most popular and leading JavaScript framework for creating headless WordPress sites at the time my first article was published in 2021. These days, though, Gatsby is in steep decline and its integration with WordPress is no longer maintained.

Next.js was also a leader back then and is still very popular today. The framework includes several starter templates designed specifically for headless WordPress instances.

SvelteKit and Nuxt are surging these days and are considered good choices for establishing headless WordPress, as was discussed during WordCamp Asia 2024.

Today, in 2024, we continue to see new JavaScript framework entrants in the space, notably Astro. Despite Gatsby’s recent troubles, the landscape of using JavaScript frameworks to create front-end experiences from the WordPress back-end is largely the same as it was a few years ago, if maybe a little easier, thanks to the availability of new templates that are integrated right out of the box.

GraphQL Transcends Headless WordPress

The biggest difference between the WPGraphQL and Gato GraphQL plugins is that, where WPGraphQL is designed to convert REST API data into GraphQL data in a single direction, Gato GraphQL uses GraphQL data in both directions in a way that can be used to manage non-headless WordPress sites as well. I say this not as a way to get you to use my plugin but to help describe how GraphQL has evolved to the point where it is useful for more cases than headless WordPress sites.

Managing a WordPress site via GraphQL is possible because GraphQL is an agnostic tool for interacting with data, whatever that interaction may be. GraphQL can fetch data from the server, modify it, store it back on the server, and invoke external services. These interactions can all be coded within a single query.

GraphQL can then be used to regex search and replace a string in all posts, which is practical when doing site migrations. We can also import a post from another WordPress site or even from an RSS feed or CSV source.

And thanks to the likes of WordPress hooks and WP-Cron, executing a GraphQL query can be an automated task. For instance, whenever the publish_post hook is triggered — i.e., a new post on the site is published — we can execute certain actions, like an email notification to the site admin, or generate a featured image with AI if the post lacks one.

In short, GraphQL works both ways and opens up new possibilities for better developer and author experiences!

GraphQL Becomes A “Core” Feature In WordPress 6.5

I have gone on record saying that GraphQL should not be a core part of WordPress. There’s a lot of reasoning behind my opinion, but what it boils down to is that the WP REST API is perfectly capable of satisfying our needs for passing data around, and adding GraphQL to the mix could be a security risk in some conditions.

My concerns aside, GraphQL officially became a first-class citizen of WordPress when it was baked into WordPress 6.5 with the introduction of Plugin Dependencies, a feature that allows plugins to identify other plugins as dependencies. We see this in the form of a new “Requires Plugins” comment in a plugin’s header:

/**
 * Plugin Name: My Ecommerce Payments for Gato GraphQL
 * Requires Plugins: gatographql
 */

WordPress sees which plugins are needed for the current plugin to function properly and installs everything together at the same time, assuming that the dependencies are readily available in the WordPress Plugin Directory.

So, check this out. Since WPGraphQL and Gato GraphQL are in the plugin directory, we can now create another plugin that internally uses GraphQL and distributes it via the plugin directory or, in general, without having to indicate how to install it. For instance, we can now use GraphQL to fetch data to render the plugin’s blocks.

In other words, plugins are now capable of more symbiotic relationships that open even more possibilities! Beyond that, every plugin in the WordPress Plugin Directory is now technically part of WordPress Core, including WPGraphQL and Gato GraphQL. So, yes, GraphQL is now technically a “core” feature that can be leveraged by other developers.

Helping WordPress Lead The CMS Market, Again

While delivering the keynote presentation during WordCamp Asia 2024, Human Made co-founder Noel Tock discussed the future of WordPress. He argues that WordPress growth has stagnated in recent years, thanks to a plethora of modern web services capable of interacting and resulting in composable content management systems tailored to certain developers in a way that WordPress simply isn’t.

Historical line chart showing monolithic WordPress declining and composable CMSs growing in innovation over time.
Slide taken from Noel Tock's WordCamp Asia 2024 keynote address. (Large preview)

Tock continues to explain how WordPress can once again become a growth engine by cleaning up the WordPress plugin ecosystem and providing first-class integrations with external services.

Historical line chart showing monolithic WordPress declining and composable CMSs growing in innovation over time.
Slide taken from Noel Tock's WordCamp Asia 2024 keynote address. (Large preview)

Do you see where I am going with this? GraphQL could play an instrumental role in WordPress’s future success. It very well could be the link between WordPress and all the different services it interacts with, positioning WordPress at the center of the web. The recent Plugin Dependencies feature we noted earlier is a peek at what WordPress could look like as it adopts more composable approaches to content management that support its position as a market leader.

Conclusion

“Headless” WordPress is still “the future” of WordPress. But as we’ve discussed, there’s very little actual movement towards that future as far as developers buying into it despite displaying deep interest in headless architectures, with WordPress purely playing the back-end role.

There are new and solid frameworks that rely on GraphQL for querying data, and those won’t go away anytime soon. And those frameworks are the ones that rely on existing WordPress plugins that consume data from the WordPress REST API and convert it to structured GraphQL data.

Meanwhile, WordPress is making strides toward greater innovation as plugin developers are now able to leverage other plugins as dependencies for their plugins. Every plugin listed in the WordPress Plugin Directory is essentially a feature of WordPress Core, including WPGraphQL and Gato GraphQL. That means GraphQL is readily available for any plugin developer to tap into as of WordPress 6.5.

GraphQL can be used not only for headless but also to manage the WordPress site. Whenever data must be transformed, whether locally or by invoking an external service, GraphQL can be the tool to do it. That even means that data transforms can be triggered automatically to open up new and interesting ways to manage content, both inside and outside of WordPress. It works both ways!

So, yes, even though headless is the future of WordPress (and shall always be), GraphQL could indeed be a key component in making WordPress once again an innovative force that shapes the future of CMS.

Wednesday, March 29, 2023

Why You Should Consider Graphs For Your Next GraphQL Project

 Let’s explore what could be your solution if you are working with a complex, highly connected dataset or would like to futureproof your project against complicated database migrations and refactoring in the future. In this article, Adam Cowley examines the Graph behind GraphQL and demonstrates why Neo4j could be the best fit for your next project.

The explosion of GraphQL over the past few years has introduced many front-end developers to the concepts of data modeling and storage, turning front-end developers into full-stack developers.

GraphQL provides developers working on a simple contract with a database, guaranteeing consistency and predictability of the data returned while also managing persistence and data fetching. The developer trusts the API to store and retrieve the data most efficiently.

But convenience comes at a cost. One day, your side project hits the front page of Hacker News, and a sudden influx of users grinds your database to a halt. Sometimes, the remedy is as simple as using the right underlying database for the loads.

In this article, I will look at the Graph behind GraphQL and demonstrate why Neo4j is the best fit for your next project.

The Graph In GraphQL

GraphQL itself is a database-agnostic query language. Many database companies and startups now offer libraries that convert a GraphQL query or mutation into a query language that works with the underlying data store, whether that be SQL for relational databases, Cypher for graph databases, or any number of proprietary query languages.

Graphs provide a natural way to represent data, where Nodes (or vertices) that represent entities or things are connected together by Relationships (or edges). Depending on the underlying data storage in your GraphQL library of choice, a certain amount of gymnastics may be involved. Suddenly, unnatural tables with strange names are created, or data is duplicated to improve query response times, introducing technical debt along the way.

This is where Neo4j comes in. Neo4j is a native Graph Database. Graph Databases are in a category all of their own, and for a good reason.

Graph databases treat the connections between data as first-class citizens, storing relationships so that highly connected datasets can be queried in real-time.

An Example: Movie Recommendations

Say we’re sick of scrolling through an endless list of thumbnails on our favorite streaming platform looking for something to watch. We decided to build a new website where users can register, provide movie ratings, and in return, receive movie recommendations based on users who have similar ratings.

In our GraphQL schema, we define types that represent movie information. Users provide movie ratings, each with a score between 1 and 5. Movies can have one or more actors and one or more directors. Movies are also tagged with one or more genres.

Note: Luckily, this Recommendations dataset already exists as a free Neo4j Sandbox. Neo4j Sandbox instances are free of charge, initially run for three days, and can be extended up to 10 days.

type User {
  userId: ID!
  name: string
  email: string
  ratings: [Rating]
}

type Rating {
  user: User!
  movie: Movie!
  rating: Int
  createdAt: Date
}
type Movie {
  movieId: ID!
  title: String
  released: Date
  actors: [Role]
  directors: [Person]
}

type Role {
  person: Person!
  movie: Movie!
  roles: [String]
}
type Person {
  personId: ID!
  name: String!
  born: Date!
  roles: [Role]
  directed: [Movie]
}

Let’s take a look at how this data will be stored in a relational database, a document store, and a graph and see where we might hit a problem when trying to generate recommendations.

In A Relational Database

Relational databases provide a structured method of data storage where data is organized into tables. Tables conform to strict rules known as a database schema, where each row contains a set number of columns, each with a set data type. Where a value may not exist, nullable columns can be used.

The underlying database schema provides a perfect base to map GraphQL Type Definitions. Each field within a type description will map one-to-one with a column. Those Type Definitions can be quickly translated into an SQL query (SQL stands for Structured Query Language) to insert or retrieve data.

A JOIN is constructed at read-time for nested types, joining two tables using foreign keys to find the corresponding records in a database. Here comes the first potential problem.

Let’s look at an Entity Relationship Diagram (ERD) that describes how the data may be stored in a relational database.

The tables highlighted in yellow represent the main entities in the data model: users, people, and movies. The tables highlighted in green represent the JOIN tables required to facilitate the many-to-many relationships between the entities.

There are two potential pitfalls here. First, let’s talk about naming. The example above is fairly straightforward, but say we have a many-to-many relationship in our data model between Products and Orders — an order may contain one or more products, and a product may appear in many orders. What do we call that table? order_products, order_line? This feels unnatural, and instantly you are adding tribal knowledge to the database, making it harder for others to understand.

When you use that table to find an actor for a particular movie, you start to hit the O(n) problem.

JOINs & The O(n) Problem

GraphQL is designed to be a flexible query language that allows you to retrieve an infinite level of nested values. The more nested items retrieved, the more joins are queried. Therefore the longer the query takes. Furthermore, the more data added to the database, the larger the underlying indexes become and the longer the query will take to return a result.

This is known as the Big O notation or O(n) notation — the number of computational resources required to compute the JOINs is relative to the size of the input data. The more data added to the database, the more data needs to be processed, and the slower the database will become.

Many relational databases support subqueries or window functions, but these must still be constructed in memory at query time, which can be an expensive operation.

This problem can be partially resolved by database tuning, partitioning, or denormalizing data to improve response times, at which point you’ll need to become a database expert.

In A Document Store

Document stores, such as MongoDB or CouchDB, differ from Relational databases in that they are designed to store unstructured or semi-structured data. Data is organized into collections, each of which consists of many documents. Each document in a collection represents a single record, which can have its own unique set of key-value pairs. This approach is more flexible than relational databases, but as the data is schema-less, you must be careful to enforce consistency through your application layer.

You would most likely create collections to store users, movies, and people.

Data Duplication for Query Performance

Document Stores can also fall foul of the O(n) problem. NoSQL databases, in general, are all designed to provide various their own solutions to the problems of read and write performance.

A common approach to solve the O(n) problem is to duplicate data across collections to speed up query responses. For example, the movies collection may store directors as an array of string values.

{
  "_id": ObjectId("63da26bc2e002491266b6205"),
  "title": "Toy Story",
  "released": "1996-03-22",
  "directors": ["Tom Lasseter"]
}

This is perfect if you only want to display the data within a UI. But if we need to ask more complex questions, for example, how many movies has Tom Lasseter directed? — things start to get complicated. Do we loop through every movie record and check the directors array for a name? What if two directors share the same name?

If you want to query across collections, you would usually store a reference to the unique ID of the record in the corresponding collection. Take the user example below: the ratings for that user can be stored as an array against the user document, making it easy to access. Each rating contains a reference (in this case, a MongoDB DBRef to reference the ObjectId of the document in the movies collection).

{
 "_id": ObjectId("63da267a89f7381acf7ab183"),
 "email": "john.doe@example.com",
 "name": "John Doe",
 "ratings": [
   {
     "movie": {
       "$ref": "movies",
       "$id": ObjectId("63da2681680f57e194eb3199"),
       "$db": "neoflix"
     },
     "rating": 5
   },
   {
     "movie": {
       "$ref": "movies",
       "$id": ObjectId("63da26b613fe29cf79d92e2f"),
       "$db": "neoflix"
     },
     "rating": 3
   },
 ]
}

Document stores support pipelines or map-reduce functions that allow you to compute the JOIN at read time. But these can become unwieldy quickly and hard to reason about, and take time to compute. These read-time JOINs also fall victim to the O(n) problem. Each reference must be looked up in an index to find the corresponding record, which must also be decoded. The larger the collection, the larger the index and the longer each lookup may take. Multiply that time and complexity by the number of nested items, and all of a sudden, we’ve got a slow and complicated pipeline or map/reduce function.

To avoid this complexity, you could also store some of the required properties for the movie, for example, the movie title, in the rating object.

You may also store the movie title as a key in the rating to balance out the ease of readability and data duplication. But now we also have to make difficult decisions on what data to duplicate to speed up. If the use case changes in any way, a mountain of work is required to fit the new use case. What if we want to query from movie to rating?

You may also want to fan out your writes, duplicating data across collections to speed up the read-time performance, but that also comes with its own maintenance headaches and a whole load of potential for technical debt.

The Case for Graphs

Now, let’s look at this data as a graph. The data structure of Nodes and Relationships fits this problem well. Rather than creating JOIN tables to handle many-to-many relationships or storing duplicated data for reference, the verbs in the use case are stored as relationships

The data model above is easier to understand and reason about. At a quick glance, you can see that a User may have one or more REVIEWED relationships pointing to a Movie node. Nodes and relationships can both contain properties stored as key-value pairs. We can use this to store the rating and createdAt properties of the review directly on the relationship.

Constant Query Times #

Remember how I mentioned earlier that relationships are treated as first-class citizens? When a relationship is created in Neo4j, a pointer is appended to the node at each end of the relationship, ensuring that every node is aware of every relationship going out from or coming into it.

This enables the query engine to quickly lookup relationships without relying on an index. This ensures that query response times remain constant to the amount of the graph touched during the query rather than the data size overall.

Querying a Neo4j graph is also different from relational databases and document stores. Neo4j uses a proprietary language called Cypher. Cypher is similar in structure to SQL, but instead of starting with a SELECT statement and using JOINs to combine data, a Cypher statement begins with a MATCH clause, which defines a pattern of data to return.

Neo4j will then parse the query, examine the database schema and use database statistics to determine the most efficient way to traverse the pattern. Regardless of the way the pattern is written, the query will be executed in the same way.

Let’s look at the SQL and Cypher statements required to retrieve the data side by side. Both queries will find the names of actors from the movie The Matrix.

SQLCypher
SELECT p.name, p.born, r.roles, m.title
FROM people p
INNER JOIN roles r on p.id = r.person_id
INNER JOIN movies m on r.movie_id = m.id
WHERE m.title = ‘The Matrix’
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE m.title = ‘The Matrix’
RETURN p.name, p.born, r.roles

In a Cypher statement, you use an ASCII-art style syntax to draw the pattern you would like to read from the graph. Nodes are surrounded by parentheses ( ( and ) ), and relationships are drawn using dashes and an arrow to represent the direction. This declarative approach differs from a Pipeline in MongoDB, where you must express exactly how the data should be retrieved.

This is only a trivial example, but the more complex the use case becomes, the more a Cypher statement comes into its own. I have shown Cypher statements to business owners, architects, and even C-level executives, who have all quickly understood what the statement is doing, which cannot be said for an SQL statement and certainly cannot be said for a pipeline.

Suddenly the barrier to data engineering doesn’t seem so high.

Conclusion

My mantra has always been to use the best tool for the job, particularly when it comes to databases. You may feel that my opinion is a little biased, as I am literally paid to have this opinion. But since first installing Neo4j around a decade ago, I’ve started to see the value of connections everywhere.

A simple network of nodes and relationships is surprisingly powerful when storing data. Graph databases allow you to avoid much additional work to model your use case to work with a database and naturally handle performance and scale.

If you would like to learn more about Neo4j, you can check Neo4j GraphAcademy, where we have constructed Beginners courses that will give you the confidence to import and query data in Neo4j, and the Developer courses teach you will show you how to connect to Neo4j using one of the five official drivers: Java, JavaScript, Python, .NET, and Go.

You can create an AuraDB Free instance pre-populated with data, which will hold 200k nodes and 400k relationships, and it’s free for as long as you need.

So, if you are working with a complex, highly connected dataset or would like to futureproof your project against complicated database migrations and refactoring in the future, why not put the Graph into GraphQL?

 

Monday, November 21, 2022

Putting The Graph In GraphQL With The Neo4j GraphQL Library

 If building a GraphQL API backed by a native graph database sounds interesting or at all helpful for the problems you’re trying to solve as a developer, perhaps you’d like to give the open-source Neo4j GraphQL Library a try. Let’s take a closer look at the reasons why!

GraphQL enables an API developer to model application data as a graph, and API clients that request that data to easily traverse this data graph to retrieve it. These are powerful game-changing capabilities. But if your backend isn’t graph-ready, these capabilities could become liabilities by putting additional pressure on your database, consuming greater time and resources.

In this article, I’ll shed some light on ways you can mitigate these issues when you use a graph database as the backend for your next GraphQL API by taking advantage of the capabilities offered by the open-source Neo4j GraphQL Library.

What Graphs Are, And Why They Need A Database

Fundamentally, a graph is a data structure composed of nodes (the entities in the data model) along with the relationships between nodes. Graphs are all about the connections in your data. For this reason, relationships are first-class citizens in the graph data model.

Graphs are so important that an entire category of databases was created to work with graphs: graph databases. Unlike relational or document databases that use tables or documents, respectively, as their data models, the data model of a graph database is (you guessed it!) a graph.

GraphQL is not and was never intended to be a database query language. It is indeed a query language, yet it lacks much of the semantics we would expect from a true database query language like SQL or Cypher. That’s on purpose. You don’t want to be exposing our entire database to all our client applications out there in the world.

Instead, GraphQL is an API query language, modeling application data as a graph and purpose-built for exposing and querying that data graph, just as SQL and Cypher were purpose-built for working with relational and graph databases, respectively. Since one of the primary functions of an API application is to interact with a database, it makes sense that GraphQL database integrations should help build GraphQL APIs that are backed by a database. That’s exactly what the Neo4j GraphQL Library does — makes it easier to build GraphQL APIs backed by Neo4j.

One of GraphQL’s most powerful capabilities enables the API designer to express the entire data domain as a graph using nodes and relationships. This way, API clients can traverse the data graph to find the relevant data. This makes better sense because most API interactions are done in the context of relationships. For example, if we want to fetch all orders placed by a specific customer or all the products in a given order, we’re traversing the pattern of relationships to find those connections in our data.

Soon after GraphQL was open-sourced by Facebook in 2015, a crop of GraphQL database integrations sprung up, evidently in an effort to address the n+1 conundrum and similar problems. Neo4j GraphQL Library was one of these integrations.

Common GraphQL Implementation Problems

Building a GraphQL API service requires you to perform two steps:

  1. Define the schema and type definitions.
  2. Create resolver functions for each type and field in the schema that will be responsible for fetching or updating data in our data layer.

Combining these schema and resolver functions gives you an executable GraphQL schema object. You may then attach the schema object to a networking layer, such as a Node.js web server or lambda function, to expose the GraphQL API to clients. Often developers will use tools like Apollo Server or GraphQL Yoga to help with this process, but it’s still up to them to handle the first two steps.

If you’ve ever written resolver functions, you’ll recall they can be a bit tedious, as they’re typically filled with boilerplate data fetching code. But even worse than lost developer productivity is the dreaded n+1 query problem. Because of the nested way that GraphQL resolver functions are called, a single GraphQL request can result in multiple round-trip requests to the database. Addressing this typically involves a batching and caching strategy, adding additional complexity to your GraphQL application.

Doubling Down On GraphQL-First Development

Originally, the term GraphQL-First Development described a collaborative process. Frontend and backend teams would agree on a GraphQL schema, then go to work independently building their respective pieces of the codebase. Database integrations extend the idea of GraphQL-First development by applying this concept to the database as well. GraphQL-type definitions can now drive the database.

You can find the full code examples presented here on GitHub.

Let’s say you’re building a business reviews application where you want to keep track of businesses, users, and user reviews. GraphQL-type definitions to describe this API might look something like this:

type Business {
  businessId: ID!
  name: String!
  city: String!
  state: String!
  address: String!
  location: Point!
  reviews: [Review!]! @relationship(type: "REVIEWS", direction: IN)
  categories: [Category!]!
    @relationship(type: "IN_CATEGORY", direction: OUT)
}

type User {
  userID: ID!
  name: String!
  reviews: [Review!]! @relationship(type: "WROTE", direction: OUT)
}

type Review {
  reviewId: ID!
  stars: Float!
  date: Date!
  text: String
  user: User! @relationship(type: "WROTE", direction: IN)
  business: Business! @relationship(type: "REVIEWS", direction: OUT)
}

type Category {
  name: String!
  businesses: [Business!]!
    @relationship(type: "IN_CATEGORY", direction: IN)
}

Note the use of the GraphQL schema directive @relationship in our type definitions. GraphQL schema directives are the language’s built-in extension mechanism and key components for extending and configuring GraphQL APIs — especially with database integrations like Neo4j GraphQL Library. In this case, the @relationship directive encodes the relationship type and direction (in or out) for pairs of nodes in the database.

Type definitions are then used to define the property graph data model in Neo4j. Instead of maintaining two schemas (one for our database and another for our API), you can now use type definitions to define both the API and the database’s data model. Furthermore, since Neo4j is schema-optional, using GraphQL to drive the database adds a layer of type safety to your application.

From GraphQL Type Definitions To Complete API Schemas

In GraphQL, you use fields on special types (Query, Mutation, and Subscription) to define the entry points for the API. In addition, you may want to define field arguments that can be passed at query time, for example, for sorting or filtering. Neo4j GraphQL Library handles this by creating entry points in the GraphQL API for create, read, update, and delete operations for each type, as well as field arguments for sorting and filtering.

Let’s look at some examples. For our business reviews application, suppose you want to show a list of businesses sorted alphabetically by name. Neo4j GraphQL Library automatically adds field arguments to accomplish just this.

{
  businesses(options: { limit: 10, sort: { name: ASC } }) {
    name
  }
}

Perhaps you want to allow the users to filter this list of businesses by searching for companies by name or keyword. The where argument handles this kind of filtering:

{
  businesses(where: { name_CONTAINS: "Brew" }) {
    name
    address
  }

You can then combine these filter arguments to express very complex operations. Say you want to find businesses that are in either the Coffee or Breakfast category and filter for reviews containing the keyword “breakfast sandwich:”

{
  businesses(
    where: {
      OR: [
        { categories_SOME: { name: "Coffee" } }
        { categories_SOME: { name: "Breakfast" } }
      ]
    }
  ) {
    name
    address
    reviews(where: { text_CONTAINS: "breakfast sandwich" }) {
    stars
    text
  }
 }
}

Using location data, for example, you can even search for businesses within 1 km of our current location:

{
  businesses(
    where: {
      location_LT: {
        distance: 1000
        point: { latitude: 37.563675, longitude: -122.322243 }
      }
    }
  ) {
  name
  address
  city
  state
  }
}

As you can see, this functionality is extremely powerful, and the generated API can be configured through the use of GraphQL schema directives.

We Don’t Need No Stinking Resolvers

As we noted earlier, GraphQL server implementations require resolver functions where the logic for interacting with the data layer lives. With database integrations such as Neo4j GraphQL Library, resolvers are generated for you at query time for translating arbitrary GraphQL requests into singular, encapsulated database queries. This is a huge developer productivity win (we don’t have to write boilerplate data fetching code — yay!). It also addresses the n+1 query problem by making a single round-trip request to the database.

Moreover, graph databases like Neo4j are optimized for exactly the kind of nested graph data traversals commonly expressed in GraphQL. Let’s see this in action. Once you’ve defined your GraphQL type definitions, here’s all the code necessary to spin up your fully functional GraphQL API:

const { ApolloServer } = require("apollo-server");
const neo4j = require("neo4j-driver");
const { Neo4jGraphQL } = require("@neo4j/graphql");

// Connect to your Neo4j instance.
const driver = neo4j.driver(
  "neo4j+s://my-neo4j-db.com",
  neo4j.auth.basic("neo4j", "letmein")
);

// Pass our GraphQL type definitions and Neo4j driver instance.
const neoSchema = new Neo4jGraphQL({ typeDefs, driver });

// Generate an executable GraphQL schema object and start
// Apollo Server.
neoSchema.getSchema().then((schema) => {
  const server = new ApolloServer({
    schema,
  });
  server.listen().then(({ url }) => {
    console.log(`GraphQL server ready at ${url}`);
  });
});

That’s it! No resolvers.

Extend GraphQL With The Power Of Cypher

So far, we’ve only been talking about basic create, read, update, and delete operations. How can you handle custom logic with Neo4j GraphQL Library?

Let’s say you want to show recommended businesses to your users based on their search or review history. One way would be to implement your own resolver function with the logic for generating those personalized recommendations built in. Yet there’s a better way to maintain the one-to-one, GraphQL-to-database operation performance guarantee: You can leverage the power of the Cypher query language using the @cypher GraphQL schema directive to define your custom logic within your GraphQL type definitions.

Cypher is an extremely powerful language that enables you to express complex graph patterns using ASCII-art-like declarative syntax. I won’t go into detail about Cypher in this article, but let’s see how you could accomplish our personalized recommendation task by adding a new field to your GraphQL-type definitions:

extend type Business {
  recommended(first: Int = 1): [Business!]!
    @cypher(
      statement: """
        MATCH (this)<-[:REVIEWS]-(:Review)<-[:WROTE]-(u:User)
        MATCH (u)-[:WROTE]->(:Review)-[:REVIEWS]->(rec:Business)
        WITH rec, COUNT(*) AS score
        RETURN rec ORDER BY score DESC LIMIT $first
      """
    )
}

Here, the Business type has a recommended field, which uses the Cypher query defined above to show recommended businesses whenever requested in the GraphQL query. You didn’t need to write a custom resolver to accomplish this. Neo4j GraphQL Library is still able to generate a single database request even when using a custom recommended field.

GraphQL Database Integrations Under The Hood

GraphQL database integrations like Neo4j GraphQL Library are powered by the GraphQLResolveInfo object. This object is passed to all resolvers, including the ones generated for us by Neo4j GraphQL Library. It contains information about both the GraphQL schema and GraphQL operation being resolved. By closely inspecting this object, GraphQL database integrations can generate database queries at the time queries are placed.

Powering Low-Code, Open Source-Powered GraphQL Tools

An open-source library that works with any JavaScript GraphQL implementation can conceivably power an entire ecosystem of low-code GraphQL tools. Collectively, these tools leverage the functionality of Neo4j GraphQL Library to help make it easier for you to build, test, and deploy GraphQL APIs backed by a real graph database.

For example, GraphQL Mesh uses Neo4j GraphQL Library to enable Neo4j as a data source for data federation. Don’t want to write the code necessary to build a GraphQL API for testing and development? The Neo4j GraphQL Toolbox is an open-source, low-code web UI that wraps Neo4j GraphQL Library. This way, it can generate a GraphQL API from an existing Neo4j database with a single click.

Where From Here

If building a GraphQL API backed by a native graph database sounds interesting or at all helpful for the problems you’re trying to solve as a developer, I would encourage you to give the Neo4j GraphQL Library a try. Also, the Neo4j GraphQL Library landing page is a good starting point for documentation, further examples, and comprehensive workshops.