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
131 changes: 70 additions & 61 deletions modules/ROOT/pages/custom-resolvers.adoc
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
[[custom-resolvers]]
= Custom Resolvers
= Custom resolvers
:description: This page describes how to use custom resolvers in the Neo4j GraphQL Library.
:page-aliases: ogm/examples/custom-resolvers.adoc

The Neo4j GraphQL Library generates query and mutation resolvers, so you don't need to implement them yourself.
However, if you need additional behaviors besides the autogenerated CRUD operations, you can specify custom resolvers for these scenarios.
This page describes how to set them up.

The library will autogenerate query and mutation resolvers, so you don't need to implement those resolvers yourself. However, if you would like additional behaviours besides the autogenerated CRUD operations, you can specify custom resolvers for these scenarios.
== `@customResolver`

== Custom object type field resolver
To add a field to an object type which is resolved from existing values in the type, rather than storing new values, you should mark it with the `@customResolver` directive and define a custom resolver for it.

[[custom-resolver-directive]]
=== `@customResolver`

If you would like to add a field to an object type which is resolved from existing values in the type, rather than storing new values, you should mark it with the `@customResolver` directive (see below) and define a custom resolver for it. Take for instance a simple schema:
Take, for instance, this schema:

[source, javascript, indent=0]
----
Expand All @@ -36,11 +37,13 @@ const neoSchema = new Neo4jGraphQL({
});
----

Here `fullName` is a value that is resolved from the fields `firstName` and `lastName`. Specifying the `@customResolver` directive on the field definition keeps `fullName` from being included in any `Query` or `Mutation` fields and hence as a property on the `:User` node in the database.
Here `fullName` is a value that is resolved from the fields `firstName` and `lastName`.
Specifying the `@customResolver` directive on the field definition keeps `fullName` from being included in any `Query` or `Mutation` fields and hence as a property on the `:User` node in the database.

The inclusion of the fields `firstName` and `lastName` in the `requires` argument means that in the definition of the resolver, the properties `firstName` and `lastName` will always be defined on the `source` object. If these fields are not specified, this cannot be guaranteed.
The inclusion of the fields `firstName` and `lastName` in the `requires` argument means that, in the definition of the resolver, the properties `firstName` and `lastName` will always be defined on the `source` object.
If these fields are not specified, this cannot be guaranteed.

==== Definition
=== Definition

[source, graphql, indent=0]
----
Expand All @@ -51,13 +54,64 @@ directive @customResolver(
) on FIELD_DEFINITION
----

==== The `requires` argument
=== Providing custom resolvers

Note that any field marked with the `@customResolver` directive requires a custom resolver to be defined.
If the directive is marked on an interface, any implementation of that interface requires a custom resolver to be defined.

Take for example this schema:

[source, graphql, indent=0]
----
interface UserInterface {
fullName: String! @customResolver
}

type User implements UserInterface {
id: ID!
fullName: String!
}
----

The following resolvers definition would cause a warning to be logged:

[source, javascript, indent=0]
----
const resolvers = {
UserInterface: {
fullName() {
return "Hello World!";
},
},
};
----

The following resolvers definition would silence the warning:

[source, javascript, indent=0]
----
const resolvers = {
User: {
fullName() {
return "Hello World!";
},
},
};
----

Mismatches between the resolver map and `@customResolver` directives are always logged to the console as a warning.

Any fields that the custom resolver depends on should be passed to the `requires` argument to ensure that during the Cypher generation process those properties are selected from the database.

Any field can be required, as long as it is not another `@customResolver` field.
=== The `requires` argument

The `requires` argument accepts a selection set string. Using a selection set string makes it possible to select fields from related types as below:
This is how the `requires` argument can be used on your schema:

* Accepts a selection set string.
* Can be used in any field, as long as it is not another `@customResolver` field.
* Should be used in case the custom resolver depends on any fields.
This ensures that, during the Cypher generation process, these properties are selected from the database.

Using a selection set string makes it possible to select fields from related types, as shown in the following example:

[source, javascript, indent=0]
----
Expand Down Expand Up @@ -92,7 +146,7 @@ const neoSchema = new Neo4jGraphQL({
});
----

In this example, the `firstName`, `lastName`, `address.street` and `address.city` fields will always be selected from the database if the `fullName` field is selected and will be available to the custom resolver.
Here the `firstName`, `lastName`, `address.street`, and `address.city` fields are always selected from the database if the `fullName` field is selected and is available to the custom resolver.

It is also possible to inline fragments to conditionally select fields from interface/union types:

