Closed chris-lee-code closed 3 years ago
@chrisbonifacio I want to know if there are particular, new ways to query DataStore with API_Key. Updating the package caused this problem. For now, I just want to enable the public access of anyone.
Hi @supersonix77 👋 As far as I know, DataStore will try to use the default authentication as set in the Amplify configuration file (aws-exports). Could you provide the code for how you're configuring Amplify and attempting make the query from both the client and the server?
Hi @chrisbonifacio. For the configuration details, I attached them above. About how I configured the app, it didn't change at all. I just put the Amplify.configure({ ...awsmobile, ssr: true });
in _app.js
. Just in case to see if the Amplify.configure()
doesn't work, I even tried to put it on the particular page I'm requesting the query.
But it still complains for all the models with the error below.
User is unauthorized to query syncTasks with auth mode API_KEY. No data could be returned.
The Tasks
for syncTasks
is just the name of the model. Will it help for me to change it to other auth modes like Cognito User Pool?
@chrisbonifacio First of all, just to test out my curiosity, changing the auth mode from API Key
to AMAZON_COGNITO_USER_POOLS
made the same problems.
DataStore - User is unauthorized to query syncAdmins with auth mode AMAZON_COGNITO_USER_POOLS. No data could be returned.
This is how the models are designed.
enum AdminRole {
SUPERADMIN
COMPANYADMIN
MANAGER
}
type Admin @model @auth(rules: [{allow: public}]) @key(name: "byCompany", fields: ["companyID"]) {
id: ID!
userID: String
role: AdminRole
companyID: ID
}
type NewCourse @model @auth(rules: [{allow: public}]) @key(name: "byProgram", fields: ["programID"]) {
id: ID!
title: String
programID: ID
description: String
skillTags: [String]
majorSkill: String
modelAnswerLink: String
videoLink: String
bannerImgLink: String
Instructor: Instructor @connection
instructorID: String
}
type Course @model @key(name: "byProgram", fields: ["programID"]) @auth(rules: [{allow: public}]) {
id: ID!
title: String
courseNo: Int
slugTitle: String
programID: ID
Instructors: [CourseInstructor] @connection(keyName: "byCourse", fields: ["id"])
description: String
skillTags: [String]
majorSkill: String
modelAnswer: String
videoCode: String
bannerImg: String
}
type Task @model @auth(rules: [{allow: public}]) {
id: ID!
isSubmitted: Boolean
fileLink: String
Course: Course @connection
User: User @connection
}
type Settings @model @auth(rules: [{allow: public}]) {
id: ID!
isEmailMarketingAgreed: Boolean
}
type Instructor @model @key(name: "byCompany", fields: ["companyID"]) @auth(rules: [{allow: public}]) {
id: ID!
firstName: String
lastName: String
jobTitle: String
companyID: ID
profileImg: String
profileBanner: String
slugTitle: String
programs: [ProgramInstructor] @connection(keyName: "byInstructor", fields: ["id"])
courses: [CourseInstructor] @connection(keyName: "byInstructor", fields: ["id"])
description: String
linkedin: String
}
type Certificate @model @auth(rules: [{allow: public}]) @key(name: "byUser", fields: ["userID"]) {
id: ID!
referenceID: String
userID: ID
programID: String
}
type Program @model @key(name: "byCompany", fields: ["companyID"]) @auth(rules: [{allow: public}]) {
id: ID!
title: String
slugTitle: String
category: String
programLogo: String
programBanner: String
Courses: [Course] @connection(keyName: "byProgram", fields: ["id"])
companyID: ID
Company: Company @connection
description: String
introVideoCode: String
finalVideoCode: String
ProgramInstructors: [ProgramInstructor] @connection(keyName: "byProgram", fields: ["id"])
NewCourses: [NewCourse] @connection(keyName: "byProgram", fields: ["id"])
}
type Company @model @auth(rules: [{allow: public}]) {
id: ID!
title: String
slugTitle: String
logoImg: String
bannerImg: String
Programs: [Program] @connection(keyName: "byCompany", fields: ["id"])
website: String
description: String
category: String
logoWhite: String
Instructors: [Instructor] @connection(keyName: "byCompany", fields: ["id"])
Admins: [Admin] @connection(keyName: "byCompany", fields: ["id"])
}
type Enroll @model @key(name: "byUser", fields: ["userID"]) @auth(rules: [{allow: public}]) {
id: ID!
User: User @connection
userID: ID
programID: String
}
type User @model @auth(rules: [{allow: public}]) {
id: ID!
given_name: String
family_name: String
username: AWSEmail
Enrolls: [Enroll] @connection(keyName: "byUser", fields: ["id"])
Settings: Settings @connection
Certificates: [Certificate] @connection(keyName: "byUser", fields: ["id"])
}
type ProgramInstructor @model(queries: null) @key(name: "byProgram", fields: ["programID", "instructorID"]) @key(name: "byInstructor", fields: ["instructorID", "programID"]) @auth(rules: [{allow: public}, {allow: public}]) {
id: ID!
programID: ID!
instructorID: ID!
program: Program! @connection(fields: ["programID"])
instructor: Instructor! @connection(fields: ["instructorID"])
}
type CourseInstructor @model(queries: null) @key(name: "byCourse", fields: ["courseID", "instructorID"]) @key(name: "byInstructor", fields: ["instructorID", "courseID"]) @auth(rules: [{allow: public}, {allow: public}]) {
id: ID!
courseID: ID!
instructorID: ID!
course: Course! @connection(fields: ["courseID"])
instructor: Instructor! @connection(fields: ["instructorID"])
}
@chrisbonifacio Funny thing is that even if I only query 1~2 Models on a page, the error always includes the sync
of all the models that exist.
Below is the example of how I try to get on the server-size using the getServerSideProps
import React, { useEffect, useState } from "react";
import AuthLayout from "layouts/Auth.js";
import Button from "reactstrap/lib/Button";
import { DataStore } from "@aws-amplify/datastore";
import { Hub, withSSRContext } from "aws-amplify";
import { Program, User } from "../../src/models";
export async function getServerSideProps(context) {
const { Auth, DataStore } = withSSRContext(context);
let user = null;
let isAuthenticated = false;
let dataStoreUser = null;
try {
const { attributes } = await Auth.currentAuthenticatedUser();
if (attributes) {
user = JSON.parse(JSON.stringify(attributes));
isAuthenticated = true;
} else {
console.log("User is not logged in.");
isAuthenticated = false;
user = null;
}
} catch (error) {
console.log("Error getting user: ", error);
}
if (user !== null && user !== undefined) {
try {
const dataStoreUserQuery = await DataStore.query(User, (userData) =>
userData.username("eq", user.email)
);
console.log("DataStore user: ", dataStoreUserQuery[0]);
dataStoreUser = JSON.parse(JSON.stringify(dataStoreUserQuery[0]));
} catch (error) {
console.log(
"Error getting the DataStore User from getServerSideProps: ",
error
);
}
}
return {
props: {
user: user,
isAuthenticated: isAuthenticated,
dataStoreUser: dataStoreUser,
},
};
}
For the code above, the getServerSideProps
at least successfully get the Auth.currentAuthenticatedUser();
.
On the client-side, I do as follow.
const getUser = async () => {
try {
console.log(DataStore);
const dataStoreUser = await DataStore.query(User);
console.log("User: ", dataStoreUser);
setDataStoreUser(dataStoreUser);
const anyProgram = await DataStore.query(Program);
console.log("Program: ", anyProgram);
} catch (error) {
console.log("error getting datastoreuser: ", error);
}
};
function handleClick(event) {
event.preventDefault();
console.log("Clicked");
getUser();
}
return (
<>
<div>
<div style={{ minHeight: "100px" }}></div>
<Button className="mt-5" onClick={handleClick}>
Get User
</Button>
</div>
</>
);
On the client-side, when I trigger the getUser()
by the onClick
method, the console.log()
gets query results of none.
User: []
Program: []
PLEASE HELP!!
Why would a page with only 1~2 Models to query even start the whole sync process with all the models?
[WARN] 32:09.530 DataStore - Realtime disabled when in a server-side environment
[WARN] 32:09.540 DataStore - User is unauthorized to query syncAdmins with auth mode API_KEY. No data could be returned.
[WARN] 32:09.540 DataStore - User is unauthorized to query syncInstructors with auth mode API_KEY. No data could be returned.
[WARN] 32:09.540 DataStore - User is unauthorized to query syncCourses with auth mode API_KEY. No data could be returned.
[WARN] 32:09.540 DataStore - User is unauthorized to query syncCompanies with auth mode API_KEY. No data could be returned.
[WARN] 32:09.540 DataStore - User is unauthorized to query syncSettings with auth mode API_KEY. No data could be returned.
[WARN] 32:09.540 DataStore - User is unauthorized to query syncCertificates with auth mode API_KEY. No data could be returned.
[WARN] 32:09.543 DataStore - User is unauthorized to query syncNewCourses with auth mode API_KEY. No data could be returned.
[WARN] 32:09.543 DataStore - User is unauthorized to query syncCourseInstructors with auth mode API_KEY. No data could be returned.
[WARN] 32:09.543 DataStore - User is unauthorized to query syncPrograms with auth mode API_KEY. No data could be returned.
[WARN] 32:09.543 DataStore - User is unauthorized to query syncUsers with auth mode API_KEY. No data could be returned.
[WARN] 32:09.544 DataStore - User is unauthorized to query syncProgramInstructors with auth mode API_KEY. No data could be returned.
[WARN] 32:09.544 DataStore - User is unauthorized to query syncEnrolls with auth mode API_KEY. No data could be returned.
[WARN] 32:09.544 DataStore - User is unauthorized to query syncTasks with auth mode API_KEY. No data could be returned.
@chrisbonifacio I found out there is a consistent warning from the console.
DataStore - Data won't be synchronized. No GraphQL endpoint configured. Did you forget Amplify.configure(awsconfig)?
But I already put the config like this.
Amplify.configure({ ...awsmobile, ssr: true });
in _app.js
Is there anything to do with this configuration?
@supersonix77 hmm, okay. Yeah, your code seems correct from what I can tell. Are you getting that error message on both the client and the server?
Could you try using API.graphql
on the server side and see if that works? I've seen issues where DataStore is not being authorized properly on the server side while API might still work.
@chrisbonifacio Awesome! I just found out the API GraphQL works perfectly. However, why does this even happen? Will I have to change all the DataStore queries to API queries?? It's going to be a big construction🥲
@chrisbonifacio There must be reasons why you guys build DataStore
and API.graphql
(even API REST
) all separately. What are the potential disadvantages with migrating from DataStore
to API.graphql
?? I don't want to risk the whole project out of this unusual error that might be addressed later in the future.
One potential issue that I can think of is that the DataStore
just right now has a problem with detecting the Amplify.configure()
in the _app.js
. But if API is able to get the data from the console, that means the configuration is properly propagated when the API.graphql
queries data.
The authProviders
in the config are undefined
. Meanwhile, the API.graphql
works on both server-side and client-side.
I think now should be good timing for you to make a proper error report regarding this matter. This is surely an error with AWS Amplify -- error when the DataStore accesses the configuration that works in API
The main difference functionally between using DataStore and API is the offline capability. However, if that is not important to you or your users then API
should be able to get the job done. As far as the DataStore issues with SSR, part of the difficulty in getting it and other Amplify libraries to work the same way they do on the client is that every server side request has to reinitialize Amplify on the server and each library uses Credentials in a different way/at different levels that withSSRContext
needs to account for. So, it will be some time before everything works exactly the way it does on the client but that is the goal.
Apologies for the inconveniences in the server side but if you want users to use DataStore on the client and API on the server, I believe that fits both library's use cases better than using DataStore in both scenarios. Let me know if this would allow you to continue your work in the meantime while we look into the issue.
That being said, I am putting together a report of the issues you and other customers are experiencing with DataStore in an SSR context and will bring it to the team's attention. Thank you for helping us find bugs and making our libraries better 🙏
@chrisbonifacio Thank you for your prompt and kind response! It really clarified the main purposes of API
and DataStore
.
And I think that's a great approach to use API
on the server-side and DataStore
on the client-side. However, the main issue is that since the configuration doesn't work for DataStore
, I just cannot use DataStore at all -- even on the client-side.
Even putting the Amplify.configure({...awsmobile, ssr:true})
on the page doesn't help. Is there any way to propagate the configuration again on the client-side?
useEffect(()=>{Amplify.configure({...awsmobile, ssr:true})},[])
//Call Amplify.configure only on the client-side when the pre-build is completed.
I'm just worried about unexpected behaviors due to multiple configuration calls initiated by several pages and functions.
Using API
with custom queries seems a lot harder compared to DataStore where I can just query with whatever attributes the model has. I think API
is straightforward only if we have the id
ready on the page so that we can quickly query with the particular id
. Otherwise, it's a lot more difficult. Anyway, thanks for reporting the issues!
@chrisbonifacio Is this a valid and the best way to query API
using fields other than id
?
https://stackoverflow.com/questions/67430279/amplify-graphql-get-item-from-other-field-than-id
@supersonix77 Yes, you would have to use the @key directive to add secondary indexes to your models.
https://docs.amplify.aws/cli/graphql-transformer/key/#how-to-use-key
Also, no problem! I'm currently still trying to reproduce the issue you're having with DataStore on the client because I believe that one should still work. It's strange to me that it's not recognizing the graphql endpoint, I haven't seen that error before so I'm not exactly sure how to fix it just yet as I've been unsuccessful in reproducing it on my end.
If you wouldn't mind adding me to your repo temporarily so that I may pull down your app and try it with your exact code. I should be able to reproduce it consistently I hope.
@chrisbonifacio Thank you for letting me know!
Alright. So I just invited you to my repo as a collaborator. Please take a look at the "beta" branch.
npm run dev
localhost:3000/program
. The code can be found in pages/program/index.js
getServerSideProps
, you will be able to find the code below that I intentionally split into two parts just to let you know how they work differently.
console.log("Getting programs...");
const dataStoreProgramsQuery = await DataStore.query(Program);
console.log(
"Results from getting DataStore programs: ",
dataStoreProgramsQuery
);
const programsQuery = await API.graphql({ query: queries.listPrograms });
console.log("API Program: ", programsQuery.data.listPrograms.items);
Please let me know what other information you would need to address this issue!
@supersonix77 I managed to get the DataStore queries to work on both the client and server side. I noticed that you were importing Amplify modules by their scoped packages. I would not recommend this if you're using aws-amplify
with SSR. So, I removed those and imported the modules from aws-amplify
instead and it started to work.
You also happen to have two instances where Amplify.configure
is being called. I think you only need the one instance in _app.js
.
Pushing up the branch as cbonifacio/fix-datastore-issues
Client
Server
@chrisbonifacio Thanks! I think this is literally what the "WOW customer experience" means.
However, unfortunately, that didn't fix the error on my side. Looking at your console having a different example program, I believe you created your own Amplify backend so that you can make it on your own. The only possibility now I can think of is there is something wrong with the Amplify backend (since you used the identical code).
Are there any ways I can invite you to my real Amplify project so that you can check if there is anything wrong with the configuration??
Ahh okay, I see what you mean. There must be some differences between our auth configurations for either API or Auth. Do you get this error even before logging in? Or only after logging in?
Also, for me to use your backend you would have to share your aws-exports file with me (you can do so by emailing me at Christopher.bonifacio@gmail.com). It would point my client towards your Auth and API resources.
@chrisbonifacio It happens both before and after logging in. Since I configured the auth mode as API Key
, not Cognito User Pool
, I don't think signing in really makes changes.
I just sent you the aws-exports
file to the email. Please take a look!
@supersonix77 Thank you! I've been taking a look at the app with your aws-exports
and unfortunately I'm still not able to reproduce the DataStore - User is unauthorized to query syncAdmins with auth mode API_KEY. No data could be returned.
error. It's really strange, I'm not sure what the issue is. My best guess is maybe it's particular to the user. Maybe try creating a new user and see if the issue persists?
I tried both logged out (API_KEY) and logged in (Cognito User Pools)
Logged Out
Logged In
You can see I removed the API call completely, so I'm only using DataStore.
🤷♂️
Tried on the client side as well, still worked 😕
Logged Out (Client)
Logged In (Client)
@supersonix77 I was finally able to reproduce the issue. I realized that I was one version of aws-amplify
ahead of you on my local branch so I downgraded to 4.2.2
and got the error. However, when I upgraded back to 4.2.3
I was still getting it. I had to delete node_modules
and the package-lock.json
file and reinstall all the dependencies (stuck with aws-amplify@4.2.3) for the error to go away again.
There must have been incompatible package versions or the dependency tree just got messed up somehow. Also, I noticed that you have the @aws-amplify/cli
as a dependency in your project. That should either be a global or dev dependency. But yeah, try deleting node_modules
and the package-lock.json
file, then maybe use yarn
to install your dependencies. I ran into issues trying to install all of them with npm
. Got this error in particular.
@chrisbonifacio Thank you so much for fixing the errors! You saved my life twice so far haha.
I still don't get why using npm
will cause the issue, while yarn
will not. At least now I understand the risk of fixing the conflicts with either npm install --force
or npm install --legacy-peer-deps
. So I assume even the aws-amplify
package has some conflicts with its peer dependencies.
I wish this bug report helped both of us fix issues like this from now on for us and other users as well.
Thank you so much for helping out!😆
"WOW customer experience"😏 👍
Anytime! Thanks for being so patient and accommodating while I tried to reproduce the issue. Definitely helped me find a fix and get you back to a working state quicker 😃
Btw, I spoke to one of the team members about using DataStore on the server side and they said that API would be more performant there because DataStore performs a sync when it starts and tries to get all the data from AppSync. This is not ideal as it will do this on every request as withSSRcontext
needs to reinitialize Amplify on each request.
So, I would definitely recommend leveraging API inside of getServerSideProps
if it's not too much trouble to refactor. Otherwise, just something to consider if you need to optimize in the future.
Again, the main benefits of using DataStore are the offline capabilities it provides on the client which you can read more about here:
This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.
Looking for a help forum? We recommend joining the Amplify Community Discord server *-help
channels or Discussions for those types of questions.
Before opening, please confirm:
JavaScript Framework
React
Amplify APIs
DataStore
Amplify Categories
auth
Environment information
Describe the bug
After I updated the Amplify packages, I just couldn't query any data using DataStore. Even though I'm working on the API Key method to enable any public access through this build, I just couldn't query any data on any pages.
Importantly, I am using NextJS
Using getServerSideProps is not the cause of the problem, though. Even if I try to call through the client-side, it complains.
The user is even authenticated, and I can get the attributes successfully using the
currentAuthenticatedUser
.Here is the warning console logs.
Expected behavior
The DataStore should successfully query the requested model.
Reproduction steps
Install the packages as shown above.
Code Snippet
Log output
aws-exports.js
Manual configuration
No response
Additional configuration
No response
Mobile Device
No response
Mobile Operating System
No response
Mobile Browser
No response
Mobile Browser Version
No response
Additional information and screenshots
No response