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 🙏

© 2025 – Pkg Stats / Ryan Hefner

neography

v0.3.1

Published

Object-graph mapping library for Neo4j.

Downloads

11

Readme

Neography

Build Status Coverage Status

Neography is object-graph mapping library for Neo4j written in TypeScript. It internally uses official neo4j driver with bolt protocol. Neography supports Active Record pattern. It's goal is to provide simple and convenient API for most common operations. It also provides DSL for constructing advanced cypher queries.

Warning

This library is at early stage of development. Some parts of API most likely will be changed over time. In many areas library requires optimizations. All suggestion, opinions and ideas are gladly welcome.

Installing

npm install neography

Configuring Neography Instance

import {Neography} from 'neography';
import {TimestampsExtension} from 'neography/extensions';

const neography = new Neography({host: 'localhost', username: 'neo4j', password: 'password'});

//register extensions for managing createdAt and updatedAt properties. 
//Timestamps are stored as integer values
neography.registerExtension(TimestampsExtension.getDefault()); 

Defining Model

Neography provides three types of model types.

1. NodeEntity

import {NodeEntity} from 'neography/model';
import {nodeEntity, attribute} from 'neography/annotations';

@nodeEntity('User')
class UserNode extends NodeEntity { 
    @attribute() firstName:string;
    @attribute() lastName:string;
}

UserNode class will be directly mapped to Neo4j's node using :User label. The label for node is set by @nodeEntity decorator. Node Entity takes optional generic type in order to enable type safe constructor taking object with node's properties.

@nodeEntity('User')
class UserNode extends NodeEntity<UserNode> { 
    @attribute() firstName:string;
    @attribute() lastName:string;
}

let user1 = new UserNode({firstName: 'John'}); // OK
let user2 = new UserNode({unknownProperty: 'John'}); // Compile time error 

NodeEntity implements active record pattern and provides save() method. It creates new node in the database for not already persisted NodeEntity instance (adding auto generated, unique, url friendly id property) or updates existing node matched by id.

2. RelationshipEntity

import {RelationshipEntity} from 'neography/model';
import {relationshipEntity, attribute} from 'neography/annotations';

@relationshipEntity('HAS_REVISION')
class HasRevision extends RelationshipEntity<HasRevision> {
    @attribute() isApproved:boolean = false;
}

HasRevision represents -[:HAS_REVISION]- relation.

3. Relationship

import {attribute, nodeEntity, relationshipEntity, relationship} from "neography/annotations";
import {NodeEntity, RelationshipEntity, Relationship} from "neography/model";

    @relationshipEntity('HAS_REVISION')
    class HasRevision extends RelationshipEntity<HasRevision> {
        @attribute() isApproved:boolean = false;
    }

    @nodeEntity('BlogPost')
    class BlogPostNode extends NodeEntity<BlogPostNode> {
        @attribute() title:string;

        @relationship(HasRevision, BlogPostNode, rel => rel.outgoing())
        nextRevision:Relationship<HasRevision, BlogPostNode>;

        @relationship(HasRevision, BlogPostNode, rel => rel.incoming())
        prevRevision:Relationship<HasRevision, BlogPostNode>
    }

It defines relationships between nodes and also supports active record pattern.

let postVer1 = new BlogPostNode({title: "Neo4j"});
let postVer2 = new BlogPostNode({title: "Neo4j is fine"});
let postVer3 = new BlogPostNode({title: "Graphs are cool"}); 

postVer1.nextRevision.set(postVer2);
postVer2.nextRevision.setWithRelation({relation: new HasRevision({isApproved: true}), node: postVer3});
await postVer1.save(); // .save() creates following nodes and relationships 
// (:BlogPost {title: "Neo4j})-[:HAS_REVISION {isApproved: false}]-> / 
// (:BlogPost {title: "Neo4j is fine"})-[:HAS_REVISION {isApproved: true}]->(:BlogPost {title: Graphs are cool})

It also provides many convenient methods for fetching connected nodes. (TODO: docs required)

Querying Nodes

Neography provides convenient query builder for fetching nodes.

    @nodeEntity('Car')
    class CarNode extends NodeEntity<CarNode> {
        @attribute() manufacturer:string;
        @attribute() horsePower:number;
    }


let result = await connection.nodeQuery(CarNode).all();

//count all cars
result = await connection.nodeQuery(CarNode).count();

//get first car
result = await connection.nodeQuery(CarNode).first();

//find car by id
result = await connection.nodeQuery(CarNode).findById('someID'); //it throws error if record not found

//find car by id or get undefined
result = await connection.nodeQuery(CarNode).firstById('someID'); //it returns undefined if node not found
//it is shortcut for
result = await connection.nodeQuery(CarNode).where(w => w.attribute('id').equal('someID')).first()

//get car with greatest horsePower
result = await connection.nodeQuery(CarNode).orderBy(by => by.attribute('horsePower').desc()).limit(1).first();

