Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5d27f14
Publish preview for all work
darrellwarde Jan 31, 2024
b9e717a
Merge pull request #105 from neo4j/chore/preview-all-branches
angrykoala Feb 14, 2024
6163507
publish 5.x docs (#113)
lidiazuin Feb 23, 2024
bbd7332
Collection of simple fixes (#107)
darrellwarde Feb 28, 2024
3d1ccb1
How-to guide on subscriptions and OGM (#101)
darrellwarde Feb 28, 2024
33092e3
Bump the prod-dependencies group with 3 updates (#94)
dependabot[bot] Mar 1, 2024
ee58783
Bump the dev-dependencies group with 1 update (#114)
dependabot[bot] Mar 1, 2024
99bb4b3
Update dependabot.yml
recrwplay Mar 1, 2024
67cf80d
Create CODEOWNERS
recrwplay Mar 1, 2024
585d7dc
Move migration to 4.0 folder
angrykoala Jan 29, 2024
37817c9
WIP: draft on migration guide for 5.0
angrykoala Jan 29, 2024
ad608af
Migration guide to 5.0.0
angrykoala Jan 30, 2024
ad73d66
Update examples in docs to comply with 5.0
angrykoala Jan 30, 2024
354a050
Apply suggestions from code review
angrykoala Jan 31, 2024
92bc357
Rever page aliases changes in troubleshooting
angrykoala Feb 1, 2024
4affb8b
5.x updates (#106)
mjfwebb Feb 15, 2024
c50c4ef
5.x publish (#112)
lidiazuin Feb 23, 2024
247918a
Remove example on custom resolvers in interfaces
angrykoala Feb 27, 2024
248ef44
Update relationships.adoc
darrellwarde Feb 27, 2024
82870d6
Remove Migration to version 4 in documentation for 5.x
angrykoala Feb 29, 2024
eab3e8a
update
lidiazuin Mar 5, 2024
7f7f489
update
lidiazuin Mar 5, 2024
b6e90fe
update
lidiazuin Mar 5, 2024
c64bb88
Merge branch '5.x' into rebase-dev
lidiazuin Mar 5, 2024
c32245d
removing link to unavailable page
lidiazuin Mar 5, 2024
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 .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./.github/ @recrwplay
3 changes: 3 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ updates:
dependency-type: "production"
dev-dependencies:
dependency-type: "development"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-minor", "version-update:semver-patch"]
2 changes: 1 addition & 1 deletion .github/workflows/docs-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
branches:
- "main"
- "dev"

jobs:

# note that the build job requires a build-verify script in package.json
Expand Down
1 change: 1 addition & 0 deletions modules/ROOT/content-nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,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
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ new Neo4jGraphQL({
});
----

[[authentication-and-authorization-jwt]]
== JWT

By default, filtering is available on https://www.rfc-editor.org/rfc/rfc7519#section-4.1[the registered claim names] in the JWT specification.
Expand Down
7 changes: 3 additions & 4 deletions modules/ROOT/pages/getting-started/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ image::neo4j-aura-dashboard.png[width=500]

=== Using a Neo4j database

For a database located at the default "bolt://localhost:7687" (see more about https://neo4j.com/docs/operations-manual/current/configuration/ports[port configuration]), with the username "neo4j" and the password "password", add the following to the bottom of your `index.js` file:
For a database located at the default "neo4j://localhost:7687" (see more about https://neo4j.com/docs/operations-manual/current/configuration/ports[port configuration]), with the username "neo4j" and the password "password", add the following to the bottom of your `index.js` file:

[source, javascript, indent=0]
----
const driver = neo4j.driver(
"bolt://localhost:7687",
"neo4j://localhost:7687",
neo4j.auth.basic("username", "password")
);

Expand All @@ -131,7 +131,6 @@ const server = new ApolloServer({
});

const { url } = await startStandaloneServer(server, {
context: async ({ req }) => ({ req }),
listen: { port: 4000 },
});

Expand Down Expand Up @@ -179,7 +178,7 @@ const typeDefs = `#graphql
`;

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

Expand Down
4 changes: 2 additions & 2 deletions modules/ROOT/pages/ogm/reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The following sections work as a reference guide to all functionalities in OGM a

|`constructor`
|Returns an `OGM` instance.
Takes an `input` object as a parameter, which is then passed to the `Neo4jGraphQL` constructor. Supported options are listed in the documentation for xref::reference/api-reference/neo4jgraphql.adoc[`Neo4jGraphQL`].
Takes an `input` object as a parameter, which is then passed to the `Neo4jGraphQL` constructor.
a|
[source, javascript, indent=0]
----
Expand All @@ -27,7 +27,7 @@ const ogm = new OGM({

|`init`
|Asynchronous method to initialize the OGM.
Internally, calls xref::reference/api-reference/neo4jgraphql.adoc#api-reference-getschema[`Neo4jGraphQL.getSchema()`] to generate a GraphQL schema, and stores the result.
Internally, calls `Neo4jGraphQL.getSchema()` to generate a GraphQL schema, and stores the result.
Initializes any models which have been created before this execution, and will throw an error if any of them are invalid.
a|
[source, javascript, indent=0]
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.
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
3 changes: 3 additions & 0 deletions modules/ROOT/pages/type-definitions/directives/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ of any required fields that is passed as arguments to the custom resolver.
| xref::/type-definitions/directives/autogeneration.adoc#type-definitions-autogeneration-id[`@id`]
| Marks a field as the unique ID for an object type, and allows for autogeneration of IDs.

| xref::/authentication-and-authorization/configuration.adoc#authentication-and-authorization-jwt[`@jwtClaim`]
| Marks a field as the unique ID for an object type, and allows for autogeneration of IDs.

| xref::/type-definitions/directives/default-values.adoc#type-definitions-default-values-limit[`@limit`]
| Used on nodes to inject values into Cypher `LIMIT` clauses.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ query {

== Asserting constraints

In order to ensure that the specified constraints exist in the database, you need to run the function `assertIndexesAndConstraints` (see more details in xref::reference/api-reference/neo4jgraphql.adoc#api-reference-assertconstraints[API reference]).
In order to ensure that the specified constraints exist in the database, you need to run the function `assertIndexesAndConstraints`.
A simple example to create the necessary constraints might look like the following, assuming a valid driver instance in the variable `driver`.
This creates two constraints, one for each field decorated with `@id` and `@unique`, and apply the indexes specified in `@fulltext`:

Expand Down
2 changes: 1 addition & 1 deletion modules/ROOT/pages/type-definitions/types/interfaces.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,4 @@ query GetProductionsStartingWithThe {
}
}
}
----
----
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@
"glob-parent": "6.0.2"
}
}
}
}