AWS Appsync Graphql - amazon-web-services

With the recent changes to the Appsync Graphql transformer, I've been trying to migrate my code accordingly but seem to fail every time.
I'm following a straightforward tutorial on youtube that explains how to build a real-time chatting app.
The guy in the video develops his backend using the Appsync API with Graphql but is doing it with a V1 transformer.
I've tried to read the documentation and did change my code, but it doesn't seem to work the way I want it to.
HIS CODE
type User #model {
id: ID!
name: String!
imageUri: String
status: String
chatRoomUser: [ChatRoomUser] #connection(keyName: "byUser", fields: ["id"])
}
type ChatRoomUser
#model
#key(name: "byUser", fields: ["userID", "chatRoomID"])
#key(name: "byChatRoom", fields: ["chatRoomID", "userID"]) {
id: ID!
userID: ID!
chatRoomID: ID!
user: User #connection(fields: ["userID"])
chatRoom: ChatRoom #connection(fields: ["chatRoomID"])
}
type ChatRoom #model {
id: ID!
chatRoomUsers: [ChatRoomUser] #connection(keyName: "byChatRoom", fields: ["id"])
}
MY CODE
type User #model {
id: ID!
name: String!
imageUri: String
status: String
chatRoomUser: [ChatRoomUser] #hasMany
}
type ChatRoomUser #model {
id: ID! #primaryKey
userID: ID! #index(name: "byUser", sortKeyFields: ["chatRoomID"])
chatRoomID: ID! #index(name: "byChatRoom", sortKeyFields: ["userID"])
user: User #belongsTo(fields: ["userID"])
chatRoom: ChatRoom #belongsTo(fields: ["chatRoomID"]
}
type ChatRoom #model {
id: ID!
chatRoomUsers: [ChatRoomUser] #hasMany
}
These are his Query results with Aws
These are my Query results
This is my auto-generated queries.js file
/* eslint-disable */
// this is an auto generated file. This will be overwritten
export const getUser = /* GraphQL */ `
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
imageUri
status
chatRoomUser {
items {
id
userID
chatRoomID
createdAt
updatedAt
userChatRoomUserId
chatRoomChatRoomUsersId
}
nextToken
}
createdAt
updatedAt
}
}
`;
export const listUsers = /* GraphQL */ `
query ListUsers(
$filter: ModelUserFilterInput
$limit: Int
$nextToken: String
) {
listUsers(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
name
imageUri
status
chatRoomUser {
nextToken
}
createdAt
updatedAt
}
nextToken
}
}
`;
export const getChatRoomUser = /* GraphQL */ `
query GetChatRoomUser($id: ID!) {
getChatRoomUser(id: $id) {
id
userID
chatRoomID
user {
id
name
imageUri
status
chatRoomUser {
nextToken
}
createdAt
updatedAt
}
chatRoom {
id
chatRoomUsers {
nextToken
}
createdAt
updatedAt
}
createdAt
updatedAt
userChatRoomUserId
chatRoomChatRoomUsersId
}
}
`;
export const listChatRoomUsers = /* GraphQL */ `
query ListChatRoomUsers(
$id: ID
$filter: ModelChatRoomUserFilterInput
$limit: Int
$nextToken: String
$sortDirection: ModelSortDirection
) {
listChatRoomUsers(
id: $id
filter: $filter
limit: $limit
nextToken: $nextToken
sortDirection: $sortDirection
) {
items {
id
userID
chatRoomID
user {
id
name
imageUri
status
createdAt
updatedAt
}
chatRoom {
id
createdAt
updatedAt
}
createdAt
updatedAt
userChatRoomUserId
chatRoomChatRoomUsersId
}
nextToken
}
}
`;
export const getChatRoom = /* GraphQL */ `
query GetChatRoom($id: ID!) {
getChatRoom(id: $id) {
id
chatRoomUsers {
items {
id
userID
chatRoomID
createdAt
updatedAt
userChatRoomUserId
chatRoomChatRoomUsersId
}
nextToken
}
createdAt
updatedAt
}
}
`;
export const listChatRooms = /* GraphQL */ `
query ListChatRooms(
$filter: ModelChatRoomFilterInput
$limit: Int
$nextToken: String
) {
listChatRooms(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
chatRoomUsers {
nextToken
}
createdAt
updatedAt
}
nextToken
}
}
`;
This is his auto-generated queries.js files
/* tslint:disable */
/* eslint-disable */
// this is an auto generated file. This will be overwritten
export const getUser = /* GraphQL */ `
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
imageUri
status
chatRoomUser {
items {
id
userID
chatRoomID
createdAt
updatedAt
}
nextToken
}
createdAt
updatedAt
}
}
`;
export const listUsers = /* GraphQL */ `
query ListUsers(
$filter: ModelUserFilterInput
$limit: Int
$nextToken: String
) {
listUsers(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
name
imageUri
status
chatRoomUser {
nextToken
}
createdAt
updatedAt
}
nextToken
}
}
`;
export const getChatRoomUser = /* GraphQL */ `
query GetChatRoomUser($id: ID!) {
getChatRoomUser(id: $id) {
id
userID
chatRoomID
user {
id
name
imageUri
status
chatRoomUser {
nextToken
}
createdAt
updatedAt
}
chatRoom {
id
chatRoomUsers {
nextToken
}
createdAt
updatedAt
}
createdAt
updatedAt
}
}
`;
export const listChatRoomUsers = /* GraphQL */ `
query ListChatRoomUsers(
$filter: ModelChatRoomUserFilterInput
$limit: Int
$nextToken: String
) {
listChatRoomUsers(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
userID
chatRoomID
user {
id
name
imageUri
status
createdAt
updatedAt
}
chatRoom {
id
createdAt
updatedAt
}
createdAt
updatedAt
}
nextToken
}
}
`;
export const getChatRoom = /* GraphQL */ `
query GetChatRoom($id: ID!) {
getChatRoom(id: $id) {
id
chatRoomUsers {
items {
id
userID
chatRoomID
createdAt
updatedAt
}
nextToken
}
createdAt
updatedAt
}
}
`;
export const listChatRooms = /* GraphQL */ `
query ListChatRooms(
$filter: ModelChatRoomFilterInput
$limit: Int
$nextToken: String
) {
listChatRooms(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
chatRoomUsers {
nextToken
}
createdAt
updatedAt
}
nextToken
}
}
`;
I would love it if someone could try and look at the code and tell me if there are any wrong pieces of code because after I run queries on the console, it doesn't give me the same results it provides to him.
I appreciate any help anyone can provide,
Matti

