Node.js Neo4j OGM inspired by Mongoose & GraphQL
Use GraphQL schema language to define Models. On Model CRUD
input validated & output resolved through a generated GraphQL schema. Modularize your neo4js-graphql-js augmented schema with the additional power of an OGM for further database operations.
Models are not designed to support querying relationships use a session for this. This library is designed to place a CRUD
api over nodes. You can also create an Executable schema to execute more complex queries.
First install Node.js, then start Neo4j & finally...
$ npm install neogoose
⚠ neo4j-driver
and graphql
are peerDependencies you may need to install them too.
$ npm install neo4j-driver graphql
const neogoose = require("neogoose");
await neogoose.connect("neo4j://localhost");
const connection1 = await neogoose.createConnection("neo4j://1.1.1.1");
const connection2 = await neogoose.createConnection("neo4j://2.2.2.2");
await neogoose.disconnect();
Models are defined using GraphQL schema language.
const User = neogoose.model(
"User",
{
typeDefs: `
type User {
id: ID!
name: String!
email: String!
}
`
}
);
await neogoose.connect("neo4j://localhost");
// https://neo4j.com/developer/javascript/
const session = await neogoose.session();
const user = neogoose.model("User");
Compile your models into an neo4js-graphql-js augmented schema
const schema = neogoose.makeAugmentedSchema();
Transforms made before calling makeAugmentedSchema
constraint
directives removedValidation
directives removed
⚠ All other schema directives here are ignored in neogoose
land
Used with;
findOne
findMany
updateOne
updateMany
deleteOne
deleteMany
count
const dan = await User.findOne({
name: "Dan",
});
const users = await User.findMany({
$or: [
{ name: "Dan" },
{ name: "Daniel" }
],
});
const users = await User.findMany({
$and: [
{ name: "Dan" },
{ repo: "neogoose" }
],
});
const users = await User.findMany({
name: {
$regex: '(?i)d.*' // equal to new Regex("^d", "i")
},
});
⚠ Javascript regex not supported use regex stated here
const users = await User.findMany({
name: {
$in: ["Dan", "Daniel"]
},
});
$eq
$gt
$gte
$in
$lt
$lte
$ne
$nin
$and
$or
$regex
Used with
findMany
updateMany
deleteMany
const paginatedUsers = await User.findMany(
query,
{ skip: 30, limit: 10 }
);
create
createMany
const user = await User.create(
{
id: uuid(),
name: "Dan",
email: "email@email.com"
},
{
return: true
}
);
await User.createMany([ ... ])
findMany
findOne
const query = {
name: "Dan",
};
const users = await User.findMany(query);
const dan = await User.findOne(query);
updateOne
updateMany
const query = {
name: "Dan",
};
const update = {
name: "naD"
};
await User.updateMany(query, update);
const user = await User.updateOne(
query,
update,
{ return: true } // use to return the updated node
);
Regular update will replace all properties use
$set
to+=
properties on the node
const query = {
name: "Dan",
};
const update = {
repo: "neogoose"
};
const user = await User.updateOne(
query,
{ $set: update },
{ return: true }
);
user.name // Dan
user.repo // neogoose
$set to null
const user = await User.updateOne(
query,
{ $set: { loggedIn: null }
);
deleteOne
deleteMany
const query = {
name: "Dan",
};
await User.deleteOne(
query,
{
detach: true // set to true for DETACH DELETE, delete nodes and relationships
}
);
const users = await User.deleteMany(
query,
{ return: true } // use to return the deleted nodes
);
count
const query = {
name: "Dan",
};
const userCount = await User.count(
query
);
Records returned from your Neo4j instance are 'pulled' through a GraphQL schema, you can use Resolvers to achieve 'virtuals' on a model.
const User = neogoose.model(
"User",
{
typeDefs: `
type User {
id: ID!
name: String!
email: String!
resolved: String!
}
`,
resolvers: {
User: {
id: (root) => root.id, // Not needed
resolved: () => "I was Resolved"
}
}
}
);
Select more than Autogenerated Selection Set works well with Resolvers and complex nested types, Used with;
findOne
findMany
updateOne
updateMany
deleteOne
deleteMany
const User = neogoose.model(
"User",
{
typeDefs: `
type NestedType {
abc: String
}
type User {
id: ID!
name: String!
email: String!
nested: NestedType!
}
`
}
);
const selectionSet = `
{
id
name
email
nested {
abc
}
}
`
const dan = await User.findOne(
{
name: "Dan",
},
{ selectionSet }
);
// exists(dan.nested.abc) === true
⚠ If you don't specify Selection Set an auto generated one will be made based on the provided type
.
const User = neogoose.model(
"User",
{
typeDefs: `
type NestedType {
abc: String
}
type User {
id: ID!
name: String!
email: String!
nested: NestedType!
}
`
}
);
const AUTO_SELECTION_SET = `
{
id
name
email
nested # ⚠ ERROR
}
`
Built in support for @Validation
directive.
const User = neogoose.model(
"User",
{
typeDefs: `
input UserProperties {
id: ID!
name: String
email: String
}
type User @Validation(properties: UserProperties) {
id: ID!
name: String!
email: String!
}
`
}
);
⚠ If you don't specify @Validation
an auto generated input
will be made based on the provided type
. Nested input
types are not supported!
Before
{
typeDefs: `
type User {
id: ID!
name: String!
email: String!
}
`
}
After
The below is representing the Models auto generated schema if you don't provide
@Validation
directive.
{
typeDefs: `
input AUTO_GENERATED { # Default if you don't specify properties
id: ID!
name: String!
email: String!
}
type User @Validation(properties: AUTO_GENERATED) {
id: ID!
name: String!
email: String!
}
`
}
Built in support for graphql-constraint-directive.
const User = neogoose.model(
"User",
{
typeDefs: `
input UserProperties {
id: ID! @constraint(minLength: 5, format: "uid")
name: String @constraint(minLength: 5)
email: String @constraint(minLength: 5, format: "email")
}
type User @Validation(properties: UserProperties) {
id: ID!
name: String!
email: String!
}
`
}
);
⚠ @constraint
directives are removed before augmented schema generation.