I am trying to update the user data using the API from AWS Amplify. I can add new record to the connected table with the createUser function imported from graphql mutation but the updateUser doesn't work and returning error. The function I used is defined as below:
async function UpdateUsers() {
try {
const user = {
role: 'student',
fname: formState.fname,
lname: formState.lname,
age: formState.age,
school: formState.school,
class_name: formState.class_name,
sid: formState.sid,
test1: formState.test1,
test2: formState.test2,
test3: formState.test3
}
const newData = await API.graphql(graphqlOperation(updateUser, {input: user}))
} catch (err) {
alert('Error when updating user', err)
}
}
}
I also tried to do it another way by deleting the old record and create a new one. But the deleteUser function generated automatically from graphql mutation did not work either.
Related
Use Case: I desire to delete a DynamoDB record with a mutation as an owner of the record using AWS Amplify, GraphQL and Typescript.
Note:
All changes have successfully deployed and built in the AWS Amplify Pipeline as a full-stack build with CI/CD
I am logged in as the owner of the record.
I have used console.log to ensure the values are obtained before the await function is ran.
Front-End Query:
await API.graphql({
query: deleteImage,
variables: {
input: {
id : fileName,
employerID : Auth.user.attributes["custom:id"],
},
},
authMode: "AMAZON_COGNITO_USER_POOLS",
});
GraphQL Schema:
type Image
#model
#auth(
rules: [
{ allow: owner, operations: [create, update, delete, read] }
]
GraphQL Mutation:
export const deleteImage = /* GraphQL */ `
mutation DeleteImage(
$input: DeleteImageInput!
$condition: ModelImageConditionInput
) {
deleteImage(input: $input, condition: $condition) {
id
owner
name
contentType
createdAt
updatedAt
employerID
}
}
`;
Error Message:
"Not Authorized to access deleteImage on type Mutation"
The unauthorized error may occur misleadingly when the record in the DynamoDB table does not exist.
Check your table to see whether the record exists, as this may be the cause.
Category: API.graphql mutation
Amplify Version: 8.0.1
Service: AppSync
Research
I've come across this documentation that notes batch is not supported, and thus requires a custom resolver.
I've also come across this feature request for cascading delete with DynamoDB Streams and Lambda.
I see there is an example for a batch put custom resolver.
Problem:
There is a #model for JobListing, and one for Image within the Amplify Graphql Schema. They have a ManyToMany Relationship. When I delete an Image record from DynamoDB, all JobListingImage records for that image need to be deleted as well.
I've considered looping through the JobListingImage records, but there could be a possiblity where there are 100,000 records to query and then delete.
Code Snippet of failed attempt
const handleDelete = async (id, name) => {
try {
await API.graphql({
query: deleteJobListingImages,
variables: {
input: {
imageID: id,
imageemployerID: Auth.user.attributes["custom:id"],
},
},
authMode: "AMAZON_COGNITO_USER_POOLS",
});
await Storage.remove(name, {
level: "protected",
});
await API.graphql({
query: deleteImage,
variables: {
input: {
id: id,
employerID: Auth.user.attributes["custom:id"],
},
},
authMode: "AMAZON_COGNITO_USER_POOLS",
});
} catch (err) {
console.log(err);
}
};
Question
How do I delete all the JobListingImages that refer to the Image id I am deleting?
I'm working with Next.js Server Side Rendering and AWS Amplify to get data. However, I've come to a roadblock, where I'm getting an error saying that there's no current user.
My question is why does the app need to have a user if the data is supposed to be read for the public?
What I'm trying to do is show data for the public, if they go to a user's profile page. They don't have to be signed into the app.
My current folder structure is:
/pages/[user]/index.js with getStaticProps and getStaticPaths:
export async function getStaticPaths() {
const SSR = withSSRContext();
const { data } = await SSR.API.graphql({ query: listUsers });
const paths = data.listUsers.items.map((user) => ({
params: { user: user.username },
}));
return {
fallback: true,
paths,
};
}
export async function getStaticProps({ params }) {
const SSR = withSSRContext();
const { data } = await SSR.API.graphql({
query: postsByUsername,
variables: {
username: params.username,
},
});
return {
props: {
posts: data.postsByUsername.items,
},
};
}
Finally figured it out. A lot of tutorials uses authMode: 'AMAZON_COGNITO_USER_POOLS ' // or AWS_IAM parameter in their graphql query for example in https://docs.amplify.aws/lib/graphqlapi/authz/q/platform/js/
// Creating a post is restricted to IAM
const createdTodo = await API.graphql({
query: queries.createTodo,
variables: {input: todoDetails},
authMode: 'AWS_IAM'
});
But you rarely come across people who use authMode: API_KEY.
So I guess, if you want the public to read without authentication, you would just need to set authMode: 'API_KEY'...
Make sure you configure your amplify API to have public key as well.
I am using aws-amplify in my react-native application for user authentication. it works fine.
modules used for aws amplify
aws-amplify: "^3.0.23", aws-amplify-react-native: "^4.2.4"
Now I would like to connect the app or retrieve the data from a DynamoDB table and use that data in my react native application.
How can I Access data from Dynamodb tables in react native using aws-amplify is there any get methods or rest APIs to retrieve data from dynamodb.
How can I establish a connection with dB to my react-native application.
Thanks in advance.
I used
aws-sdk/dist/aws-sdk-react-native
var AWS = require('aws-sdk/dist/aws-sdk-react-native');
AWS.config.update({
region: 'your region',
dynamoDbCrc32: false
});
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'your identityPool Id'
});
const gp = AWS.config.credentials.getPromise();
gp.then(() => {
const documentClient = new AWS.DynamoDB.DocumentClient();
const params = {
TableName: "table name",
Key: "your key",
};
documentClient.get(params, function(err, data) {
if (err){
console.log(err)
} else {
console.log(data)
}
}
I m using graphql api and when i create any model, aws automatically creates queries and mutations for that.
Let's say i have this model in schema json.
type ExpoTicketsObject
#model
#auth(rules: [{ allow: private, provider: iam, operations: [read, create, update, delete] }]) {
id: ID!
tickets: AWSJSON!
}
here are related query and mutation:
in lambda function i defined the query:
const appsync = require("aws-appsync");
const gql = require("graphql-tag");
// inorder to use fetch api in node, we need this polyfill
require("cross-fetch/polyfill");
const getExpoToken = gql`
query getExpoToken($token: String!) {
# token is the primary key.
getExpoToken(token: $token) {
id
token
playerUsername
}
}
`;
then I call it:
const tokenRes = await graphqlClient.query({
query: getExpoToken,
variables: { token: event.arguments.token }
});
What I want to do?
I want to create REST API that returns data from my DynamoDB table which is being created by GraphQL model.
What I've done
Create GraphQL model
type Public #model {
id: ID!
name: String!
}
Create REST API with Lambda Function with access to my PublicTable
$ amplify add api
? Please select from one of the below mentioned services: REST
? Provide a friendly name for your resource to be used as a label for this category in the project: rest
? Provide a path (e.g., /book/{isbn}): /items
? Choose a Lambda source Create a new Lambda function
? Provide an AWS Lambda function name: listPublic
? Choose the runtime that you want to use: NodeJS
? Choose the function template that you want to use: Hello World
Available advanced settings:
- Resource access permissions
- Scheduled recurring invocation
- Lambda layers configuration
? Do you want to configure advanced settings? Yes
? Do you want to access other resources in this project from your Lambda function? Yes
? Select the category storage
? Storage has 8 resources in this project. Select the one you would like your Lambda to access Public:#model(appsync)
? Select the operations you want to permit for Public:#model(appsync) create, read, update, delete
You can access the following resource attributes as environment variables from your Lambda function
API_MYPROJECT_GRAPHQLAPIIDOUTPUT
API_MYPROJECT_PUBLICTABLE_ARN
API_MYPROJECT_PUBLICTABLE_NAME
ENV
REGION
? Do you want to invoke this function on a recurring schedule? No
? Do you want to configure Lambda layers for this function? No
? Do you want to edit the local lambda function now? No
Successfully added resource listPublic locally.
Next steps:
Check out sample function code generated in <project-dir>/amplify/backend/function/listPublic/src
"amplify function build" builds all of your functions currently in the project
"amplify mock function <functionName>" runs your function locally
"amplify push" builds all of your local backend resources and provisions them in the cloud
"amplify publish" builds all of your local backend and front-end resources (if you added hosting category) and provisions them in the cloud
Succesfully added the Lambda function locally
? Restrict API access No
? Do you want to add another path? No
Successfully added resource rest locally
Edit my Lambda function
/* Amplify Params - DO NOT EDIT
API_MYPROJECT_GRAPHQLAPIIDOUTPUT
API_MYPROJECT_PUBLICTABLE_ARN
API_MYPROJECT_PUBLICTABLE_NAME
ENV
REGION
Amplify Params - DO NOT EDIT */
const AWS = require("aws-sdk");
const region = process.env.REGION
AWS.config.update({ region });
const docClient = new AWS.DynamoDB.DocumentClient();
const params = {
TableName: "PublicTable"
}
async function listItems(){
try {
const data = await docClient.scan(params).promise()
return data
} catch (err) {
return err
}
}
exports.handler = async (event) => {
try {
const data = await listItems()
return { body: JSON.stringify(data) }
} catch (err) {
return { error: err }
}
};
Push my updates
$ amplify push
Open my REST API endpoint /items
{
"message": "User: arn:aws:sts::829736458236:assumed-role/myprojectLambdaRolef4f571b-dev/listPublic-dev is not authorized to perform: dynamodb:Scan on resource: arn:aws:dynamodb:us-east-1:8297345848236:table/Public-ssrh52tnjvcdrp5h7evy3zdldsd-dev",
"code": "AccessDeniedException",
"time": "2021-04-21T21:21:32.778Z",
"requestId": "JOA5KO3GVS3QG7RQ2V824NGFVV4KQNSO5AEMVJF66Q9ASUAAJG",
"statusCode": 400,
"retryable": false,
"retryDelay": 28.689093010346657
}
Problems
What I did wrong?
How do I access my table and why I didn't get it when I created it?
Why API_MYPROJECT_PUBLICTABLE_NAME and other constants are needed?
Decision
The problem turned out to be either the NodeJS version or the amplify-cli version. After updating amplify-cli and installing the node on the 14.16.0 version, everything worked.
I also changed the name of the table to what Amplify creates for us, although this code did not work before. The code became like this:
/* Amplify Params - DO NOT EDIT
API_MYPROJECT_GRAPHQLAPIIDOUTPUT
API_MYPROJECT_PUBLICTABLE_ARN
API_MYPROJECT_PUBLICTABLE_NAME
ENV
REGION
Amplify Params - DO NOT EDIT */
const AWS = require("aws-sdk");
const region = process.env.REGION
const tableName = process.env.API_MYPROJECT_PUBLICTABLE_NAME
AWS.config.update({ region });
const docClient = new AWS.DynamoDB.DocumentClient();
const params = {
TableName: tableName
}
async function listItems(){
try {
const data = await docClient.scan(params).promise()
return data
} catch (err) {
return err
}
}
exports.handler = async (event) => {
try {
const data = await listItems()
return { body: JSON.stringify(data) }
} catch (err) {
return { error: err }
}
};