Since you are not using primaryKey(which you are using userID and chatRoomID) for hasMany relation. You need to add indexName in User and ChatRoom model's #hasMany directive.
#belongsTo and #hasMany indexName should be same for the that relation.
type User #model {
id: ID!
name: String!
imageUri: String
status: String
chatRoomUser: [ChatRoomUser] #hasMany (indexName: "byUser")
}
type ChatRoomUser #model {
id: ID! #primaryKey
userID: ID! #index(name: "byUser", sortKeyFields: ["chatRoomID"])
chatRoomID: ID! #index(name: "byChatRoom", sortKeyFields: ["userID"])
user: User #belongsTo(fields: ["userID"])
chatRoom: ChatRoom #belongsTo(fields: ["chatRoomID"])
}
type ChatRoom #model {
id: ID!
chatRoomUsers: [ChatRoomUser] #hasMany (indexName: "byChatRoom")
}
For more information, please check out the Amplify GraphQL docs

Related

GraphQL save self relation in custom resolver in amplify

I have the following models:
type Field #model {
id: ID!
fieldID: ID #index(name: "byField", sortKeyFields: ["name"])
name: String!
type: String!
required: Boolean
fields: [Field] #hasMany(indexName: "byField", fields: ["id"])
}
type Mutation {
batchCreateField(fields: [BatchCreateField]): [Field]
}
input BatchCreateField {
id: ID
fieldID: ID
name: String!
type: String!
required: Boolean
fields: [BatchCreateField]
}
And wrote a custom resolver:
$util.log.info($util.toJson($context))
#set($isAuthorized = false)
#set( $createdAt = $util.time.nowISO8601() )
#set($fieldsArray = [])
#foreach($item in \${ctx.args.fields})
$util.qr($item.put("id", $util.defaultIfNullOrBlank($item.id, $util.autoId())))
$util.qr($item.put("createdAt", $util.defaultIfNull($item.createdAt, $createdAt)))
$util.qr($item.put("updatedAt", $util.defaultIfNull($item.updatedAt, $createdAt)))
$util.qr($item.put("__typename", "Field"))
$util.qr($fieldsArray.add($util.dynamodb.toMapValues($item)))
#end
## [End] Initialization default values. **
$util.toJson( {
"version": "2018-05-29",
"operation": "BatchPutItem",
"tables": {
"Field-INSERT_APIID-INSERT_PROJECT_ENV": $fieldsArray
}
} )
Saving in batch works fine, the self relation is not working. Is there any way i can save this self relation like below in a batch and the resolver autofilling the sub fields with the fieldID of the previously inserted field?
let fieldInput: CreateFieldInput = {
name: field.name,
type: field.type,
required: field.required,
fields: field.fields
};
batchFieldsInput.push(fieldInput)
API.graphql(graphqlOperation(batchCreateField, {fields: batchFieldsInput}))