Expand Down Expand Up @@ -124,7 +178,7 @@ type Journal implements Publication {
}
----

It is **not** possible to require extra fields generated by the library such as aggregations and connections.
However, it is **not** possible to require extra fields generated by the library such as aggregations and connections.
For example, the type definitions below would throw an error as they attempt to require the `publicationsAggregate`:

[source, graphql, indent=0]
Expand Down Expand Up @@ -155,48 +209,3 @@ type Journal implements Publication {
}
----

==== Providing custom resolvers

Note that any field marked with the `@customResolver` directive, requires a custom resolver to be defined.
If the directive is marked on an interface, any implementation of that interface requires a custom resolver to be defined.
Take for example this schema:

[source, graphql, indent=0]
----
interface UserInterface {
fullName: String! @customResolver
}

type User implements UserInterface {
id: ID!
fullName: String!
}
----

The following resolvers definition would cause a warning to be logged:

[source, javascript, indent=0]
----
const resolvers = {
UserInterface: {
fullName() {
return "Hello World!";
},
},
};
----

The following resolvers definition would silence the warning:

[source, javascript, indent=0]
----
const resolvers = {
User: {
fullName() {
return "Hello World!";
},
},
};
----

Mismatches between the resolver map and `@customResolver` directives will always be logged to the console as a warning.
58 changes: 10 additions & 48 deletions modules/ROOT/pages/driver-configuration.adoc
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
[[driver-configuration]]
:description: This page describes the configuration of the Neo4j GraphQL Library driver.
= Driver configuration

This page describes the configuration of the Neo4j GraphQL Library driver.

== Neo4j Driver

Either an instance of the https://github.com/neo4j/neo4j-javascript-driver[Neo4j JavaScript driver] must be passed in on construction of your `Neo4jGraphQL` instance (or alternatively, `OGM`), or a driver, session or transaction passed into the `context.executionContext` per request.
For the Neo4j GraphQL Library to work, either an instance of the https://github.com/neo4j/neo4j-javascript-driver[Neo4j JavaScript driver] must be passed in on construction of your `Neo4jGraphQL` instance (or alternatively, `OGM`), or a driver, session or transaction passed into the `context.executionContext` per request.

The examples in this chapter assume a Neo4j database running at "bolt://localhost:7687" with a username of "neo4j" and a password of "password".
The examples in this page assume a Neo4j database running at "bolt://localhost:7687" with a username of "neo4j" and a password of "password".

=== Neo4j GraphQL Library

Expand Down Expand Up @@ -95,9 +98,10 @@ const ogm = new OGM({ typeDefs, driver });
----

[[driver-configuration-database-compatibility]]
== Database Compatibility
== Database compatibility

Use the `checkNeo4jCompat` method available on either a `Neo4jGraphQL` or `OGM` instance to ensure the specified DBMS is of the required version, and has the necessary functions and procedures available. The `checkNeo4jCompat` will throw an `Error` if the DBMS is incompatible, with details of the incompatibilities.
Use the `checkNeo4jCompat` method available on either a `Neo4jGraphQL` or `OGM` instance to ensure the specified DBMS is of the required version, and has the necessary functions and procedures available.
The `checkNeo4jCompat` throws an `Error` if the DBMS is incompatible, with details of the incompatibilities.

=== `Neo4jGraphQL`

Expand Down Expand Up @@ -143,7 +147,7 @@ const ogm = new OGM({ typeDefs, driver });
await ogm.checkNeo4jCompat();
----

== Specifying Neo4j database
== Specifying the Neo4j database

There are two ways to specify which database within a DBMS should be used.

Expand Down Expand Up @@ -176,46 +180,4 @@ const server = new ApolloServer({
await startStandaloneServer(server, {
context: async ({ req }) => ({ req, sessionConfig: { database: "my-database" }}),
});
----

=== `Neo4jGraphQL` constructor

WARNING: This solution is deprecated, and will be removed in 4.0.0. Please pass into the context as in the example directly above.

[source, javascript, indent=0]
----
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { Neo4jGraphQL } from "@neo4j/graphql";
import neo4j from "neo4j-driver";

const typeDefs = `#graphql
type User {
name: String
}
`;

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

const neoSchema = new Neo4jGraphQL({
typeDefs,
driver,
config: {
driverConfig: {
database: "my-database",
},
},
});


const server = new ApolloServer({
schema: await neoSchema.getSchema(),
});

await startStandaloneServer(server, {
context: async ({ req }) => ({ req }),
});
----
----