Skip to content

HuakunShen/nestjs-neo4j-graphql-demo

Repository files navigation

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.

Folder Structure

  • apps/server
    • GraphQL server using NestJS and Neo4j
  • packages/codegen
    • Generate TypeScript types and client code from GraphQL server introspection

Prerequisites

Consider installing bun to run the TypeScript code directly without compiling or worrying about module types.

GraphQL Schema

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
}

Server

Run Server

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.

Usage

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.

Resolver

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:

Codegen

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.

About

NestJS Neo4j GraphQL Demo

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published