Filter Expression can only contain non-primary key attributes

I'm new to AWS Amplify, and have created a GraphQL API for a project that I'm working on in order to learn it & AppSync. Essentially I have a schema that looks like the following:
type User #model {
id: ID! #primaryKey
boards: [Board] #hasMany
createdAt: String!
updatedAt: String!
}
type Board #model {
id: ID! #primaryKey
createdBy: User!
title: String!
}
and I'm trying to run the following query in the AppSync console:
query MyQuery {
listUsers {
items {
boards {
items {
title
}
}
}
}
}
But for some reason I keep seeing this error:
Filter Expression can only contain non-primary key attributes: Primary key attribute: userBoardsId
I've specified the primary key in both models, and I'm aware that AppSync generates the 'userBoardsId' foreign key, but I'm not sure why it's causing an issue.
Have you tried like this? Since boards is array, you need to add items
query MyQuery {
listUsers {
items {
boards {
items {
title
}
}
}
}
}
EDIT:
type User #model {
id: ID! #primaryKey
boards: [Board] #hasMany (indexName: "byUser")
createdAt: String!
updatedAt: String!
}
type Board #model {
id: ID! #primaryKey
userID: ID! #index(name: "byUser")
createdBy: User
title: String!
}

AWS Appsync Graphql query to get list of items returns empty array even though the dynamodb table has items in it

