npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@eunikitin/neo4j-graphql-js

v0.1.21

Published

A GraphQL to Cypher query execution layer for Neo4j.

Downloads

3

Readme

neo4j-graphql-js

A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations.

neo4j-graphql-js is in early development. There are rough edges and APIs may change. Please file issues for any bugs that you find or feature requests.

Installation and usage

Install

npm install --save neo4j-graphql-js

Then call neo4jgraphql() in your GraphQL resolver. Your GraphQL query will be translated to Cypher and the query passed to Neo4j.

import { neo4jgraphql } from 'neo4j-graphql-js';

const resolvers = {
  Query: {
    Movie(object, params, ctx, resolveInfo) {
      return neo4jgraphql(object, params, ctx, resolveInfo);
    }
  }
};

What is neo4j-graphql-js

A package to make it easier to use GraphQL and Neo4j together. neo4j-graphql-js translates GraphQL queries to a single Cypher query, eliminating the need to write queries in GraphQL resolvers and for batching queries. It also exposes the Cypher query language through GraphQL via the @cypher schema directive.

Goals

  • Translate GraphQL queries to Cypher to simplify the process of writing GraphQL resolvers
  • Allow for custom logic by overriding of any resolver function
  • Work with graphl-tools, graphql-js, and apollo-server
  • Support GraphQL servers that need to resolve data from multiple data services/databases
  • Expose the power of Cypher through GraphQL via the @cypher directive

How it works

neo4j-graphql-js aims to simplify the process of building GraphQL APIs backed by Neo4j, embracing the paradigm of GraphQL First Development. Specifically,

  • The Neo4j datamodel is defined by a GraphQL schema.
  • Inside resolver functions, GraphQL queries are translated to Cypher queries and can be sent to a Neo4j database by including a Neo4j driver instance in the context object of the GraphQL request.
  • Any resolver can be overridden by a custom resolver function implementation to allow for custom logic
  • Optionally, GraphQL fields can be resolved by a user defined Cypher query through the use of the @cypher schema directive.

Start with a GraphQL schema

GraphQL First Development is all about starting with a well defined GraphQL schema. Here we'll use the GraphQL schema IDL syntax, compatible with graphql-tools (and other libraries) to define a simple schema:

const typeDefs = `
type Movie {
  movieId: ID!
  title: String
  year: Int
  plot: String
  poster: String
  imdbRating: Float
  similar(first: Int = 3, offset: Int = 0): [Movie] @cypher(statement: "MATCH (this)-[:IN_GENRE]->(:Genre)<-[:IN_GENRE]-(o:Movie) RETURN o")
  degree: Int @cypher(statement: "RETURN SIZE((this)-->())")
  actors(first: Int = 3, offset: Int = 0): [Actor] @relation(name: "ACTED_IN", direction:"IN")
}

type Actor {
  id: ID!
  name: String
  movies: [Movie]
}


type Query {
  Movie(id: ID, title: String, year: Int, imdbRating: Float, first: Int, offset: Int): [Movie]
}
`;

We define two types, Movie and Actor as well as a top level Query Movie which becomes our entry point. This looks like a standard GraphQL schema, except for the use of two directives @relation and @cypher. In GraphQL directives allow us to annotate fields and provide an extension point for GraphQL.

  • @cypher directive - maps the specified Cypher query to the value of the field. In the Cypher query, this is bound to the current object being resolved.
  • @relation directive - used to indicate relationships in the data model. The name argument specifies the relationship type, and direction indicates the direction of the relationship ("IN" or "OUT" are valid values)

Translate GraphQL To Cypher

Inside each resolver, use neo4j-graphql() to generate the Cypher required to resolve the GraphQL query, passing through the query arguments, context and resolveInfo objects.

import { neo4jgraphql } from 'neo4j-graphql-js';

const resolvers = {
  // entry point to GraphQL service
  Query: {
    Movie(object, params, ctx, resolveInfo) {
      return neo4jgraphql(object, params, ctx, resolveInfo);
    }
  }
};

