cancel
Showing results for 
Search instead for 
Did you mean: 

Add mandatory server side filtering on the query resolver

pwmb
Node

So I want to write a custom query resolver where user are querying for Projects

GQL type looks something like this

type Projects {
  id: ID!
  name: String!
  users: [User!]! @relation(name: "CAN_ACCESS", direction: IN)
  # ... etc
}

Now user can query this as usual from the exposed graphql endpoint.

But now we want to add this logic that only authorised users (present in users: [User!]!) can only see the results, mind you this is a server side enforced filter and not client side.

Say there are 5 projects in total but Tom has access to only 2 out of those 5 projects he should be able to view only those 2 projects.

How to achieve this ? I wrote a custom Query resolver (with cypher) but still dont see the desired results as I am not sure how to pass filter to neo4jgraphql(...)

Code I have now:

export const QueryProjects = async (root, args, ctx, info) => {
  const results = await ctx.session.readTransaction((t) =>
    t.run(
      `MATCH (:User {id: $id}) -[:CAN_ACCESS]-> (p:Project)
      RETURN p`,
      { id: ctx.user.id }
    )
  )
  return await neo4jgraphql(root, args, ctx, info);

how do I take the filtered results and pass it to neo4jgraphql so that it then return a valid grapgql response?

1 REPLY 1

William_Lyon
Graph Fellow

I think you can accomplish this with a @cypher directive making use of cypherParams in the context object to inject the user id value from the context into the Cypher query. Any values in the cypherParams object in the context will be passed as parameters to the Cypher query.

So, first grab the user object (I'm assuming you're using some auth middleware that adds the user to the req object) and inject it into context.cypherParams:

server = new ApolloServer({
  schema,
  context: ({ req }) => {
    return {driver, cypherParams: { user: req.user}}
  }

Then in the GraphQL schema you can add a @cypher schema directive with your authorization logic that references cypherParams.user.id:

type Query {
  myProjects: [Project] @cypher(statement: "MATCH (u:User {id: $cypherParams.user.id})-[:CAN_ACCESS]->(p:Project) RETURN p"
}

We did something similar in the GRANDcast.fm podcast demo application here: grandcast.fm/schema.graphql at master · johnymontana/grandcast.fm · GitHub

Note that this is using the old neo4j-graphql-js library, you might be interested in the newer official @neo4j/graphql library which has a more powerful authorization model. For this use case there is an @auth directive that supports adding a where rule that can automatically add the authorization rule filter to the generated Cypher query so not even the @cypher schema directive is needed.

Nodes 2022
Nodes
NODES 2022, Neo4j Online Education Summit

All the sessions of the conference are now available online