Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
07-08-2022 05:43 AM - edited 07-08-2022 05:46 AM
Hi I am having massive trouble to do an apparently very simple thing:
NestJS + GraphQL + Neo4J
The issue: the custom directive of neo4j generated from graphql schema are unknown to any framework from the outside.
I see that many other people have struggled with that and can not find any example or doc how to find it.
Here is my generated schema out of neo4j:
Now I want just to spin up a nestjs stack using this schema and generate the types for it, so I do something like:
The error is always the same, the @relationship directive is just unknown to nestjs and there is no way to configure it easilly.
Thanks in advance for your help.
Solved! Go to Solution.
08-16-2022 07:05 AM - edited 08-16-2022 07:06 AM
It looks like I found a solution, and its name is ... OGM
With OGM I get my generated types out of the graphQL schema and I can pass a custom resolver to my neo4jgraphql server so I use the custom resolvers and thats it. My mistake was in the assumption that nestjs can do the type generation out of a neo4j specific schema, but the only way to resolve that is to explain custom directives to nestjs, which is too much work and not needed.
To save time to others, here is a full working example, assuming you have a generated graphql schema.
import { Neo4jGraphQL } from '@neo4j/graphql';
import { generate, OGM } from '@neo4j/graphql-ogm';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { readFileSync } from 'fs';
import neo4j from 'neo4j-driver';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ModelMap } from './schema/ogmTypes';
const driver = neo4j.driver(
'neo4j://localhost:7687',
neo4j.auth.basic('neo4j', 'password')
);
// Step 1. generate your schema out of neo4j db, using neo4j driver
// like
// const { toGraphQLTypeDefs } = require("@neo4j/introspector")
// const typeDefs = await toGraphQLTypeDefs(sessionFactory);
// fs.writeFileSync(
// 'apps/be/src/app/schema/generatedSchema.graphql',
// typeDefs
// );
// await driver.close();
//
const SCHEMA = 'apps/be/src/app/schema/schemaMovieDB.graphql';
const generateTypesFromGraphQlSchema = false;
const generatedTypesPath = 'apps/be/src/app/schema/ogmTypes.ts';
@Module({
imports: [
GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
useFactory: async () => {
const typeDefs = readFileSync(SCHEMA, 'utf-8');
// 2. Step, generate TS types according to given schema to use it for custom logic
const ogm = new OGM<ModelMap>({ typeDefs, driver });
if (generateTypesFromGraphQlSchema) {
await generate({
ogm,
outFile: generatedTypesPath,
});
console.log('Types Generated to ', generatedTypesPath);
process.exit(1);
}
await ogm.init();
const resolvers = {
Query: {
yourCustomerResolverName: async (_source, { name }) => {
const extractedType = 'Person';
const SomeCustomType = ogm.model(extractedType);
const companies = await SomeCustomType.find({ where:{name:"SomeName"} });
// const companies = [];
return companies;
},
},
};
// 3. Step bring it all together with graphQL Schema and custom resolvers to configure the final graphQL server
const neo4jGraphQL = new Neo4jGraphQL({ typeDefs, driver, resolvers });
const schema = await neo4jGraphQL.getSchema();
return {
schema,
playground: true,
};
},
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
07-08-2022 11:23 AM
You need to await the async schema generation before you pass it to Apollo Server,
you definitely need to use the schema generated by the library.
07-12-2022 02:03 AM - edited 07-13-2022 01:44 AM
Hi Michael,
thanks for the tip, yes I know I had this issue in the code, but I though its not about it.
Now I managed to create it loading before with async config in nest, but I still have the same issue. Here is my currenct code, its obviously still not taking the schema.
import { Neo4jGraphQL } from '@neo4j/graphql';
import { ApolloDriverConfig, ApolloDriver } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { readFileSync } from 'fs';
import neo4j from 'neo4j-driver';
import { AppController } from './app.controller';
import { AppService } from './app.service';
const neoDriver = neo4j.driver(
'neo4j://localhost:7687',
neo4j.auth.basic('neo4j', 'password')
);
@Module({
imports: [
GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
useFactory: async () => {
const typeDefs = readFileSync(
'apps/be/src/app/schema/generatedSchemaRel.graphql',
'utf-8'
);
const neo4jGraphQL = new Neo4jGraphQL({ typeDefs, driver: neoDriver });
const schema = await neo4jGraphQL.getSchema();
console.log('neo4jGraphQLSchema in app ', Object.keys(schema));
return {
schema,
typePaths: ['./**/*.graphql'],
definitions: {
path: `${process.cwd()}/apps/be/src/app/schema/generatedGraphql.ts`,
},
};
},
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Here is the log
[Nest] 75139 - 07/13/2022, 11:08:44 AM LOG [NestFactory] Starting Nest application...
neo4jGraphQLSchema in app [
'__validationErrors',
'description',
'extensions',
'astNode',
'extensionASTNodes',
'_queryType',
'_mutationType',
'_subscriptionType',
'_directives',
'_typeMap',
'_subTypeMap',
'_implementationsMap'
]
[Nest] 75139 - 07/13/2022, 11:08:44 AM LOG [InstanceLoader] AppModule dependencies initialized +138ms
[Nest] 75139 - 07/13/2022, 11:08:44 AM LOG [InstanceLoader] GraphQLSchemaBuilderModule dependencies initialized +0ms
[Nest] 75139 - 07/13/2022, 11:08:44 AM LOG [InstanceLoader] GraphQLModule dependencies initialized +0ms
[Nest] 75139 - 07/13/2022, 11:08:44 AM LOG [RoutesResolver] AppController {/api}: +2ms
[Nest] 75139 - 07/13/2022, 11:08:44 AM LOG [RouterExplorer] Mapped {/api, GET} route +2ms
/projectRootPath/node_modules/graphql/validation/validate.js:135
throw new Error(errors.map((error) => error.message).join('\n\n'));
^
Error: Unknown directive "@relationshipProperties".
Unknown directive "@relationship".
Unknown type "BigInt". Did you mean "Int"?
.... some more similar Unknown type errors here...
Unknown type "BigInt". Did you mean "Int"?
at assertValidSDL (/projectRootPath/node_modules/graphql/validation/validate.js:135:11)
at Object.buildASTSchema (/projectRootPath/node_modules/graphql/utilities/buildASTSchema.js:44:34)
at makeExecutableSchema (/projectRootPath/node_modules/@nestjs/graphql/node_modules/@graphql-tools/schema/index.js:495:26)
at GraphQLFactory.mergeWithSchema (/projectRootPath/node_modules/@nestjs/graphql/dist/graphql.factory.js:67:68)
at ApolloDriver.start (/projectRootPath/node_modules/@nestjs/apollo/dist/drivers/apollo.driver.js:19:51)
at GraphQLModule.onModuleInit (/projectRootPath/node_modules/@nestjs/graphql/dist/graphql.module.js:104:36)
at callModuleInitHook (/projectRootPath/node_modules/@nestjs/core/hooks/on-module-init.hook.js:51:9)
at NestApplication.callInitHook (/projectRootPath/node_modules/@nestjs/core/nest-application-context.js:178:13)
at NestApplication.init (/projectRootPath/node_modules/@nestjs/core/nest-application.js:96:9)
at NestApplication.listen (/projectRootPath/node_modules/@nestjs/core/nest-application.js:158:33)
07-14-2022 02:29 PM
You could try declaring the directives in the GraphQL typedefs. For example
directive @relationship on FIELD_DEFINITION
07-19-2022 01:17 AM
Thank you!
Let me try to go down that route. I have seen implementations like that but was assuming you should have inside the neo graph ql package, since it looks like you are doing it without nestjs in other places.
07-21-2022 05:17 PM
Hi Minimalist.
Did you figure out how to resolve this issue? I'm stuck in the same place as you 😞
07-27-2022 01:41 AM
Not yet, I am starting this week, I will post here my progress. If you find something new, pls update me as well....
07-27-2022 02:47 AM
Hi Minimalist.
I went down the rabbit hole of adding directives to the schema, but that didn't feel right.
Then found a working example with the below code and that worked. Haven't looked into the details as to why, but it allowed me to continue testing other bits and pieces.
Hope this helps!
BR
import { Neo4jGraphQL, } from '@neo4j/graphql';
import { ApolloDriverConfig, ApolloDriver } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { readFileSync } from 'fs';
import neo4j from 'neo4j-driver';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigService, ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot(),
GraphQLModule.forRootAsync<ApolloDriverConfig>({
imports: [ConfigModule],
inject: [ConfigService],
driver: ApolloDriver,
useFactory: async (configService: ConfigService) => {
const typeDefs = readFileSync('schema/schema.graphql', 'utf-8');
const neo4jUri = configService.get('NEO4J_URI');
const neo4jUser = configService.get('NEO4J_USERNAME');
const neo4jPassword = configService.get('NEO4J_PASSWORD');
const driver = neo4j.driver(
neo4jUri,
neo4j.auth.basic(neo4jUser, neo4jPassword)
);
const neoSchema = new Neo4jGraphQL({ typeDefs, driver });
const schema = await neoSchema.getSchema()
return {
debug: true,
playground: true,
schema: schema
};
},
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
07-27-2022 04:29 AM
Hi Isevme1,
thank you so much for this update. Yes, I see this example here: https://github.com/chenxinhu/nestjs-graphql-neo4j-example
Unfortunately it only works if the TS types are not generated out of schema, but this was actually my motivation. Anyways, I think I will find a different way to generate the typings and update you if I have something new. Are you able to submit a call out of graphql that hits the neo4j with this setup?
08-16-2022 07:05 AM - edited 08-16-2022 07:06 AM
It looks like I found a solution, and its name is ... OGM
With OGM I get my generated types out of the graphQL schema and I can pass a custom resolver to my neo4jgraphql server so I use the custom resolvers and thats it. My mistake was in the assumption that nestjs can do the type generation out of a neo4j specific schema, but the only way to resolve that is to explain custom directives to nestjs, which is too much work and not needed.
To save time to others, here is a full working example, assuming you have a generated graphql schema.
import { Neo4jGraphQL } from '@neo4j/graphql';
import { generate, OGM } from '@neo4j/graphql-ogm';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { readFileSync } from 'fs';
import neo4j from 'neo4j-driver';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ModelMap } from './schema/ogmTypes';
const driver = neo4j.driver(
'neo4j://localhost:7687',
neo4j.auth.basic('neo4j', 'password')
);
// Step 1. generate your schema out of neo4j db, using neo4j driver
// like
// const { toGraphQLTypeDefs } = require("@neo4j/introspector")
// const typeDefs = await toGraphQLTypeDefs(sessionFactory);
// fs.writeFileSync(
// 'apps/be/src/app/schema/generatedSchema.graphql',
// typeDefs
// );
// await driver.close();
//
const SCHEMA = 'apps/be/src/app/schema/schemaMovieDB.graphql';
const generateTypesFromGraphQlSchema = false;
const generatedTypesPath = 'apps/be/src/app/schema/ogmTypes.ts';
@Module({
imports: [
GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
useFactory: async () => {
const typeDefs = readFileSync(SCHEMA, 'utf-8');
// 2. Step, generate TS types according to given schema to use it for custom logic
const ogm = new OGM<ModelMap>({ typeDefs, driver });
if (generateTypesFromGraphQlSchema) {
await generate({
ogm,
outFile: generatedTypesPath,
});
console.log('Types Generated to ', generatedTypesPath);
process.exit(1);
}
await ogm.init();
const resolvers = {
Query: {
yourCustomerResolverName: async (_source, { name }) => {
const extractedType = 'Person';
const SomeCustomType = ogm.model(extractedType);
const companies = await SomeCustomType.find({ where:{name:"SomeName"} });
// const companies = [];
return companies;
},
},
};
// 3. Step bring it all together with graphQL Schema and custom resolvers to configure the final graphQL server
const neo4jGraphQL = new Neo4jGraphQL({ typeDefs, driver, resolvers });
const schema = await neo4jGraphQL.getSchema();
return {
schema,
playground: true,
};
},
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
All the sessions of the conference are now available online