AWS AppSync GraphQL subscriptions returning null values on event - graphql

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.

Related

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!
}

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?

How to resolve subselections / relations in prisma (nested lists)

Let's take an example from the github repo of prisma:
We have a user, the user could have multiple posts, and one post could have multiple links.
My goal is, to retrieve all posts and all links.
This means, my response is a list (links) in a list (posts).
I want to map the values I get back as two nested lists.
datamodel.prisma
type User {
id: ID! #id
email: String! #unique
name: String
posts: [Post]!
}
type Post {
id: ID! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
published: Boolean! #default(value: false)
title: String!
content: String
author: User!
links: [Link]!
}
type Link {
id: ID! #id
url: String
title: String
post: Post!
}
schema.graphql
type Query {
...
}
type Mutation {
...
}
type Link {
id: ID!
url: String
title: String
post: Post!
}
type Post {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
published: Boolean!
title: String!
content: String
author: User!
}
type User {
id: ID!
email: String!
name: String
posts: [Post]!
}
I want to query all posts of a user, and all of the links for every post in the response.
How would I query this request?
user {
id
posts {
id
links {
id
}
}
}
The above code snipper would not work.
EDIT
I want to use the following:
User: {
listPosts: (parent, args, context, info) {
return context.prisma.posts().links()
}
}
So in my response (data in front-end via react-apollo Query Component), I want to map over posts AND the links in each post.
BUT the links attribute in posts is null.
Is there another way to achieve this?!
According to the docs:
Prisma client has a fluent API to query relations in your database. Meaning you can simply chain your method calls to navigate the relation properties of the returned records. This is only possible when retrieving single records, not for lists. Meaning you can not query relation fields of records that are returned in a list.
In order to get around that limitation, you can use the $fragment method:
const fragment = `
fragment UserWithPostsAndLinks on User {
id
email
name
posts {
id
title
content
links {
id
url
title
}
}
}
`
const userWithPostsAndLinks = await prisma.user({ id: args.id }).$fragment(fragment)

Cannot query field 'password' on type 'User' 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

Features work, but console delivers extraneous error: "Variable "$id" of required type "ID!" was not provided."

My CRUD operations work. But many of them also generate a single, specific console error I haven't been able to track down.
[GraphQL error]: Message: Variable "$id" of required type "ID!" was not provided., Location: [object Object], Path: undefined
Example: I can view a list of users without generating an error, and I can access the "details" view of a single user without generating an error. Those two work fine in isolation. But somehow, traversing from the former to the latter generates the error.
I tested both queries (users and user) in GraphQL-Playground, and did not receive errors. I also tried the IDs as type "String" rather than type "ID!". (Also without the !). I experimented with other "hacky" ways of feeding the user id to the resolver. And I've tried passing in other fields in lieu of the "info" argument in the query resolver.
To reiterate, Viewing either page in isolation (ie: clearing the console and reloading either page) does NOT trigger the error. It seems to be something about the journey from /users to user?id=12345.
from datamodel.prisma:
type User {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
name: String!
email: String! #unique
password: String!
resetToken: String
resetTokenExpiry: Float
permissions: Permission
posts: [Post]! #relation(name: "PostsOfUser" onDelete: CASCADE)
comments: [Comment!]! #relation(name: "CommentsOfUser" onDelete:
CASCADE)
from query object in schema.graphql:
user(id: ID!): User
user query resolver:
async user(parent, { id }, { db, request }, info) {
const user = await db.query.user({ where: { id } }, info);
return user;
gql query using react-apollo and graphql-tag packages:
const SINGLE_USER_QUERY = gql`
query SINGLE_USER_QUERY($id: ID!){
user(id: $id) {
id
name
email
permissions
createdAt
updatedAt
}
}
`;
Expected behavior: User can click on a single user from a list of users, and the following page will contain details about that user.
Actual results: It works, but also generates console errors:
[GraphQL error]: Message: Variable "$id" of required type "ID!" was
not provided., Location: [object Object], Path: undefined
index.js:86
[Network error]: ServerError: Response not successful: Received status code 400
index.js:2178 GraphQL error occurred [getDataFromTree] Error:
Network error: Response not successful: Received status code 400
at new ApolloError (ApolloError.js:37)
at QueryManager.js:328
at QueryManager.js:700
at Array.forEach (<anonymous>)
at QueryManager.js:699
at Map.forEach (<anonymous>)
at QueryManager.push../node_modules/apollo-client/core/QueryManager.js.QueryManager.broadcastQueries (QueryManager.js:694)
at QueryManager.js:277
Please add an if condition for the parameter and check whether it is true. Then add a get(REST API) or query(GrahQL) function inside it. Then it won't show this error. Please see the below example
async user(parent, { id }, { db, request }, info) {
if ( id ) {
//enter your code
const user = await db.query.user({ where: { id } }, info);
return user;
}
}

Resources