Let's say I have a User type that contains the following fields:
type User {
name: String
username: String
someOtherField1: String
someOtherField2: String
someOtherField3: String
someOtherField4: String
creditCardNumber: String
}
If I am querying for myself, it's okay to return all fields, because the information is mine. So returning creditCardNumber
to the client is no biggie. But if I am querying for someone else, I should only be able to access the public info on the returned user. Returning creditCardNumber
would be terrible. And even if I don't code the query on the client to do so, what would prevent a malicious user from digging into the code, updating the client-side query to include creditCardNumber
, and executing it?
What is the best way to achieve this level of field restriction across queries in GraphQL? My only thought on this so far is to create a separate UserSearch
type, i.e.
type UserSearch {
name: String
username: String
someOtherField1: String
someOtherField2: String
someOtherField3: String
someOtherField4: String
}
which excludes the private fields, however this does not feel DRY as you'd be creating many types that are 90% similar in structure form each other.
Is there a cleaner way to implement this, that doesn't create unnecessary types or duplicate fields?
By default, GraphQL types are maybe types, which means you can return null to any field.
Which means, inside the resolve
function of your creditCardNumber
field, you could check if the current user is the user being fetched, and if so, you return the number. Otherwise, you return null.
To access "contexted" data such as the current user, GraphQL let you pass an object context
when executing the graphql function:
graphql(schema, query, rootValue, context) // <-- the last parameter
And you will find this object in each field resolve
function signature:
...
resolve: (object, args, context) => {
// Third argument is your object
},
....
So what you can do, is pass the current logged user in the context:
graphql(schema, query, rootValue, { user: getCurrentLoggedUser() })
And in your User
type, inside the creditCardNumber
field resolve function:
creditCardNumber: {
...
resolve: (user, args, context) => {
if (user.id === context.user.id)
return user.creditCardNumber;
return null;
},
...
}
If you are using graphql-express
, the context is by default the request, and you can customize it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With