Custom Resolvers

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.

Custom object type field resolver

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:

const typeDefs = `
    type User {
        firstName: String!
        lastName: String!
        fullName: String! @customResolver(requires: ["firstName", "lastName"])
    }
`;

const resolvers = {
    User: {
        fullName(source) {
            return `${source.firstName} ${source.lastName}`;
        },
    },
};

const neoSchema = new Neo4jGraphQL({
    typeDefs,
    resolvers,
});

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.

@customResolver

This field will essentially be completely ignored during the generation of Query and Mutation fields, and will require a custom resolver to resolve the field.

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. Allowable fields are any returning a Scalar or Enum type including those defined using the @cypher directive.

Definition

"""Informs @neo4j/graphql that a field will be resolved by a custom resolver, and allows specification of any field dependencies."""
directive @customResolver(
    """Fields that the custom resolver will depend on."""
    requires: [String!]
) on FIELD_DEFINITION

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:

interface UserInterface {
    fullName: String! @customResolver
}

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

The following resolvers definition would be invalid:

const resolvers = {
    UserInterface: {
        fullName() {
            return "Hello World!";
        },
    },
};

Instead, the following resolvers definition would be required:

const resolvers = {
    User: {
        fullName() {
            return "Hello World!";
        },
    },
};

These checks may not always be required or desirable. If this is the case, they can be disabled using the startupValidation config option:

const neoSchema = new Neo4jGraphQL({
    typeDefs,
    config: {
        startupValidation: {
          resolvers: false
        },
    },
})

@computed

The @computed directive has been deprecated and will be removed in version 4.0. Please use the @customResolver directive instead.

This field will essentially be completely ignored during the generation of Query and Mutation fields, and will require another way to resolve the field, such as through the use of a custom resolver.

Any fields that the custom resolver depends on should be passed to the from argument to ensure that during the Cypher generation process those properties are selected from the database. Allowable fields are any returning a Scalar or Enum type including those defined using the @cypher directive.

Definition

"""Informs @neo4j/graphql that a field will be resolved by a custom resolver, and allows specification of any field dependencies."""
directive @computed(
    """Fields that the custom resolver will depend on."""
    from: [String!]
) on FIELD_DEFINITION

Custom Query/Mutation type field resolver

You can define additional custom Query and Mutation fields in your type definitions and provide custom resolvers for them. A prime use case for this is using the OGM to manipulate types and fields which are not available through the API. You can find an example of it being used in this capacity in the Custom Resolvers example.