// get all cars with horsePower greater than 200
result = await connection.nodeQuery(CarNode).where(w => w.attribute('horsePower').greaterThan(200)).all();

// get all cars made by Porsche and Fiat (case sensitive search)  
result = await connection.nodeQuery(CarNode).where(w => w.attribute('manufacturer').in(['Porsche', 'Fiat'])).all();

Query Builder for Cypher

Query builder provides simple DSL for building cypher queries. It tries to reflect cypher syntax without introducing any additional abstractions.

import {neography} from 'neography';
const neography = new Neography({host: 'localhost', username: 'neo4j', password: 'password'});

//create new instance of query builder
neography.query();

Inserting Nodes

//create instance of UserNode and assign properties values
let userNode = new UserNode({firstName: 'Jane', lastName: 'Doe'});

//create query
let insertQuery = neography.query()
    .create(c => c.node(userNode).as('user'))
    .returns('user');

Query can be executed by calling runQuery method on Connection instance. And returns properly mapped response

let response:{user: UserNode}[] = await connection.runQuery(insertQuery).toArray();

It equals to following cypher query

CREATE(user:UserNode { firstName: "Jane", lastName: "Doe" })
RETURN user

Response object provides convenient helper methods for manipulating data.

let response:UserNode = await connection.runQuery(insertQuery).pluck('user').first();

Matching Nodes

let matchQuery = neography.query()
    .match(m => m.node(UserNode).params({firstName: 'Jane'}).as('user'))
    .returns('user');

let users:UserNode[] = await connection.runQuery(matchQuery).pluck('user').toArray();

Matching Nodes Using Where

let matchQuery = neography.query()
    .match(m => m.node(UserNode).as('user'))
    .where(w => w.attribute('firstName').equal('Jane'))
    .returns('user');

let users:UserNode[] = await connection.runQuery(matchQuery).pluck('user').toArray();
let matchQuery = neography.query()
    .match(m => m.node(UserNode).as('user'))
    .where(w => w.attribute('id').in(['Xfcs3-2', 'YgfUop89']))
    .returns('user');

let users:UserNode[] = await connection.runQuery(matchQuery).pluck('user').toArray();

Matching Nodes Using Where Literal Statement

let matchWhere = neography.query()
    .match(m => m.node(UserNode).as('user'))
    .where(w => w.literal(`user.createdAt < {userCreateDate}`).params({userCreateDate: int(new Date('2016-12-31').getTime())}))
    .returns('user');

let users:UserNode[] = await connection.runQuery(matchWhere).pluck('user').toArray();

Matching Nodes Connected by Relationship

let matchWhere = neography.query()
    .match(m => [
        m.node(UserNode).as('user').params({id: 'someId'}),
        m.relation(KnowsRelation).as('relation'),
        m.node(UserNode).as('friend')
        ])
    .returns('user', 'relation', 'friend');

type Response = {user: UserNode, relation: KnowsRelation, friend: UserNode};
let friends:Response = await connection.runQuery(matchWhere).toArray();

Matching Nodes Using Optional Match

let matchWhere = neography.query()
    .match(m => m.node(UserNode).as('user').params({id: 'someId'}))
    .optionalMatch(m => [
        m.matchedNode('user'),
        m.relation(KnowsRelation).as('relation'),
        m.node(UserNode).as('friend')
    ])
    .returns('user', 'relation', 'friend');

type Response = {user: UserNode, relation: KnowsRelation, friend: UserNode};
let usersOptionallyHavingFriends:Response = await connection.runQuery(matchWhere).toArray();

Creating Relations For Existing Nodes

let knowsRelation = new KnowsRelation({since: int(new Date().getTime())})

let createRelation = neography.query()
                         .match(m => [
                             m.node(UserNode).params({id: 'someExistingId'}).as('user'),
                             m.node(UserNode).params({id: 'someOtherExistingId'}).as('friend')
                         ])
                         .create(c => [
                             c.matchedNode('user'),
                             c.relation(knowsRelation).as('relation'),
                             c.matchedNode('friend')]
                         )
                         .returns('user','relation', 'friend');

type Response = {user: UserNode, relation: KnowsRelation, friend: UserNode};
let userWithFriend:Response = await connection.runQuery(createRelation).toArray();

Creating Relations with New Node

let friend = new User({firstName: 'Aaron', lastName: 'Smith'});
let knowsRelation = new KnowsRelation({since: int(new Date().getTime())})

let createRelation = neography.query()
                         .match(m => m.node(UserNode).params({id: 'someExistingId'}).as('user'))
                         .create(c => [
                             c.matchedNode('user'),
                             c.relation(knowsRelation).as('relation'),
                             c.node(friend).as('friend')]
                         )
                         .returns('user','relation', 'friend');

type Response = {user: UserNode, relation: KnowsRelation, friend: UserNode};
let userWithFriend:Response = await connection.runQuery(createRelation).toArray();