amplify-js
amplify-js copied to clipboard
Datastore observeQuery not recieving remote updates on some models after initial sync
Before opening, please confirm:
- [X] I have installed the latest version of the Amplify CLI (see above), and confirmed that the issue still persists.
- [X] I have searched for duplicate or closed issues.
- [X] I have read the guide for submitting bug reports.
- [X] I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
- [X] I have removed any sensitive information from my code snippets and submission.
How did you install the Amplify CLI?
No response
If applicable, what version of Node.js are you using?
No response
Amplify CLI Version
9.2.1
What operating system are you using?
Ubunutu Mate
Did you make any manual changes to the cloud resources managed by Amplify? Please describe the changes made.
No manual changes made.
Amplify Categories
api
Amplify Commands
Not applicable
Describe the bug
I use observeQuery for several models in my application so I can display any changes immediately to the user without them having to reload. This is working fine for changes that are happening locally for the current user using the app, for example they make a change to a record via a form on the app and the observeQuery subscription immediately sees the changes.
However when a remote change happens then the observeQuery subscription does not pick up the change immediately and the user would need to hard refresh their browser or log out and back in to pick up the change. Examples of this kind of change could be a record being updated via a lambda function, or going into Amplify Studio and making a change, or another user using the app changes a public record then that user would see the change right away but others would need to hard refresh.
The strange thing here is one of my models DOES process remote changes as expected but as far as I can tell none of my other ones do. I've posted my schema.graphql below, the model that does work is Profile
, an example of one that doesn't work is Score
Expected behavior
observeQuery should always detect remote changes to the subscribed model
Reproduction steps
This Works Correctly
- create an observeQuery subscription to the
Profile
model and log to the console any time there is a change
const subscription = DataStore.observeQuery(Profile).subscribe(snapshot => {
const { isSynced, items } = snapshot;
isSynced && console.log(items);
});
- Go into Amplify Studio and create a new Profile record or modify an existing one, you will see the log coming up in the console correctly.
This Does Not Work (Same code, different model)
- create an observeQuery subscription to the
Score
model and log to the console any time there is a change
const subscription = DataStore.observeQuery(Score).subscribe(snapshot => {
const { isSynced, items } = snapshot;
isSynced && console.log(items);
});
- Go into Amplify Studio and create a new Score record or modify an existing one, you will see not see any logs for any remote changes. You will only see logs for local changes.
GraphQL schema(s)
# Put schemas below this line
# This "input" configures a global authorization rule to enable public access to
# all models in this schema. Learn more about authorization rules here: https://docs.amplify.aws/cli/graphql/authorization-rules
#input AMPLIFY { globalAuthRule: AuthRule = { allow: public } } # FOR TESTING ONLY!
# Naming rules follows:
# https://graphql-rules.com/rules/naming
# AppSync Scalar Type def:
# https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html
# Misc
# Subscription type not available - https://github.com/aws-amplify/amplify-cli/issues/5554
# ** For visible, add all users to group members, and add member to groups attribute when we want to make visible
type Score @model @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]},
{allow: groups, groupsField: "groups", operations: [read]}
]) {
id: ID!
groups: [String]
name: String!
profile: Profile! @belongsTo
derivesFromId: ID @index(name: "byDerivesFromId", queryField: "scoreByDerivesFromId")
scoreData: AWSJSON
publicScore: PublicScore @hasOne
groupScore: GroupScore @hasOne
editable: Boolean!
visible: Boolean!
shareable: Boolean!
community: Boolean
completed: Boolean
groupVisible: Boolean @default(value: "false")
#profileId: ID @index(name: "byProfile", queryField: "scoreByProfileId")
}
type PublicScore @model @auth(rules:[
{allow: public, provider: iam, operations: [read]},
{allow: owner, operations: [read, create, update, delete]},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]},
{allow: groups, groupsField: "groups", operations: [read]}
]) {
id: ID!
groups: [String]
name: String!
derivesFromId: ID @index(name: "byDerivesFromId", queryField: "publicScoreByDerivesFromId")
score: Score @belongsTo
scoreId: ID! @index(name: "publicScoreByScoreId", queryField: "listPublicScoresByScoreId")
scoreData: AWSJSON
editable: Boolean!
visible: Boolean!
}
type CommunityScore @model @searchable @auth(rules:[
{allow: private, provider: iam, operations: [read]},
{allow: owner, operations: [read, create, update, delete]},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]}
]) {
id: ID!
groups: [String]
name: String!
scoreId: ID!
profileId: ID!
profileName: String!
scoreData: AWSJSON
editable: Boolean!
visible: Boolean!
featured: Boolean!
sortProfileName: String
sortName: String
sortTimestamp: AWSTimestamp
}
type GroupScore @model @searchable @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]}
{allow: groups, groupsField: "groups", operations: [read]},
{allow: private, provider: iam, operations: [read, update]}
]) {
id: ID!
groups: [String]
name: String!
derivesFromId: ID @index(name: "byDerivesFromId", queryField: "groupScoreByDerivesFromId")
score: Score @belongsTo
scoreId: ID!
profileId: ID
profileName: String
scoreData: AWSJSON
editable: Boolean!
visible: Boolean!
featured: Boolean!
completed: Boolean @default(value: "false")
groupVisible: Boolean @default(value: "false")
class: Class @belongsTo
archived: Boolean @default(value: "false")
}
type Profile @model @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: private, operations: [read, update], provider: iam},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]}
]) {
id: ID!
groups: [String] # e.g. class101_admin (teacher group)
name: String! @index(name: "profileByName", queryField: "listProfilesByName")
capabilities: AWSJSON
isDefault: Boolean
archived: Boolean @default(value: "false")
class: Class @belongsTo
account: Account @belongsTo
user: User @belongsTo
scores: [Score] @hasMany
owner: ID
}
type Class @model @searchable @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: private, operations: [read, update], provider: iam},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]},
{allow: groups, groupsField: "groups", operations: [read]}
]) {
id: ID!
groups: [String] # e.g. class101_admin (teacher group)
name: String!
account: Account @belongsTo
profiles: [Profile] @hasMany
groupScores: [GroupScore] @hasMany
studentInvitations: [StudentInvitation] @hasMany
archived: Boolean
organization: Organization @belongsTo
owner: ID
}
type EducatorInvitation @model @searchable @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: public, provider: iam, operations: [read]},
{allow: private, operations: [read, update], provider: iam},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]},
{allow: groups, groupsField: "groups", operations: [read]}
]) {
id: ID!
groups: [String]
firstName: String!
lastName: String!
email: String!
accepted: Boolean!
organization: Organization! @belongsTo
educatorUserId: String
lastResend: AWSTimestamp
}
type StudentInvitation @model @searchable @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: public, provider: iam, operations: [read]},
{allow: private, operations: [read, update, delete], provider: iam},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]},
{allow: groups, groupsField: "groups", operations: [read]}
]) {
id: ID!
groups: [String]
firstName: String!
lastName: String!
email: String!
class: Class! @belongsTo
accepted: Boolean!
studentUserId: String
lastResend: AWSTimestamp
owner: ID
}
type User @model @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]}
]) {
id: ID!
email: String # email address
groups: [String] # e.g. class101_admin (teacher group)
name: String
profiles: [Profile] @hasMany
account: Account! @belongsTo
#userprofiles: [Profile] @hasMany(indexName: "byUser", fields: ["id"])
}
enum AccountType {
INDIVIDUAL,
STUDENT,
EDUCATOR,
EDUCATOR_ADMIN,
EVENT,
SUPPORT,
ADMIN
}
type Account @model @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: private, operations: [read, update], provider: iam},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]},
{allow: groups, groupsField: "groups", operations: [read, create, update, delete]}
]) {
id: ID!
groups: [String] # e.g. class101_admin (teacher group)
accountType: AccountType!
master: Account @hasOne
user: User @hasOne
profiles: [Profile] @hasMany
classes: [Class] @hasMany
plan: [Plan] @hasMany
owner: ID
}
type Organization @model @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]},
{allow: groups, groupsField: "groups", operations: [read]},
{allow: public, operations: [read], provider: iam},
{allow: private, operations: [read, update], provider: iam}
]) {
id: ID!
groups: [String]
capabilities: AWSJSON!
email: String!
name: String!
seatLimit: Int!
educatorInvitations: [EducatorInvitation] @hasMany
classes: [Class] @hasMany
accepted: Boolean @default(value: "false")
}
type Transaction @model @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]}
]) {
id: ID!
groups: [String]
plan: Plan @belongsTo
stripeSessionId: String
stripeCustomerId: String
stripeSubscriptionId: String @index(name: "transactionByStripeSubscriptionId", queryField: "listTransactionsByStripeSubscriptionId")
}
type Plan @model @auth(rules:[
{allow: owner, operations: [read, create, update, delete]},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]}
]) {
id: ID!
groups: [String]
transaction: Transaction @hasOne
account: Account! @belongsTo
offering: Offering! @belongsTo
startDate: AWSDate!
endDate: AWSTimestamp
active: Boolean!
owner: ID
}
# plans[] should be safe since plan doesn't allow private/public access
type Offering @model @auth(rules:[
{allow: public, provider: iam, operations: [read]},
{allow: private, operations: [read]},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]}
]) {
id: ID!
groups: [String]
name: String!
description: String
plans: [Plan] @hasMany
durationDays: Int
price: String
isDefault: Boolean
enabled: Boolean!
visible: Boolean!
contentUrl: AWSURL
iframeContentUrl: AWSURL
capabilities: AWSJSON
retiredDate: AWSDate
scoreLimit: Int
seatLimit: Int
schoolLimit: Int
stripePriceId: String
}
type Content @model @auth(rules:[
{allow: public, provider: iam, operations: [read]},
{allow: private, operations: [read]},
{allow: owner, operations: [read, create, update, delete]},
{allow: groups, groups: ["Admin"], operations: [read, create, update, delete]}
]) {
id: ID!
groups: [String]
key: String!
value: String
description: String
}
Log output
No response
Additional information
No response
Hi @Jesmaster 👋 I would test any of your models that are using a dynamic group authorization rule and those that are only using static groups. There is a known limitation for subscriptions with dynamic group authorization.
For more info: https://docs.amplify.aws/cli/graphql/authorization-rules/#user-group-based-data-access
It might also be worth checking what auth mode DataStore is subscribing to the model with? You may be able to find it if you check the Network Activity tab for something like this:

Then, in the Payload tab there should be a Header that suggests what kind of authMode was used to subscribe, in my case it was an API_KEY.

Lastly, are there any errors in the console that suggest the user was not authorized to subscribe or receive any data when creating/updating a record remotely?
Hi 👋 Closing this as we have not heard back from you. If you are still experiencing this issue and in need of assistance, please feel free to comment and provide us with any information previously requested by our team members so we can re-open this issue and be better able to assist you.
Thank you!