GraphQL to Cypher translation works by inspecting the GraphQL schema, the GraphQL query and arguments. For example, this simple GraphQL query

{
  Movie(title: "River Runs Through It, A") {
    title
    year
    imdbRating
  }
}

is translated into the Cypher query

MATCH (movie:Movie {title:"River Runs Through It, A"})
RETURN movie { .title , .year , .imdbRating } AS movie
SKIP 0

A slightly more complicated traversal

{
  Movie(title: "River Runs Through It, A") {
    title
    year
    imdbRating
    actors {
      name
    }
  }
}

becomes

MATCH (movie:Movie {title:"River Runs Through It, A"})
RETURN movie { .title , .year , .imdbRating,
  actors: [(movie)<-[ACTED_IN]-(movie_actors:Actor) | movie_actors { .name }] }
AS movie
SKIP 0

@cypher directive

NOTE: The @cypher directive has a dependency on the APOC procedure library, specifically the function apoc.cypher.runFirstColumn to run subqueries. If you'd like to make use of the @cypher feature you'll need to install this version of APOC in Neo4j 3.2.

GraphQL is fairly limited when it comes to expressing complex queries such as filtering, or aggregations. We expose the graph querying language Cypher through GraphQL via the @cypher directive. Annotate a field in your schema with the @cypher directive to map the results of that query to the annotated GraphQL field. For example:

type Movie {
  movieId: ID!
  title: String
  year: Int
  plot: String
  similar(first: Int = 3, offset: Int = 0): [Movie]
    @cypher(
      statement: "MATCH (this)-[:IN_GENRE]->(:Genre)<-[:IN_GENRE]-(o:Movie) RETURN o ORDER BY COUNT(*) DESC"
    )
}

The field similar will be resolved using the Cypher query

MATCH (this)-[:IN_GENRE]->(:Genre)<-[:IN_GENRE]-(o:Movie) RETURN o ORDER BY COUNT(*) DESC

to find movies with overlapping Genres.

Querying a GraphQL field marked with a @cypher directive executes that query as a subquery:

GraphQL:

{
  Movie(title: "River Runs Through It, A") {
    title
    year
    imdbRating
    actors {
      name
    }
    similar(first: 3) {
      title
    }
  }
}

Cypher:

MATCH (movie:Movie {title:"River Runs Through It, A"})
RETURN movie { .title , .year , .imdbRating,
  actors: [(movie)<-[ACTED_IN]-(movie_actors:Actor) | movie_actors { .name }],
  similar: [ x IN apoc.cypher.runFirstColumn("
        WITH {this} AS this
        MATCH (this)-[:IN_GENRE]->(:Genre)<-[:IN_GENRE]-(o:Movie)
        RETURN o",
        {this: movie}, true) | x { .title }][..3]
} AS movie
SKIP 0

Query Neo4j

Inject a Neo4j driver instance in the context of each GraphQL request and neo4j-graphql-js will query the Neo4j database and return the results to resolve the GraphQL query.

let driver;

function context(headers, secrets) {
  if (!driver) {
    driver = neo4j.driver(
      'bolt://localhost:7687',
      neo4j.auth.basic('neo4j', 'letmein')
    );
  }
  return { driver };
}
server.use(
  '/graphql',
  bodyParser.json(),
  graphqlExpress(request => ({
    schema,
    rootValue,
    context: context(request.headers, process.env)
  }))
);

See /examples for complete examples using different GraphQL server libraries.

Benefits

  • Send a single query to the database
  • No need to write queries for each resolver
  • Exposes the power of the Cypher query langauge through GraphQL

Test

We use the ava test runner.

npm install
npm build
npm test

Currently we only have simple unit tests verifying generated Cypher as translated from GraphQL queries and schema. More complete tests coming soon.

Examples

See /examples

Features

  • [x] translate basic GraphQL queries to Cypher
  • [x] first and offset arguments for pagination
  • [x] @cypher schema directive for exposing Cypher through GraphQL
  • [x] Handle enumeration types
  • [x] Handle fragments
  • [ ] Handle interface types
  • [ ] Handle inline fragments
  • [ ] Ordering