How to use the OGM with subscriptions

The Neo4j GraphQL Library can be used in conjunction with the OGM in order to extend the library’s functionalities, or to take advantage of the @private directive.

Usage

This section shows how to use subscriptions with the OGM inside custom resolvers.

Prerequisites

Use the following type definitions:

const typeDefs = `#graphql
    type User {
      email: String!
      password: String! @private
    }
`;

Set up a server that supports subscriptions. See more instructions in the Getting started page.

Adding the OGM

Enable the subscriptions feature in the OGM constructor:

const ogm = new OGM({
    typeDefs,
    driver,
    features: {
        subscriptions: true
    },
});

Create the User model, which utilizes the @private marked field on the User type in the type definitions.

const User = ogm.model("User");

Initialize the OGM instance before using it by adding the following line to the main() function:

await ogm.init();

Adding a custom resolver

Custom resolvers can be used for multiple reasons such as performing data manipulation and checks, or interact with third party systems. In this case, you only need to create a User node with the password field set. You can do that by adding a sign-up mutation:

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 { users } = await User.create({
        input: [
          {
            username,
            password,
          },
        ],
      });
      return createJWT({ sub: users[0].id });
    },
  },
};
const typeDefs = `
    type Mutation {
        signUp(username: String!, password: String!): String!
    }
`;

Conclusion

Altogether, it should look like this:

const driver = neo4j.driver(
  "bolt://localhost:7687",
  neo4j.auth.basic("neo4j", "password")
);
const typeDefs = `
  type User {
   email: String!
   password: String! @private
  }
  type Mutation {
    signUp(username: String!, password: String!): String! # custom resolver
  }
`
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 { users } = await User.create({
          input: [
              {
                  username,
                  password,
              }
          ]
      });
      return createJWT({ sub: users[0].id });
    },
  },
};
const neoSchema = new Neo4jGraphQL({
    typeDefs,
    driver,
    resolvers,
    feature: {
        subscriptions: true,
    },
});
const ogm = new OGM({
    typeDefs,
    driver,
    features: {
        subscriptions: true
    },
});
const User = ogm.model("User");

async function main() {
  // initialize the OGM instance
  await ogm.init();

   // Apollo server setup with WebSockets
  const app = express();
  const httpServer = createServer(app);
  const wsServer = new WebSocketServer({
    server: httpServer,
    path: "/graphql",
  });

  // Neo4j schema
  const schema = await neoSchema.getSchema();

  const serverCleanup = useServer(
    {
      schema,
      context: (ctx) => {
        return ctx;
      },
    },
    wsServer
  );

  const server = new ApolloServer({
    schema,
    plugins: [
      ApolloServerPluginDrainHttpServer({
        httpServer,
      }),
      {
        async serverWillStart() {
          return Promise.resolve({
            async drainServer() {
              await serverCleanup.dispose();
            },
          });
        },
      },
    ],
  });
  await server.start();

  app.use(
    "/graphql",
    cors(),
    bodyParser.json(),
    expressMiddleware(server, {
      context: async ({ req }) => ({ req }),
    })
  );

  const PORT = 4000;
  httpServer.listen(PORT, () => {
    console.log(`Server is now running on http://localhost:${PORT}/graphql`);
  });
}

Receiving the subscription events

First, run the following subscription to receive User creation events:

subscription {
  userCreated {
    createdUser {
      email
    }
    event
  }
}

Then run the sign-up mutation:

mutation {
  signUp(email: "jon.doe@xyz.com", password: "jondoe") {
    email
    password
  }
}

The results should look like this:

{
  "data": {
    "userCreated": {
      "createdUser": {
        "email": "jon.doe@xyz.com",
        "password": "jondoe"
      },
      "event": "CREATE"
    }
  }
}