NestJS Neo4j GraphQL Demo
Turborepo monorepo is used because there are serveral packages that are used in this project. And NestJS insist on using old commonjs modules, while I like to use ESM modules for other packages.
If any typescript in in NestJS project folder cannot be compiled (such as codegen), the entire server won't run even if the server code itself is correct. This is probably because tsc
is used to compile the entire project, and it fails when it encounters a typescript file that cannot be compiled.
So I have to isolate NestJS project from other packages.
- apps/server
- GraphQL server using NestJS and Neo4j
- packages/codegen
- Generate TypeScript types and client code from GraphQL server introspection
Consider installing bun
to run the TypeScript code directly without compiling or worrying about module types.
type Mutation {
signUp(username: String!, password: String!): String
signIn(username: String!, password: String!): String
}
# Only authenticated users can access this type
type Movie @authentication {
title: String
actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN)
}
# Anyone can access this type
type Actor {
name: String
movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
}
# Only authenticated users can access this type
type User @authentication {
id: ID! @id
username: String!
# this is just an example of how to use @authorization to restrict access to a field
# If you list all users without the plaintextPassword field, you will see all users
# If you list all users with the plaintextPassword field, you will only see the user whose id matches the jwt.sub (which is the id of the authenticated user)
# in reality, never store plaintext passwords in the database
plaintextPassword: String!
@authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }])
password: String! @private
}
pnpm install
cd apps/server
cp .env.template .env
# fill in the .env file
# pnpm start
bun --watch src/main.ts # bun works better, if you use nest to run the server, the error message is sometimes wrong.
Open http://localhost:3000/graphql
to access the GraphQL playground.
Use this query to sign up 2 users: user1 and user2
mutation Signup($username: String!, $password: String!) {
signUp(username: $username, password: $password)
}
Set the Authorization
header to the token of user2 returned by the signUp
mutation in the playground.
{
"Authorization": "Bearer <token>"
}
Now use the following query to get all users:
query Users {
users {
id
username
}
}
You should see both user1 and user2
but if you add plaintextPassword field to the query, you will see that only user2 is returned
query Users {
users {
id
username
plaintextPassword
}
}
This is because of the @authorization
directive on the plaintextPassword
field in the schema.
Most queries and mutations are auto-generated by neo4j's library, but we defined 2 custom mutations: signUp
and signIn
, which required custom resolvers.
Providing nestjs resolver class won't work.
export const neoSchema = new Neo4jGraphQL({
typeDefs: typeDefs,
driver: neo4jDriver,
resolvers: authResolvers,
features: {
authorization: {
key: "huakun",
},
},
});
The resolver must be provided to Neo4jGraphQL constructor. It must be an object, so NestJS's class-based resolver module won't work.
You must provide regular apollo stype resolvers.
export const authResolvers = {
Mutation: {
signUp: async (_source, { username, password }) => {
...
return createJWT({ sub: users[0].id });
},
signIn: async (_source, { username, password }) => {
...
return createJWT({ sub: user.id });
},
},
};
See these files for more details:
- ./apps/server/src/graphql/graphql.module.ts,
- ./apps/server/src/utils/neo4j.ts
- ./apps/server/src/utils/graphql.ts
- ./apps/server/src/utils/auth.resolver.t
https://the-guild.dev/graphql/codegen is used to generate TypeScript types and more from the GraphQL schema.
Usually you provide the graphql schema file, but in this demo, the schema is designed for neo4j and not recognized by the codegen tool.
You need to let Neo4jGraphQL
generate the schema and deploy it to a server first, then provide the server's endpoint to the codegen tool.
Then the codegen tool will introspect the schema from the server and generate the types.
Make sure the server is running before running the codegen
cd packages/codegen
pnpm codegen
The generated files are in the packages/codegen/src/gql
folder.
Sample operations can be added to packages/codegen/operations
. Types and caller for operations will also be generated.
Read the documentation of codegen for more details.