grand rule them all

In this article I will show you the power of the GRAND stack, for creating a web application on top of Neo4j where everything is typed, just by using your data schema.

The code source of this article can be found on this gitlab repository To run it, you must have a Neo4j database with the movies graph (ie. :play movie in the Neo4j browser). And don’t forget to change the login/password in the file backend/src/config.ts.

The GRAND stack

Developed by Neo4j, the GRAND stack is made for creating web application with modern technologies. It’s composed of :

It’s a mainly a GraphQL backend on top of Neo4j, and a single page application.

grandstack architecture
In this case we use React, but you can replace it by Angular or Vue.js

GraphQL & Neo4j, a great story

GraphQL considered that your data schema is a graph, and Neo4j is a graph database. So there is a perfect match between both.

Neo4j has developed a library called neo4j-graphql-js that do the glue between GraphQL & Neo4,j and it’s pretty powerful.

It can generate for you all your GraphQL resolvers just by adding schema directives

What I love also, it’s that we avoid the N+1 problem in GraphQL : one GraphQL query equals to one Cypher query. So we have performances!

But the library can do more for you, it can generate the GraphQL schema but also the Neo4j schema. Let’s see that.

Generate the schema from the Neo4j’s one

neo4j to graphql

neo4j-graphql-js comes with the function inferSchema that generates the GraphQL schema directly from a Neo4j database.

This code generates the GraphQL schema from the Neo4j one, and displays it in the console:

import { inferSchema } from "neo4j-graphql-js";
import neo4j from "neo4j-driver";
import { config } from "../src/config";

// create the neo4j driver
const driver = neo4j.driver(config.neo4j.url, neo4j.auth.basic(config.neo4j.login, config.neo4j.password));

