Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/ROOT/content-nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
*** xref:ogm/directives.adoc[]
*** xref:ogm/selection-set.adoc[]
*** xref:ogm/type-generation.adoc[]
*** xref:ogm/subscriptions.adoc[]
*** xref:ogm/reference.adoc[]

** xref:driver-configuration.adoc[]
Expand Down
247 changes: 247 additions & 0 deletions modules/ROOT/pages/ogm/subscriptions.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
[[ogm-subscriptions]]
:description: This how-to guide shows how to use the OGM with subscriptions.
= 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 xref:ogm/private.adoc[`@private` directive].

== Usage

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

=== Prerequisites

. Use the following type definitions:
[source, javascript, indent=0]
----
const typeDefs = `#graphql
type User {
email: String!
password: String! @private
}
`;
----

. Set up a subscriptions plugin instance to be shared between the Library and the OGM constructors:
[source, javascript, indent=0]
----
const subscriptionsPlugin = new Neo4jGraphQLSubscriptionsSingleInstancePlugin();
----

. Set up a server that supports subscriptions.
See more instructions in the xref:subscriptions/getting-started.adoc#setting-up-server[Getting started page].


=== Adding the OGM

In order to utilize the `@private` marked field on the `User` type, you need to create the `User` model.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this line shouldn't be a step in the guide with an example of how to do that?

Also, to use the subscriptions, you need to pass in the subscriptions plugin instance to the OGM constructor:

[source, javascript, indent=0]
----
const ogm = new OGM({ typeDefs, driver, plugins: { subscriptions: subscriptionsPlugin } });
const Profile = ogm.model("Profile");
----

Make sure you initialize the OGM instance before using it by adding the following line to the `main()` function:
[source, javascript, indent=0]
----
await ogm.init();
----

=== Adding a custom resolver

xref:custom-resolvers/[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.
For example:

[source, javascript, indent=0]
----
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!
}
`
----

[discrete]
=== Conclusion

Altogether, it should look like this:

[source, javascript, indent=0]
----
const subscriptionsPlugin = new Neo4jGraphQLSubscriptionsSingleInstancePlugin();
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 });
},
},
};
// Share the subscriptions plugin instance across the Library and the OGM
const neoSchema = new Neo4jGraphQL({
typeDefs,
driver,
resolvers,
plugins: {
subscriptions: subscriptionsPlugin,
},
});
const ogm = new OGM({ typeDefs, driver, plugins: { subscriptions: subscriptionsPlugin } });
const Profile = ogm.model("Profile");

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:
[source, gql, indent=0]
----
subscription {
userCreated {
createdUser {
email
}
event
}
}
----

Then run the sign-up mutation:
[source, gql, indent=0]
----
mutation {
signUp(email: "jon.doe@xyz.com", password: "jondoe") {
email
password
}
}
----

The results should look like this:
[source, gql, indent=0]
----
{
"data": {
"userCreated": {
"createdUser": {
"email": "jon.doe@xyz.com",
"password": "jondoe"
},
"event": "CREATE"
}
}
}
----
1 change: 1 addition & 0 deletions modules/ROOT/pages/subscriptions/getting-started.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Then, the next step is to install the following dependencies:
npm i --save ws graphql-ws neo4j-driver @neo4j/graphql express @apollo/server body-parser cors
----

[setting-up-server]
== Set up an `@apollo/server` server

Add the following code to your `index.js` file to implement a simple `@apollo/server` server with subscriptions (for more options, see link:https://www.apollographql.com/docs/apollo-server/data/subscriptions/[Apollo's documentation]):
Expand Down