Cannot query field 'password' on type 'User' graphql - graphql

I'm using graphql and prisma.
datamodel.prisma
type User {
id: ID! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
email: String! #unique
password: String!
first_name: String
}
schema.graphql
scalar Date
type Query {
users: [User!]!
}
type User {
id: ID!
createdAt: Date!
updatedAt: Date!
email: String!
first_name: String
}
resolver
users: (parent, args, context) => {
return context.prisma.users();
}
I expected to get a user list, but received the error:
query
{
users {
email
}
}
error
"Cannot query field 'password' on type 'User'. (line 7, column 5):\n password\n ^"
UPDATE 1
Tried to use a fragment, but got the same:
{
users {
...userFields
}
}
fragment userFields on User {
email
}

I'd like to also add a scenario that can very easily cause this same issue that took me a while to debug and I'm sure others will encounter, because it took me quite some time to realize the issue was actually being caused in my FRONTEND code where I was defining my auth-related Mutations.
Set Up
Here's what that looked like while developing much of my application:
datamodel.prisma (I've omitted some fields for simplicity sake)
type User {
id: ID! #id
name: String!
email: String! #unique
password: String!
}
schema.graphql (just showing the signUp Mutation for simplicity)
type Mutation {
signUp(email: String!, password: String!, name: String!): User!
}
SignUp.js (where I access the signUp Mutation exposed in schema.graphql)
const SIGNUP_MUTATION = gql`
mutation SIGNUP_MUTATION(
$email: String!
$name: String!
$password: String!
) {
signUp(email: $email, name: $name, password: $password) {
id
email
name
password
}
}
`
Notice that I am returning id, email, name, and password - this was because I wanted to make sure everything was working in development.
Introducing the Cannot query field 'password' on type 'User' error
Once I began working on security and created a special User type in schema.graphql so that I could hide protected fields such as password, that's when I got this issue:
schema.graphql (notice that I am now not exposing the password field on this frontend-facing User type)
type Mutation {
signUp(email: String!, password: String!, name: String!): User!
}
type User {
id: ID!
name: String!
email: String!
}
Solution
Because of the nature of this error message, I spent most of my morning puzzling over my backend code. But it turned out that the error was actually being caused in SignUp.js, where I was RETURNING the password field.
The solution was to simply remove that line from the list of return fields like so:
const SIGNUP_MUTATION = gql`
mutation SIGNUP_MUTATION(
$email: String!
$name: String!
$password: String!
) {
signUp(email: $email, name: $name, password: $password) {
id
email
name
}
}
`
Key Lessons
So if you're experiencing this issue, please check ALL of your relevant mutations and make sure that you're not returning any fields that you have protected as I described here.
Be sure to also check your frontend code and make sure you aren't trying to return fields that you have now protected and are no longer exposing to the frontend.
I hope this is helpful and saves people some time!

... aaah Prisma ...
I don't know if interfaces, unions or input types are supported. Graphql docs
Prisma generates almost everything ... but defining password as required (as type for DBB generation) for datamodel should not block querying for a type subset or type defined on existing model without using all fields.
For me it's a bit missleading error message. It can be resolver related.
Try to match types in resolver, don't return direct prisma query (operates on model types), but map queried data (an array) to filter out password field/property (to be query type compatible). It's a security concern, too - passwords shouldn't be read from outside.

I've created custom query which return a fragment and seems the error gone.

Just run in your console(in prisma folder):
PRISMA_MANAGEMENT_API_SECRET=mysecret42 prisma deploy

Related

AWS AppSync GraphQL subscriptions returning null values on event

New to GraphQL, Amplify, AppSync, etc, and running into an issue when attempting to subscribe to an onUpdate event.
I added the 'API' library to my Amplify project with authentication through an API key. I can successfully send a mutation (updatePurchaseOrder) request through Postman, and the subscription listener registers an update, but the data returned is null on everything besides the id of the updated record.
screenshot in the AppSync console
The status field is null here, I would expect to see the new updated value. Is that the expected behavior?
The defined type on this:
type PurchaseOrder #model #auth(rules: [ { allow: public } ] ){
id: ID!
name: String!
description: String!
user: String
time: String
file: String!
status: String
}
Schema mutations and subscriptions created from the initial type definition haven't been changed:
type PurchaseOrder {
id: ID!
name: String!
description: String!
user: String
time: String
file: String!
status: String
createdAt: AWSDateTime!
updatedAt: AWSDateTime!
}
type Mutation {
updatePurchaseOrder(input: UpdatePurchaseOrderInput!, condition: ModelPurchaseOrderConditionInput): PurchaseOrder
}
input UpdatePurchaseOrderInput {
id: ID!
name: String
description: String
user: String
time: String
file: String
status: String
}
type Subscription {
onUpdatePurchaseOrder: PurchaseOrder
#aws_subscribe(mutations: ["updatePurchaseOrder"])
}
I tried logging to console in browser and see the same empty object returned. Figured listening through the AppSyncConsole would be less error prone, but I'm still seeing the same result.
Figured out where I went wrong... I needed to specify the response fields I wanted in the POST query (eg.:
mutation updatePurchaseOrder {
updatePurchaseOrder(input: {status: "Pending", id: "53a98236-7b64-428c-93ea-2fae5228c0ef"}) {
id
**status**
}
)
'status' was not in the query response parameters originally. The subscription response will only include fields that are part of the mutation response in the query itself.

How to resolve Inconsistent __typename error in Relay?

I just tried to implement the Relay in Frontend for this graphql tutorial, In that tutorial, they created graphql server to store URL(Link) bookmarks with the User who posted those URLs.
The relationship between the link and the users is:
Link belongs_to :user,
User has_many :links.
And I listed out all the Links with Users in Frontend, at the time I got the below error.
Warning: RelayResponseNormalizer: Invalid record 1. Expected __typename to be consistent, but the record was assigned conflicting types Link and User. The GraphQL server likely violated the globally unique id requirement by returning the same id for different objects
I'm not aware of how much it will impact the application. because I got the expected result from Frontend.
Frontend View of Query.
I read this relay official blog for this kind of error, but there is no example to know how exactly to resolve this. so can someone help to resolve this?
Relay Query
graphql`
query LinkListQuery {
allLinks {
id,
description,
url,
postedBy {
id,
name
}
}
}`
Schema:
input AUTH_PROVIDER_CREDENTIALS {
email: String!
password: String!
}
input AuthProviderSignupData {
credentials: AUTH_PROVIDER_CREDENTIALS
}
type Link implements Node {
description: String!
id: ID!
postedBy: User
url: String!
votes: [Vote!]!
}
input LinkFilter {
OR: [LinkFilter!]
descriptionContains: String
urlContains: String
}
type Mutation {
createLink(description: String!, url: String!): Link!
createUser(name: String!, authProvider: AuthProviderSignupData): User!
createVote(linkId: ID): Vote!
signinUser(credentials: AUTH_PROVIDER_CREDENTIALS): SignInUserPayload
}
"""An object with an ID."""
interface Node {
"""ID of the object."""
id: ID!
}
type Query {
allLinks(filter: LinkFilter, first: Int, skip: Int): [Link]!
"""Fetches an object given its ID."""
node(
"""ID of the object."""
id: ID!
): Node
}
"""Autogenerated return type of SignInUser"""
type SignInUserPayload {
token: String
user: User
}
type User implements Node {
email: String!
id: ID!
links: [Link!]!
name: String!
votes: [Vote!]!
}
type Vote {
id: ID!
link: Link!
user: User!
}

Hasura GraphQL Type Error Inconsistent Object

I'm trying to add the User object to my AuthPayload in Hasura Actions. I'm new to GraphQL and Hasura, not sure what I'm doing wrong. Here's the error I'm getting in the Hasura console.
Inconsistent object: validation for the given custom types failed
because the type "User" of the field "user" in the object type
"AuthPayload" is object type which isn't allowed.
Here is what I have...
Action definition
type Mutation {
login(
email: String!
password: String!
): AuthPayload
}
New types definition
type AuthPayload {
token: String
user: User
}
type User {
id: uuid!
email: String!
role: String!
}
The error message clearly indicates that fields in AuthPayload could not be of complex types (objects).
So the fix would be:
type AuthPayload {
token : String
user_id : uuid!
email: String!
role: String!
}
put all fields of type User inside AuthPayload.

How do you expose different subsets of a GraphQL type to different users?

I have a User type with many fields on it. I want to expose different fields on it depending on who is querying information about the User. What is a good way to organize this without having many many different types each representing a slightly different view of a user? Here is an example with 4 different types representing different views of the same user. Is there a better way to organize this?
Of course I can make all the fields nullable but that doesn't seem helpful to the developer querying the data.
type UserForSelf {
id: ID!
username: String!
avatarUrl: String!
email: String!
mailingAddress: Address!
team: Team!
lastLogin: DateTime!
}
type UserForPublic {
id: ID!
username: String!
avatarUrl: String!
}
type UserForAdmin {
id: ID!
username: String!
avatarUrl: String!
email: String!
team: Team!
lastLogin: DateTime!
}
type UserForTeamMember {
id: ID!
username: String!
avatarUrl: String!
email: String!
team: Team!
}
You should consider using Schema Directives for this use case.
That basically allows you to only resolve some specific field if the user has permission for it. Otherwise you can return null or throw an error.
So in the end you would have a single type User like this:
directive #hasRole(role: String) on FIELD_DEFINITION
type User {
id: ID!
username: String!
avatarUrl: String!
email: String! #hasRole(role: "USER")
mailingAddress: Address! #hasRole(role: "USER")
team: Team! #hasRole(role: "USER")
lastLogin: DateTime! #hasRole(role: "USER")
}
Then you can have a directive resolver kinda like this:
const directiveResolvers = {
...,
hasRole: (next, source, {role}, ctx) => {
const user = getUser()
if (role === user.role) return next();
throw new Error(`Must have role: ${role}, you have role: ${user.role}`)
},
...
}
If you have a field that only ADMIN can query, you would just use the #hasRole(role: "USER") directive.
Then your service layer (or your resolver if you don't have a service layer) would be responsible to define which User to fetch (if your own user or some user based on ID as long as you have permission).
You can use directives for a lot of different use cases. Here are a few good references:
https://www.prisma.io/blog/graphql-directive-permissions-authorization-made-easy-54c076b5368e
https://www.apollographql.com/docs/apollo-server/schema/directives/

How to properly append to an array using Dgraph GraphQL API?

I'm trying to spin up Dgraph, but appears as though to add a node to an array of nodes using the GraphQL api requires an unnecessary amount of work if I understand it correctly:
I have the following simplified schema:
type User #secret(field: "password") {
account: String! #id
email: String! #search(by: [hash])
extension: String! #search(by: [hash])
phone: String! #search(by: [hash])
hasCreated: [Transaction]! #hasInverse(field: from)
hasReceived: [Transaction]! #hasInverse(field: to)
}
type Transaction {
id: ID!
type: TransactionType! #search(by:[terms])
amount: Float!
assetCode: String! #search(by:[terms])
to: User!
from: User!
initiatedAt: DateTime! #search(by:[hash])
completedAt: DateTime #search(by:[hash])
status: Status!
}
To me, it appears as though to add a node to the User's hasCreated or hasReceived fields would require me to pull the entire array, append a new Transaction to the array and then use an updateUser mutation to complete the update. But the updateUser mutation would require me to get all the Transactions and all Users attached to these transactions and so on.
Example trying to retrieve an entire user object:
query {
getUser(id:"%s"){
account
email
extension
phone
hasCreated {
id
type
amount
assetCode
to {
...
}
}
hasReceived {
id
type
amount
assetCode
to {
...
}
}
}
}
}
Is there another way to append to arrays or update objects using the GraphQL api without having to retrieve whole objects?

Resources