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"
}
}
}