GithubHelp home page GithubHelp logo

Comments (10)

maticzav avatar maticzav commented on April 27, 2024 9

Perfect, thank you so much for this input. 🎉

So, the reason you are experiencing this issue has to do with how GraphQL processes responses. I'll use SDL to present the idea, but I believe you should have no problem translating this into your schema as well.

GraphQL uses strongly typed language. That means that there's a particular "contract" between client and server which makes sure no result is unpredictable. My thinking is this; if I am querying type book by its id and want to access id, name and content field which are all non-nullable I rely on the fact that every single field in a book instance will have value once I get the result. This way, I don't have to write checks on the frontend to make sure every field, in fact, has a value.

Imagine a situation where one of the fields throws an error. In such a scenario, GraphQL can no longer ensure that all fields in Book will have a value. Hence, to keep the contract trustworthy, it has to return null.

GraphQL Shield works on a similar pattern; If the user has no access to a particular field, we throw an error. Makes sense, doesn't it? In your specific case, the problem is that secret is non-nullable field. Taking into consideration the above section, we can easily understand why in case the customer is not authenticated, user returns null. If it weren't so, GraphQL would break the contract.

My vague remodelling of your schema looks something like this;

type Query {
  node: User!
}

type User {
  name: String!
  ...
  secret: String!
}

Now to fix the error, you've probably guessed by now, you have to change non-null secret to nullable secret.

I hope this helps you solve the problem! 🙂

A quick advice; thinking of schema as a contract between your client and server could change the way you perceive it. For the field to be non-nullable, you have to ensure that no matter what, the client can receive it.

PS.: Check out node Query field. Can you ensure that there will always be a user no matter the id?

from graphql-shield.

AlessandroFerrariFood avatar AlessandroFerrariFood commented on April 27, 2024 3

You are right!
I missed the not-null.
Changing solved it.

Thank you very much!

PS: node Query field called with wrong id returns a not found graphql error

from graphql-shield.

AlessandroFerrariFood avatar AlessandroFerrariFood commented on April 27, 2024 1

I have a big proprietary application I can't share but I assembled a strongly reduced but complete version to test the issue on.
Here is its schema
schema.txt
Permission are
const permissions = shield({ User: { firstName: allow, lastName: allow, secret: deny } });
Following query
{ currentUser { name } }
correctly give
{ "data": { "currentUser": { "name": "Paolo Rossi" } } }

Following query
{ currentUser { name secret } }
wrongly give
{ "data": { "currentUser": null }, "errors": [ { "message": "Not Authorised!", "locations": [ { "line": 5, "column": 5 } ], "path": [ "currentUser", "secret" ] } ] }
but I expected
{ "data": { "currentUser": { "name": "Paolo Rossi" } }, "errors": [ { "message": "Not Authorised!", "locations": [ { "line": 5, "column": 5 } ], "path": [ "currentUser", "secret" ] } ] }
or similar.

from graphql-shield.

maticzav avatar maticzav commented on April 27, 2024

Hey @AlessandroFerrariFood 👋

Could you share the chunk of your schema that is being affected? 🙂

from graphql-shield.

mkaradeniz avatar mkaradeniz commented on April 27, 2024

Hey,

I have a related issue to this. Let's say my schema looks like this:

type Resource {
  id: ID!
  createdBy: User
}

type User {
  id: ID!
  name: String!
}

I now have a field rule on Resource to deny unauthenticated users from querying createdBy.

When I now query Resource unauthenticated like this:

{
  resources {
    id
    createdBy {
      id
      name
    }
  }
}

I would have expected to get this result:

{
  "data": {
    "resources": [
      {
        "id": 1,
        "createdBy": null
      },
      {
        "id": 2,
        "createdBy": null
      }
    ]
  }
}

But I get this:

{
  "error": {
    "errors": [
      {
        "message": "Not Authorised!",
        "locations": [
          {
            "line": 4,
            "column": 5
          }
        ],
        "path": [
          "resources",
          0,
          "createdBy"
        ]
      },
      {
        "message": "Not Authorised!",
        "locations": [
          {
            "line": 4,
            "column": 5
          }
        ],
        "path": [
          "resources",
          1,
          "createdBy"
        ]
      }
    ],
    "data": {
      "resources": [
        {
          "id": 1,
          "createdBy": null
        },
        {
          "id": 2,
          "createdBy": null
        }
      ]
    }
  }
}

Is there a way to suppress this error so it just returns null for createdBy and be done with it?

A bit more of context: In the app I'm working on I can't know which permission the user has, so I want to still be able to write the queries oblivious to this and just return null to fields the user isn't allowed to query (as long as the fields are nullable of course) so I can handle it in the frontend. Is this something I can actually achieve with graphql-shield?

from graphql-shield.

maticzav avatar maticzav commented on April 27, 2024

Hey, let's see what's going on!

tldr; I think the "problem" is on the frontend because it defaults to error and doesn't consume the data below

So, I think there are some settings regarding how client handles errors. What I wanted to ask before is how does the data in the error case relate to your schema (the rugs part).

from graphql-shield.

mkaradeniz avatar mkaradeniz commented on April 27, 2024

Oh gosh, sorry. I wanted to edit the code I posted so it is more clear, but I forgot to edit the error part. I edited my original post.

The frontend itself isn't built yet, this is all from the playground.

I think it all boils down to this: If someone queries a nullable field without permission to access that field, I simply want to return null instead of throwing an error.

from graphql-shield.

maticzav avatar maticzav commented on April 27, 2024

I think it all boils down to this: If someone queries a nullable field without permission to access that field, I simply want to return null instead of throwing an error.

It makes sense, no worries! 🙂

I mean, the easiest way to do that is to make fields nullable, as you mentioned. It's not possible to not throw a permission error, but that shouldn't be a problem, in my opinion.

Why's ignoring the error, and only collecting the data, not an option?

from graphql-shield.

mkaradeniz avatar mkaradeniz commented on April 27, 2024

Why's ignoring the error, and only collecting the data, not an option?

The problem I was facing is, that (when using nexus framework) errors were returning 500 status codes. With this hack graphql-nexus/nexus#761 (comment) I was able to change it. Now it does return an errors array but also the data so I should be able to ignore it later

So I guess it was more of a nexus issue than a graphql-shield one, though I would've preferred to not throw an error but I get that this is probably not intended.

Thanks so much :)

from graphql-shield.

fforres avatar fforres commented on April 27, 2024

In case other people are looking for the same, link that @mkaradeniz is not existing any more (Maybe this one was the same?)
graphql-nexus/nexus#383 (comment)

For context, what we want to do, is format the response (in this case in apollo) so it will include the data also :)

const server = new ApolloServer({
  // more config
  formatResponse: (response, { operationName }) => {
    if (response && operationName) {
      return {
        ...response,
        data: response.data || {
          [operationName]: null,
        },
      }
    }

    return {
      data: null,
    }
  },
})

from graphql-shield.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.