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
constraintdirectives removedValidationdirectives removed
⚠ All other schema directives here are ignored in neogoose land
Used with;
findOnefindManyupdateOneupdateManydeleteOnedeleteManycount
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
findManyupdateManydeleteMany
const paginatedUsers = await User.findMany(
query,
{ skip: 30, limit: 10 }
);createcreateMany
const user = await User.create(
{
id: uuid(),
name: "Dan",
email: "email@email.com"
},
{
return: true
}
);
await User.createMany([ ... ])findManyfindOne
const query = {
name: "Dan",
};
const users = await User.findMany(query);
const dan = await User.findOne(query);updateOneupdateMany
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
$setto+=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 }
);deleteOnedeleteMany
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;
findOnefindManyupdateOneupdateManydeleteOnedeleteMany
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
@Validationdirective.
{
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.
