Custom Resolvers

A common case for using the OGM will be within custom resolvers inside a Neo4j GraphQL instance (very meta!), due to the fact that it has access to some fields which the Neo4j GraphQL Library may not. A common use case might be to have a password field marked with directive @private, and a custom resolver for creating users with passwords.

To get started with this example, create your example application directory, create a new project and also the file which will contain your application code:

mkdir ogm-custom-resolvers-example
cd ogm-custom-resolvers-example
npm init --yes
touch index.js

Then you need to install your dependencies:

npm install @neo4j/graphql-ogm graphql neo4j-driver apollo-server

Assuming a running Neo4j database at "bolt://localhost:7687" with username "neo4j" and password "password", in your empty index.js file, add the following code:

const { Neo4jGraphQL } = require("@neo4j/graphql");
const { OGM } = require("@neo4j/graphql-ogm");
const { ApolloServer } = require("apollo-server");
const neo4j = require("neo4j-driver");

const { createJWT, comparePassword } = require("./utils"); // example util functions

const driver = neo4j.driver(
    "bolt://localhost:7687",
    neo4j.auth.basic("neo4j", "password")
);

const typeDefs = `
    type User {
        id: ID @id
        username: String!
        password: String! @private
    }

    type Mutation {
        signUp(username: String!, password: String!): String! ### JWT
        signIn(username: String!, password: String!): String! ### JWT
    }
`;

const ogm = new OGM({ typeDefs, driver });
const User = ogm.model("User");

const resolvers = {
    Mutation: {
        signUp: async (_source, { username, password }) => {
            const [existing] = await User.find({
                where: {
                    username,
                },
            });

            if (existing) {
                throw new Error(`User with username ${username} already exists!`);
            }

            const [user] = await User.create({
                input: [
                    {
                        username,
                        password,
                    }
                ]
            });

            return createJWT({ sub: user.id });
        },
        signIn: async (_source, { email, password }) => {
            const [user] = await User.find({
                where: {
                    username,
                },
            });

            if (!user) {
                throw new Error(`User with username ${username} not found!`);
            }

            const correctPassword = await comparePassword(password, user.password);

            if (!correctPassword) {
                throw new Error(`Incorrect password for user with username ${username}!`);
            }

            return createJWT({ sub: user.id });
        },
    },
};

const neoSchema = new Neo4jGraphQL({
    typeDefs,
    resolvers,
    config: {
        jwt: {
            secret: "secret",
        },
    },
});

const server = new ApolloServer({
    schema: neoSchema.schema,
    context: ({ req }) => ({ req }),
});

server.listen().then(({ url }) => {
    console.log(`🚀 Server ready at ${url}`);
});

It’s important to note the JWT secret being passed into the Neo4jGraphQL constructor in this example.

Back in the command line, run the following command to start your server:

node index.js

You should see the following output:

🚀 Server ready at http://localhost:4000/

You can execute the signUp Mutation against this GraphQL API to sign up, but when you go to query the user through the same API, the password field will not be available.