I'm running an Angular 11 application that is integrated with AWS Amplify and Appsync using GraphQL and dynamoDB for the backend.
This is my Graphql schema:-
type School
#model
#auth(
rules: [{ allow: owner, ownerField: "admins", operations: [update, read] }]
) {
id: ID!
name: String!
admins: [Member]
classes: [Class] #connection(name: "SchoolClasses")
members: [Member] #connection(name: "SchoolMembers")
}
type Class
#model
#auth(
rules: [{ allow: owner, ownerField: "admins", operations: [update, read] }]
) {
id: ID!
name: String!
school: School #connection(name: "SchoolClasses")
admins: [Member]
members: [Member] #connection(name: "ClassMembers")
}
type Member #model #auth(rules: [{ allow: owner }]) {
id: ID!
name: String!
school: School #connection(name: "SchoolMembers")
class: Class #connection(name: "ClassMembers")
}
This is my client definition:-
const client = new AWSAppSyncClient({
url: awsconfig.aws_appsync_graphqlEndpoint,
region: awsconfig.aws_appsync_region,
auth: {
type: awsconfig.aws_appsync_authenticationType,
jwtToken: async () =>
(await Auth.currentSession()).getAccessToken().getJwtToken(),
},
complexObjectsCredentials: () => Auth.currentCredentials(),
cacheOptions: {
dataIdFromObject: (obj: any) => `${obj.__typename}:${obj.myKey}`,
},
});
This is my query method:-
client
.query({
query: ListSchools,
})
.then((data: any) => {
console.log('data from listSchools ', data);
console.log(data.data.listSchools.items);
});
};
This is my query definition:-
import gql from 'graphql-tag';
export default gql`
query ListSchools(
$filter: ModelSchoolFilterInput
$limit: Int
$nextToken: String
) {
listSchools(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
name
admins {
id
name
createdAt
updatedAt
owner
}
classes {
nextToken
}
members {
nextToken
}
createdAt
updatedAt
}
nextToken
}
}
`;
The output for data in the console looks like this:-
{
"data":{
"listSchools":{
"items":[],
"nextToken":null,
"__typename":"ModelSchoolConnection"
}
},
"loading":false,
"networkStatus":7,
"stale":false
}
As you can see, the items is an empty array. But currently I have 3 items in my dynamoDB table:-
What am I doing wrong?
I have checked the regions to see if it is querying a different region, but it is checking the correct region, so I should be seeing the results. Also, wouldn't it throw an error if we're querying the wrong table?
I figured it out. The issue was in the GraphQL Schema definition where I had set the #auth paramter to only allow a certain admin to access the list, that's why I was getting back an empty array. I removed the #auth parameter and it now gives back the proper list of items.

AWS Amplify GraphQL - One to Many connections return empty list when queried

I've been following the AWS GraphQL CLI guide for setting up an API for my app, but am having trouble with connections.
The following is my current Graphql schema, with some attributes removed
type Employee #model {
id: ID!
employment: [Employment!] #connection(name: "byEmployeeIDByCompanyID", fields: ["id"])
}
type Company #model {
id: ID!
employees: [Employment!] #connection(name: "byCompanyIDByDateHired", fields: ["id"])
}
type Employment #model
#key(name: "byEmployeeIDByCompanyID", fields: ["employeeID", "companyID"], queryField: "employmentByEmployeeIDByCompanyID") {
id: ID!
companyID: ID!
employeeID: ID!
company: Company! #connection(fields: ["companyID"])
employee: Employee! #connection(fields: ["employeeID"])
}
When I query Employees or Companys, [Employment] always returns an empty array. Do I need to edit the resolvers for these fields? They should work out of the box, no?
From my understanding, using #key with 'name' and multiple 'fields' creates a secondary index on the table, and specifying that key with #connection tells the connection to use that key instead of the tables primary index. In the "byEmployeeIDByCompanyID" key, for example, employeeID is the partition key, and companyID is the sort key. A query on the "employmentByEmployeeIDByCompanyID" queryField with an employeeID but no companyID returns all the employments for a given employee, which is what I want, so why isn't the connection working?
I found success in editing the resolvers, so I'm going to go with this for now. For Employee.employment, I added:
"index": "byEmployeeIDByCompanyID",
to the request mapping template, and changed the query from:
{
"expression": "#partitionKey = :partitionKey",
"expressionNames": {
"#partitionKey": "id"
},
"expressionValues": {
":partitionKey": {
"S": "$context.source.id"
}
}
}
to
{
"expression": "#partitionKey = :partitionKey",
"expressionNames": {
"#partitionKey": "employeeID"
},
"expressionValues": {
":partitionKey": {
"S": "$context.source.id"
}
}
}

Error: GraphQL error: Query condition missed key schema element