// infer the graphql schema from neo4j
inferSchema(driver).then((result) => {

For the Neo4j movies graph, the result is:

type Person {
   _id: Long!
   born: Int
   name: String!
   acted_in: [Movie] @relation(name: "ACTED_IN", direction: OUT)
   directed: [Movie] @relation(name: "DIRECTED", direction: OUT)
   produced: [Movie] @relation(name: "PRODUCED", direction: OUT)
   wrote: [Movie] @relation(name: "WROTE", direction: OUT)
   follows: [Person] @relation(name: "FOLLOWS", direction: OUT)
   reviewed: [Movie] @relation(name: "REVIEWED", direction: OUT)

type Movie {
   _id: Long!
   released: Int!
   tagline: String
   title: String!
   persons_acted_in: [Person] @relation(name: "ACTED_IN", direction: IN)
   persons_directed: [Person] @relation(name: "DIRECTED", direction: IN)
   persons_produced: [Person] @relation(name: "PRODUCED", direction: IN)
   persons_wrote: [Person] @relation(name: "WROTE", direction: IN)
   persons_reviewed: [Person] @relation(name: "REVIEWED", direction: IN)

type ACTED_IN @relation(name: "ACTED_IN") {
  from: Person!
  to: Movie!
  roles: [String]!

type REVIEWED @relation(name: "REVIEWED") {
  from: Person!
  to: Movie!
  rating: Int!
  summary: String!

Pretty cool, isn’t it? Generally I don’t use it this way, I modify it a little by:

  • removing the _id (never use the internal id of Neo4j, it’s a bad practice)

  • rename the properties for relationships

  • review the cardinality of relationships (tips: you can also use the directive @relation for a cardinality of 1)

In my package.json, I have a task that run the piece of code above just by running npm run generate:schema But you can directly use the generated schema as it is.

Generate Neo4j schema from the GraphQL’s one

graphql to neo4j

You can also generate the Neo4j schema (ie. indexes, constraints) from the GraphQL schema.

Since version 2.16.0, neo4j-graphql-js comes with those directives:

  • @id: to be used on primary key fields

  • @index: to be used on fields where you want to create an index

  • @unique: to be used on fields that should be unique

The @id can be only used once per node, the library doesn’t support node keys. And the @index directive doesn’t support composite index. The directive creates one index per field.

Simple example :

type Person {
   id: ID! @id
   name: String! @index
   hash: String! @unique
   born: Date

Now that the definition is done, you need to apply this schema on Neo4j by calling assertSchema like that :

import { Express } from "express";
import { Server } from "http";
import { ApolloServer } from "apollo-server-express";
import { makeAugmentedSchema, assertSchema } from "neo4j-graphql-js";
import neo4j from "neo4j-driver";
import { config } from "../config";
import { resolvers, typeDefs, config as gqlConfig } from "./schema";

export function register(server: Server, app: Express): void {
  // create the neo4j driver
  const driver = neo4j.driver(
    neo4j.auth.basic(config.neo4j.login, config.neo4j.password)

  // create the Neo4j graphql schema
  const schema = makeAugmentedSchema({
    config: gqlConfig

  // create the graphql server with apollo
  const serverGraphql = new ApolloServer({
    context: { driver }

  // Register the graphql server to express
  serverGraphql.applyMiddleware({ app });

  // Sync the Neo4j schema (ie. indexes, constraints)
  assertSchema({ schema, driver, debug: true });

This is the result (due to the debug: true) :

│ (index) │      label      │   key   │    keys     │ unique │  action   │
│    0    │    'Person''name'  │ [ 'name' ]  │ false'CREATED' │
│    1    │    'Person''id'   │  [ 'id' ]   │  true'CREATED' │
│    2    │    'Person''hash'  │ [ 'hash' ]  │  true'CREATED' │

The assertSchema synchronizes your GraphQL definition with the Neo4j schema. For example, if you remove the @unique on the hash field and you re-run the script, the result will be:

│ (index) │  label   │  key   │    keys    │ unique │  action   │
│    0    │ 'Person''name' │ [ 'name' ] │ false'KEPT'   │
│    1    │ 'Person''id'  │  [ 'id' ]  │  true'KEPT'   │
│    2    │ 'Person''hash' │ [ 'hash' ] │  true'DROPPED' │

As you can see the unique constraint has been dropped!

React, TypeScript & GraphQL

react graphql ts

If you want to build a React application where types matter, obviously you need TypeScript.

Ok, but we can go further in the types definition with GraphQL, and I will show you in the next sections. But first we need to initialize our React project.

Create the project

The easiest way to create a React project with TypeScript is to use the create-react-app template with TypeScript support like that :

$> npx create-react-app frontend --template typescript

Then we need to add GraphQL and Apollo to support GraphQL

$> npm install @apollo/client GraphQL

For the dependencies, that’s all, but we need to make some code to create our GraphQL client (check the file src/graphql/client).

import { ApolloClient, InMemoryCache } from "@apollo/client";

export const client = new ApolloClient({
  uri: "http://localhost:4000/graphql",
  cache: new InMemoryCache(),

Finally, you just have to wrap your application with the ApolloProvider in your index.tsx:

import React from "react";
import ReactDOM from "react-dom";
import * as serviceWorker from "./serviceWorker";
import "./index.css";
import { App } from "./App";
// graphQl
import { ApolloProvider } from "@apollo/client";
import { client } from "./graphql/client";

    <ApolloProvider client={client}>
      <App />


At this step, you have a working React application that supports TypeScript and GraphQL.

For more details on how to integrate Apollo to your React project, you can check this page.

Generating types & hooks

To see the generation in action, we need to make some GraphQL code, so let’s continue our example based on the movies graph.

Some GraphQL code

As an example, I will do a simple query that retrieves all the actors, and the movies they played in.

First, I create a GraphQL fragment for each model:

import gql from "graphql-tag";
import { DocumentNode } from "graphql";

export const fragments: { [name: string]: DocumentNode } = {
  movie: gql`
    fragment Movie on Movie {
  person: gql`
    fragment Person on Person {

And then I can write my query:

import gql from "graphql-tag";
import { fragments } from "./fragments";

export const getActors = gql`
  query GetActors {
    actors: Person {
      acted_in {

Now we can see the cool part, the code generation.

Code Generation

Now I will show you how to generate your code from the GraphQL schema, queries & fragment.

To do that, I use graphql-codegen. Let’s install all the dependencies:

$> npm install \
  @graphql-codegen/cli \
  @graphql-codegen/typescript \
  @graphql-codegen/typescript-graphql-files-modules \
  @graphql-codegen/typescript-operations \

And create the following task in the package.json, so the code generation will be performed with npm run generate:types:

"scripts": {
  "generate:types": "graphql-codegen",

The last point is to create the configuration file for graphql-codegen. At the root of the React project, you must have a file called codegen.xml with the following content:

schema: http://localhost:4000/graphql
documents: ["src/graphql/**/*.ts"]
      - typescript
      - typescript-operations
      - typescript-react-apollo
      withHooks: true
      avoidOptionals: true
For more details on the configuration, check this page.

Some explanations :

  • schema: http://localhost:4000/GraphQL: defines the url of your GraphQL endpoint, so the generator can retrieve your GraphQL schema. It also means that your backend must be running to generate your code.

  • documents: ["src/graphql/*/.ts"]: defines the locations where the code generator can find your GraphQL queries and fragments.

  • generates: defines how and where the code will be generated. For the where, it’s in the file ./src/graphql/types.tsx. For the how, I have defined three plugins:

Now you can generate the types:

$> npm run generate:types

> frontend@0.1.0 generate:types /home/bsimard/worspaces/ouestware/grand-stack-example/frontend
> graphql-codegen

  ✔ Parse configuration
  ✔ Generate outputs

Let see the generated code int the file src/graphql/types.

The generated code

From your GraphQL schema

The generator do a lot of works on your schema, you will find types for:

  • GraphQL types (in our case Movie & Person)

  • GraphQL inputs and variables for your queries and mutations

  • the definition of your queries and mutations (search for export type Mutation = { or export type Query = {)

As an example, we will take a look at the Movie type:

export type Movie = {
  __typename?: 'Movie';
  _id: Maybe<Scalars['String']>;
  released: Scalars['Int'];
  tagline: Maybe<Scalars['String']>;
  title: Scalars['String'];
  persons_acted_in: Maybe<Array<Maybe<Person>>>;
  persons_directed: Maybe<Array<Maybe<Person>>>;
  persons_produced: Maybe<Array<Maybe<Person>>>;
  persons_wrote: Maybe<Array<Maybe<Person>>>;
  persons_reviewed: Maybe<Array<Maybe<Person>>>;

It’s the exact translation of your type from your GraphQL schema.

From your GraphQL code (queries, fragment, …​)

The generator parses also your front-end code, so it knows your queries, fragments, …​

For each fragment, you will find a type called ${my_fragment_name}Fragment. In the code we have defined a fragment named Movie, so let’s take a look at MovieFragment:

export type MovieFragment = (
  { __typename?: 'Movie' }
  & Pick<Movie, '_id' | 'title' | 'tagline' | 'released'>

And the best part is the generation of the React hooks. For each query (or mutation), you will find a hook called use${my_query_name}Query. In the code we have defined a query named GetActors, so let’s take a look at useGetActorsQuery:

export function useGetActorsQuery(baseOptions?: Apollo.QueryHookOptions<GetActorsQuery, GetActorsQueryVariables>) {
  return Apollo.useQuery<GetActorsQuery, GetActorsQueryVariables>(GetActorsDocument, baseOptions);

// for reference
export type GetActorsQueryVariables = Exact<{ [key: string]: never; }>;
export type GetActorsQuery = (
  { __typename?: 'Query' }
  & { actors: Maybe<Array<Maybe<(
    { __typename?: 'Person' }
    & { acted_in: Maybe<Array<Maybe<(
      { __typename?: 'Movie' }
      & MovieFragment
    )>>> }
    & PersonFragment
  )>>> }

As you see, everything is typed (result, variables, options, …​).

How to use the generated code

You just have to use the generated hook, like a normal Apollo hook. Here’s an example:

import React from "react";
import { useGetActorsQuery } from "./graphql/types";
import { ActorBox } from "./ActorBox";

export const ActorsList: React.FC = () => {
  // Loading the data
  const { data, loading, error } = useGetActorsQuery({ variables: {} });
  return (
      {loading && <p>Loading ...</p>}

      {error && => {
          return <p>e.message</p>;

      {data?.actors && => {
          return <ActorBox actor={actor} />;

What I also like is to use the fragments in my simple components that just display the item:

import React from "react";
import { PersonFragment, MovieFragment } from "./graphql/types";
import { MovieBox } from "./MovieBox";

interface Props {
  actor: (PersonFragment & { acted_in: Array<MovieFragment | null> | null }) | null;

export const ActorBox: React.FC<Props> = (props: Props) => {
  const { actor } = props;

  if (actor === null) return null;
  return (
    <div className="actor">
        {} - ({actor.born})
      <div className="actor-movies">
        {actor.acted_in?.map((movie) => {
          return <MovieBox key={movie?._id} movie={movie} />;
You can take a look at the full code source of this React application here.

If you run the code, you should see this result:

react screenshot


Here we have a robust stack where every layers have types, and where they are propagated from the database to the front-end. That comes with a lot of advantages:

  • fast development, thanks to code generation (from neo4j-graphql-js & graphql-codegen)

  • every one talk about the same schema

  • we have a strong interface between all the layers

  • auto-completion in IDE with types checking

  • data refactoring is easy, we directly see the impacts at the compilation