In my last post Implementing cocktail recommendation engine using neo4j, I explained how to implement the most basic recommendation engine using Neo4j, this post will explain how to use that recommendation engine in your statically generated website using GatsbyJs.

First of all, let’s start by describing how the whole stack looks like. We have the Ne4j graph database running and the Cypher query ready to get the recommendation for every drink. We will need an Apollo server running in front of it to enable graphQL. GatsbyJs will connect to that graphQL endpoint during the build time to query for the recommended/similar drinks while building each drink’s detail page.

Setting up Apollo server to serve graphQL endpoint

In my previous post on the topic Low waste data modeling using graph I had explained how to set up an Apollo server backed by Neo4J database using Neo4j-driver, Apollo-server and neo4j-graphql-js. You can have a read for more detailed explanation on how its done. Just not to repeat myself I will only explain the parts that will be different from what was covered in that post.

Extending inferred typedef to add similar drinks field

To start the apollo server, you need to provide the typedef or the schema. As done in the last post we are inferring the schema from the running Neo4j database. This creates types, queries, and mutations for all the nodes and relations present in the running neo4j database. The only thing that is missing is the recommendation / similar drinks query. We will just add it as a field to Drink as that is where we will need that. The following code shows how you can extend Drink and add extra fields to the type. We are using the @cypher directive to resolve the field by directly providing Cypher query to it. This means we don’t need to add a resolver function for this field, how awesome is that.

let { typeDefs } = await inferSchema(driver, schemaInferenceOptions);

typeDefs += /* GraphQl */ `
    extend type Drink {
    similar: [Drink] @cypher (statement: """
      MATCH (this)-[:HAS_INGREDIENT]->(ingredient),
        (ingredient)<-[:HAS_INGREDIENT]-(otherCocktail:Drink)
        WHERE this <> otherCocktail
        with otherCocktail, size(collect(distinct ingredient)) as commonCount
        RETURN otherCocktail 
        ORDER BY commonCount DESC Limit 10
    """)
    }
`;

const augmentedSchema = makeAugmentedSchema({ typeDefs });

Now, you can start the apollo server by providing the schema that we have created.

const server = new ApolloServer({
  context: {
    driver,
    driverConfig: { database: process.env.NEO4J_DATABASE || 'neo4j' },
  },
  schema: augmentedSchema,
  introspection: true,
  playground: true,
});

For complete code refer to the Low waste data modeling using graph

Once you run the code, you will have a graphQL endpoint that has recommendation engine support 🎉.

Drink type definition, graphql schema
Drink type in graphql with similar drinks field

Now you can query for any drink and similar drinks to that.

GraphQL query to retrieve recommended drinks

Adding as a source for GatsbyJs

At this point, if you are familiar with how to use a graphQL endpoint as a source for gatsby, it should be straightforward to you. But if not let’s see how it is done.

In graphQl, you can add data sources from lots of different databases, backend platforms, filesystem, markdown, JSON, etc. We are going to use gatsby-source-graphql as we want to use graphQL as our data source.

Install gatsby-source-graphql by

npm install gatsby-source-graphql

And basically follow their doc to set up your project with the source. You will have to add the source config in plugins array in your gatsby-config.js

// In your gatsby-config.js
module.exports = {
  plugins: [
    // Simple config, passing URL
    {
      resolve: "gatsby-source-graphql",
      options: {
        // Arbitrary name for the remote schema Query type
        typeName: "CocktailRecommendation",
        // Field under which the remote schema will be accessible. You'll use this in your Gatsby query
        fieldName: "cocktailRecommendation",
        // Url to query from
        url: "http://localhost:4001/graphql",
      },
    },
}

Now, run your gatsby project in develop mode.

gatsby develop

The terminal should log the following message after the build is completed,

View GraphiQL, an in-browser IDE, to explore your site's data and schema
 ⠀
   http://localhost:8000/___graphql

If you go to the URL logged in your terminal you will get to the graphql endpoint created by Gatsby which now should also have the new type listed and you should be able to query for the recommendations from there.

You can now query for the cocktail you like and also get similar drinks there.

Conclusion

Adding a graphQL endpoint for Neo4j database is very straight forward, and easier than any other backends, as most of the time it eliminates the requirement of defining the resolvers. You get the running grapQL endpoint out of the box without doing much, but if you want to extend what is generated, it is also very simple to extend typedefs and add custom fields/queries that resolve automatically by using @cypher directive provided by neo4j-graphql-js library.

The second part, getting the graphql endpoint into the GatsbyJs site is just few lines of configurations. And now you are able to to use all powerful graph algorithms in your Static website generated by Gatsby.