I am using Amplify and therefore DynamoDB. I believe I have a fairly simple schema setup, but coming from MySQL my brain is going a little screw!!
The basic setup (a football league) is,
[League]
[Season]
[Divisions]
[Teams]
[Club]
[Ground]
[Club]
[Teams]
[TeamConnection] (I needed a connection schema as a team can belong to multiple divisions/seasons/leagues. I could not think of another way to connect this on the `Team` model)
[League]
[Season]
[Division]
Schemas
....
Other Schemas
...
type Club #model #key(name: "byClub", fields: ["leagueID", "name"])
{
id: ID!
name: String!
leagueID: ID!
leagues: [League] #connection(fields: ["leagueID"])
teams: [Team] #connection(keyName: "byTeams", fields: ["id"])
grounds: [Ground] #connection(keyName: "byGround", fields: ["id"])
}
enum TeamGender {
Male
Female
}
type Team #model
#key(name: "byTeamsClubId", fields: ["clubID"])
#key(name: "byTeams", fields: ["clubID", "name"])
{
id: ID!
name: String!
faId: ID!
clubID: ID!
club: Club #connection(fields: ["clubID"])
teamDetails: [TeamConnection] #connection(keyName: "byTeamsConnection", fields: ["id"])
gender: TeamGender!
}
type TeamConnection #model #key(name: "byTeamsConnection", fields: ["teamID","seasonID", "leagueID", "divisionID"])
{
id: ID!
teamID: ID!
leagueID: ID!
seasonID: ID!
divisionID: ID!
leagues: [League] #connection(fields: ["leagueID"])
teams: [Team] #connection(fields: ["teamID"])
seasons: [Season] #connection(fields: ["seasonID"])
divisions: [Division] #connection(fields: ["divisionID"])
}
type Ground #model #key(name: "byGround", fields: ["clubID", "name"])
{
id: ID!
name: String!
address1: String
address2: String
town: String
postcode: String
rating: Int
type: String
link: String
clubID: ID!
clubs: [Club] #connection(fields: ["clubID"])
}
Error
This query works with no error
//$leagueID: ''
export const LIST_CLUBS = /* GraphQL */ `
query ListClubs($leagueID: ID) {
listClubs(filter: { leagueID: { eq: $leagueID } }) {
items {
name
leagueID
}
}
}
`
This is the auto generated query used. If I used the below query, then the error seen below will kick in. This does confuse me.
//filter: { leagueID: { eq: leagueID } },
export const listClubs = /* GraphQL */ `
query ListClubs(
$filter: ModelClubFilterInput
$limit: Int
$nextToken: String
) {
listClubs(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
name
leagueID
leagues {
items {
id
name
faId
logo
seasons {
items {
id
name
faId
yearStart
yearEnd
leagueID
createdAt
updatedAt
}
nextToken
}
division {
items {
id
name
faId
divisionSeasonFaId
leagueID
seasonID
ageInput
level
createdAt
updatedAt
}
nextToken
}
createdAt
updatedAt
}
nextToken
}
teams {
items {
id
name
faId
clubID
club {
id
name
leagueID
leagues {
nextToken
}
teams {
nextToken
}
grounds {
nextToken
}
createdAt
updatedAt
}
teamDetails {
items {
id
teamID
leagueID
seasonID
divisionID
createdAt
updatedAt
}
nextToken
}
gender
createdAt
updatedAt
}
nextToken
}
grounds {
items {
id
name
address1
address2
town
postcode
rating
type
link
clubID
clubs {
items {
id
name
leagueID
createdAt
updatedAt
}
nextToken
}
createdAt
updatedAt
}
nextToken
}
createdAt
updatedAt
}
nextToken
}
}
`;
//Console
Error: GraphQL error: Query condition missed key schema element
GraphQL error: Query condition missed key schema element
GraphQL error: Query condition missed key schema element
GraphQL error: Query condition missed key schema element
GraphQL error: Query condition missed key schema element
GraphQL error: Query condition missed key schema element
GraphQL error: Query condition missed key schema element
GraphQL error: Query condition missed key schema element
GraphQL error: Query condition missed key schema element
GraphQL error: Query condition missed key schema element
GraphQL error: Query condition missed key schema element
at new ApolloError (/var/www/co.uk/node_modules/apollo-client/bundle.umd.js:92:26)
Query working as mentioned above.
Errors returned from GraphIQL
However frustratingly, which I did not know. The data is actually returned, plus the errors at the end.