Using AWS Amplify, I finally removed the #key attribute of one of my models (I don't need it, I want an autogenerated UUID).
When doing a amplify push I get this error
Attempting to edit the key schema of the NewsTable table in the News stack.
An error occured during the push operation: Attempting to edit the key schema of the NewsTable table in the News stack.
How can I force it ?
I don't mind losing data as it's not in production. I just don't want to hack it with a crappy rename or having to delete my whole App Sync project for this.
NOT FOR PRODUCTION. Be careful, this method erases all your data.
Assuming you need to remove the key "licenceNumber" from the "Car" model
type Car #model {
licenceNumber: String! #key
color: String!
}
Push anything but remove that Car model
type Car_renamed_to_make_the_push_work #model {
licenceNumber: String!
color: String!
}
And then rename your model again, but without the key
type Car #model {
licenceNumber: String!
color: String!
}
Related
I'm fairly new to Amplify. I ran the amplify commands to create the src/models/index.d.ts and the generated API file, src/app/API.ts files. Each of these have the same types generated except that embedded collections generated via #belogsTo and #hasMany is not on the type in API.ts but IS on index.d.ts. When I try running the code I have a JSON object that I've cast to the model in src/app/API.ts. Like I say, there is an embedded collection that does NOT show up on the API file. Here is what I have for the schema file schema.graphql.
EDIT: It appears there are multiple ways to do the same thing, such as getting, updating, and deleting data. I think that using API.ts is the latests way to do it? I'm just totally confused by all of this.
type Blog #model {
id: ID! #primaryKey
name: String!
posts: [Post] #hasMany
}
type Post #model {
id: ID! #primaryKey
title: String!
blog: Blog #belongsTo
comments: [Comment] #hasMany
}
type Comment #model {
id: ID! #primaryKey
post: Post #belongsTo
content: String!
}
The API model that was generated does not have comments on type Post. Here is the generated code in the API file:
export type CreatePostInput = {
id?: string | null;
title: string;
blogPostsId?: string | null;
};
That is the type that is passed into CreatePostInput. When I try to specify the json:
const json = {
blogPostsId: 'Some Random UUID String',
title: 'MyPost',
} as Post;
I cannot add a comments array to this json because CreatePostInput and Post models does not contain comments that I specified on the schema.graphql.
My question is, first, what definition of Blog should I be using, the one in the index.t.ds file or the definition in the API file? If the answer is the one in index.d.ts, how to do go about creating one because the CreateBlogInput is the type that CreatePost in API.ts file.
I've tried different things that I've found on the interwebs from AWS blogs to Medium posts.
I am using AWS Appsync with dynamoDB and trying to create a connection between two schema Course and Badges in which once course can have multiple badges list. While badges list are static (don't change it frequently). I tried this but it doesn't worked.
type Badge #model #auth(rules: [{allow: private}]) {
id: ID!
Name: String
}
type Course #model #auth(rules: [{allow: private}]) {
id: ID!
badges: [Badge] #connection
}
You can find the solution here as for the documentation.
Please make sure that you don't read the legacy docs, as #connection directive is deprecated.
Hope it helps!
Which Category is your question related to?
DynamoDB, AppSync(GraphQL)
Amplify CLI Version
4.50.2
Provide additional details e.g. code snippets
BACKGROUND:
I'm new in AWS serverless app systems and as a frontend dev, I'm quite enjoying it thanks to auto-generated APIs, tables, connections, resolvers etc. I'm using Angular/Ionic in frontend and S3, DynamoDB, AppSync, Cognito, Amplify-cli for the backend.
WHAT I HAVE:
Here is a part of my schema. I can easily use auto-generated APIs to List/Get Feedbacks with additional filters (i.e. score: { ge: 3 }). And thanks to the #connection I can see the User's details in the listed Feedback items.
type User #model #auth(rules: [{ allow: owner }]) {
id: ID!
email: String!
name: String!
region: String!
sector: String!
companyType: String!
}
type Feedback #model #auth(rules: [{ allow: owner }]) {
id: ID!
user: User #connection
score: Int!
content: String
}
WHAT I WANT:
I want to list Feedbacks based on several fields on User type, such as user's region (i.e. user.region: { contains: 'United States' }). Now I searched for a solution quite a lot like, #2311 , and I learned that amplify codegen only creates top-level filtering. In order to use cross-table filtering, I believe I need to modify resolvers, lambda functions, queries and inputs. Which, for a beginner, it looks quite complex.
WHAT I TRIED/CONSIDERED:
I tried listing all Users and Feedbacks separately and filtering them in front-end. But then the client downloads all these unnecessary data. Also because of the pagination limit, user experience takes a hit as they see an empty list and repeatedly need to click Load More button.
Thanks to some suggestions, I also thought about duplicating the User details in Feedback table to be able to search/filter them. Then the problem is that if User updates his/her info, duplicated values will be out-of-date. Also there will be too many duplicated data, as I need this feature for other tables also.
I also heard about using ElasticSearch for this problem but someone mentioned for a simple filtering he got 30$ monthly cost, so I got cold feet.
I tried the resolver solution to add a custom filtering in it. But I found that quite complex for a beginner. Also I will need this cross-table filtering in many other tables as well, so I think would be hard to manage. If that is the best-practice, I'd appreciate it if someone can guide me through it.
QUESTIONS:
What would be the easiest/beginner-friendly solution for me to achieve this cross-table filtering? I am open to alternative solutions.
Is this cross-table filtering a bad approach for a no-SQL setup? Since I need some relationship between two tables. (I thought #connection would be enough). Should I switch to an SQL setup before it is too late?
Is it possible for Amplify to auto-generate a solution for this in the future? I feel like many people are experiencing the same issue.
Thank you in advance.
Amplify, and really DynamoDB in general, requires you to think about your access patterns ahead of time. There is a lot of really good information out there to help guide you through what this thought process can look like. Particularly, I like Nader Dabit's https://dev.to/dabit3/data-modeling-in-depth-with-graphql-aws-amplify-17-data-access-patterns-4meh
At first glance, I think I would add a new #key called byCountry to the User model, which will create a new Global Secondary Index on that property for you in DDB and will give you some new query methods as well. Check out https://docs.amplify.aws/cli/graphql-transformer/key#designing-data-models-using-key for more examples.
Once you have User.getByCountry in place, you should then be able to also bring back each user's Feedbacks.
query USAUsersWithFeedbacks {
listUsersByCountry(country: "USA") {
items {
feedbacks {
items {
content
}
nextToken
}
}
nextToken
}
}
Finally, you can use JavaScript to fetch all while the nextToken is not null. You will be able to re-use this function for each country you are interested in and you should be able to extend this example for other properties by adding additional #keys.
My former answer can still be useful for others in specific scenarios, but I found a better way to achieve nested filtering when I realized you can filter nested items in custom queries.
Schema:
type User #model {
id: ID!
email: String!
name: String!
region: String!
sector: String!
companyType: String!
feedbacks: [Feedback] #connection # <-- User has many feedbacks
}
Custom query:
query ListUserWithFeedback(
$filter: ModelUserFilterInput # <-- Filter Users by Region or any other User field
$limit: Int
$nextToken: String
$filterFeedback: ModelFeedbackFilterInput # <-- Filter inner Feedbacks by Feedback fields
$nextTokenFeedback: String
) {
listUsers(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
email
name
region
sector
companyType
feedbacks(filter: $filterFeedback, nextToken: $nextTokenFeedback) {
items {
content
createdAt
id
score
}
nextToken
}
createdAt
updatedAt
}
nextToken
}
}
$filter can be something like:
{ region: { contains: 'Turkey' } }
$filterFeedback can be like:
{
and: [{ content: { contains: 'hello' }, score: { ge: 4 } }]
}
This way both Users and Feedbacks can be filtered at the same time.
Ok thanks to #alex's answers I implemented the following. The idea is instead of listing Feedbacks and trying to filter them by User fields, we list Users and collect their Feedbacks from the response:
Updated schema.graphql as follows:
type User
#model
#auth(rules: [{ allow: owner }])
#key(name: "byRegion", fields: ["region"], queryField: "userByRegion") # <-- added byRegion key {
id: ID!
email: String!
name: String!
region: String!
sector: String!
companyType: String!
feedbacks: [Feedback] #connection # <-- added feedbacks connection
}
Added userFeedbacksId parameter while calling CreateFeedback. So they will appear while listing Users.
Added custom query UserByRegionWithFeedback under src/graphql/custom-queries.graphl and used amplify codegen to build it:
query UserByRegionWithFeedback(
$region: String
$sortDirection: ModelSortDirection
$filter: ModelUserFilterInput
$limit: Int
$nextToken: String # <-- nextToken for getting more Users
$nextTokenFeedback: String # <-- nextToken for getting more Feedbacks
) {
userByRegion(
region: $region
sortDirection: $sortDirection
filter: $filter
limit: $limit
nextToken: $nextToken
) {
items {
id
email
name
region
sector
companyType
feedbacks(nextToken: $nextTokenFeedback) {
items {
content
createdAt
id
score
}
nextToken
}
createdAt
updatedAt
owner
}
nextToken
}
}
Now I call this API like the following:
nextToken = {
user: null,
feedback: null
};
feedbacks: any;
async listFeedbacks() {
try {
const res = await this.api.UserByRegionWithFeedback(
'Turkey', // <-- region: filter Users by their region, I will add UI input later
null, // <-- sortDirection
null, // <-- filter
null, // <-- limit
this.nextToken.feedback == null ? this.nextToken.user : null, // <-- User nextToken: Only send if Feedback NextToken is null
this.nextToken.feedback // <-- Feedback nextToken
);
// Get User NextToken
this.nextToken.user = res.nextToken;
// Initialize Feedback NextToken as null
this.nextToken.feedback = null;
// Loop Users in the response
res.items.map((user) => {
// Get Feedback NextToken from User if it is not null (Or else last User in the list could overrite it)
if (user.feedbacks.nextToken) {
this.nextToken.feedback = user.feedbacks.nextToken;
}
// Push the feedback items into the list to diplay in UI
this.feedbacks.push(...user.feedbacks.items);
});
} catch (error) {
this.handleError.show(error);
}
}
Lastly I added a Load More button in the UI which calls listFeedbacks() function. So if there is any Feedback NextToken, I send it to the API. (Note that multiple user feedbacks can have a nextToken).
If all feedbacks are ok and if there is a User NextToken, I send that to the API and repeat the process for new Users.
I believe this could be much simpler with an SQL setup, but this will work for now. I hope it helps others in my situation. And if there is any ideas to make this better I'm all ears.
shown below is the graphql Schema.
#auth(rules: [{ allow: owner,operations: [create, delete ] ,ownerField: "user"}])
{
id: ID!
videoKey: String!
videoThumbnailKey :String!
videoTitle:String!
videoDescription:String!
channelName:String!
videoLikes: Int
videoDislikes: Int
comments: [Comment] #connection(keyName: "byVideo", fields: ["id"])
user: String
}
type Comment #model
#auth(rules: [{ allow: owner,operations: [create,delete] ,ownerField: "user"}])
#key(name: "byVideo", fields: ["videoID", "comment"]) {
id: ID!
videoID: ID!
comment: String!
video: Video #connection(fields: ["videoID"])
user: String
}
type LikedVideos #model
{
id:ID!
video:[Video]#connection
}
this is the query for listing videos
and these are the results
this is the query for getting a video
and these are the results
all of these works fine, but when I try to delete it I get error.
this is the mutation query
and this is the error response i got
Seems like you have not added sort key in dynamo db table and the other items have the same primary key as of first item. I faced similar issue and worked after adding sort key.
This seems to be another bug in AWS Amplify or local DynamoDB.
I am using the same schema - when I add even just a space to schema.grapql, first delete operation works. The delete operations executed after are not working.
Also, it seems like something is throttling query response (I am using amplify mock command):
E.g. I have 7 items in the database and when I am trying to list all of them, I got responses with 1, 2, or even zero items, multiple times - when I would expect, that they will be loaded in 1 request, at the same time.
I am using AWS Amplify
"aws-amplify": "^3.1.1",
"aws-amplify-react-native": "^4.2.5",
Even if you modify schema.graphql, it's still unreliable.
I am using AWS Amplify to configure Appsync in my project. In my schema.graphql, I have the following types:
type Post #model {
id: ID!
tweet: Tweet
}
type Tweet {
id: ID
text: String!
}
Tweet is a nested object under Post, it is intentionally not a #model.
When I upload to AWS via the cli (amplify push), it removes the Tweet's id field from TweetInput on the generated schema. This is the generated schema from the AWS console:
input CreatePostInput {
id: ID
tweet: TweetInput
}
type Tweet {
id: ID
text: String!
}
input TweetInput {
text: String!
}
I have searched the Appsync documentation, but I cannot find anywhere that says I can't use an id field on an object type.
Is there any way around this? I'd like to avoid renaming the field if I can.
Turns out that this is a bug in the CLI https://github.com/aws-amplify/amplify-cli/issues/1984
Hopefully it gets resolved soon.