I'm experiencing issues when using aws amplify to generate a graphql API
My model has mainly two objects (User and messages):
User object:
type User
#model(subscriptions: null)
#auth(rules: [{ allow: owner, ownerField: "id" }]) {
id: ID!
email: String!
username: String!
avatar: S3Object
name: String
conversations: [Conversation] #manyToMany(relationName: "UserConversations")
messages: [Message!]! #hasMany(indexName: "byAuthor", fields: ["id"])
createdAt: String
updatedAt: String
}
Message object:
type Message
#model(subscriptions: null)
#auth(
rules: [
{
allow: owner
ownerField: "authorId"
operations: [create, update, delete]
}
]
) {
id: ID!
author: User #belongsTo(fields: ["authorId"])
authorId: ID! #index(name: "byAuthor", sortKeyFields: ["content"])
content: String!
conversation: Conversation! #belongsTo(fields: ["messageConversationId"])
messageConversationId: ID!
#index(name: "byConversation", sortKeyFields: ["createdAt"])
createdAt: String
updatedAt: String
}
There's a hasMany/belongsTo relationship between the two and auth rules on both.
After I signin to the API and try to create a user object through the JS API, i'm getting the following error
'Not Authorized to access messages on type ModelMessageConnection'
await AuthAPI.graphql(
graphqlOperation(createUser, {
input: {
id,
username,
email,
name,
},
})
);
This is actually due to the message rule that was missing the read action. Changing the object model to the code below fixed it
type Message
#model(subscriptions: null)
#auth(
rules: [
{
allow: owner
ownerField: "authorId"
operations: [create, update, delete, read]
}
]
) {
id: ID!
author: User #belongsTo(fields: ["authorId"])
authorId: ID! #index(name: "byAuthor", sortKeyFields: ["content"])
content: String!
conversation: Conversation! #belongsTo(fields: ["messageConversationId"])
messageConversationId: ID!
#index(name: "byConversation", sortKeyFields: ["createdAt"])
createdAt: String
updatedAt: String
}
Related
I am using GraphQl API in an Amplify project and having some difficulty understanding how to model a many to many relationship between users. I get how to add a join table between two other tables. But now sure how to do it for the same table.
This is what I have but I'm almost certain it's not correct. I want each user to be able to add other Users as 'relations':
type User
#model
#auth(rules: [{ allow: owner, operations: [create, delete, update] }]) {
id: ID!
cognitoId: ID!
username: String!
registered: Boolean
contacts: [UserContacts] #connection(keyName: "byContact", fields: ["id"])
contactOfOtherUsers: [UserContacts] #connection(keyName: "byContact2", fields: ["id"])
}
type UserContacts
#model
#auth(rules: [{ allow: owner, operations: [create, delete, update] }])
#key(name: "byContact", fields: ["userID", "contactID"])
#key(name: "byContact2", fields: ["contactID", "userID"]) {
id: ID!
userID: ID!
contactID: ID!
user: User! #connection(fields: ["userID"])
contact: User! #connection(fields: ["contactID"])
}
I'm pretty new to Amplify and not really sure what approach to take.
What seems very wrong to me is the contactOfOtherUsers field in User. It is redundant but not sure how else to link the join table.
Maybe too late but this might work. Disclaimer: This would apply to Amplify GraphQL Transformer v1, which still works but it is deprecated as it was replaced by GraphQL Transformer v2
type User
#model
#auth(rules: [{ allow: owner}]) {
id: ID!
cognitoId: ID!
username: String!
registered: Boolean
contacts: [UserContact] #connection(keyName: "byContact", fields: ["id"])
}
type UserContact
#model
#auth(
rules: [
{ allow: owner }
{
allow: owner
ownerField: "contacts"
operations: [update, delete, read]
}
]
) #key(name: "byContact", fields: ["contactOneID", "contactTwoID"]){
id: ID!
contactOneID: ID!
contactTwoID: ID!
contactOne: User! #connection(fields: ["contactOneID"])
contactTwo: User! #connection(fields: ["contactTwoID"])
contacts: [ID]
}
Imagine I have the following AppSync GraphQL schema with a User type, and a Post type with an editors field set to an array of Users:
type User
#model
#auth(rules: [
{ allow: owner }
])
{
id: ID!
owner: String!
username: String!
}
type Post
#model
#auth(rules: [
{ allow: owner },
# Can I do this?
# { allow: owner, ownerField: "editors.owner", operations: [update] }
])
{
id: ID!
owner: String!
title: String!
content: String
editors: [User]
}
How do I create an #auth rule to give update permissions to the Users in the editors array?
If you're using the amazon Cognito user pool you should set the editor type inside Post to be an array of String and set the values to the Cognito ids of the users that you want to have access. This is explained in the amplify cli documentation.
To have the editors to be of type User I suggest you to create another paramether named differently (for example editorUsers) and connect it to the User model as described here
Your schema should look like this:
type User
#model
#key(name: "byUser", fields: ["username"])
#auth(rules: [
{ allow: owner }
])
{
id: ID!
owner: String!
username: String!
}
type Post
#model
#auth(rules: [
{ allow: owner },
{ allow: owner, ownerField: "editors", operations: [update] }
])
{
id: ID!
owner: String!
title: String!
content: String
editors: [String]
editorsUsers: [User] #connection(keyName: "byUser", fields: ["id"])
}
Let's take an example from the github repo of prisma:
We have a user, the user could have multiple posts, and one post could have multiple links.
My goal is, to retrieve all posts and all links.
This means, my response is a list (links) in a list (posts).
I want to map the values I get back as two nested lists.
datamodel.prisma
type User {
id: ID! #id
email: String! #unique
name: String
posts: [Post]!
}
type Post {
id: ID! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
published: Boolean! #default(value: false)
title: String!
content: String
author: User!
links: [Link]!
}
type Link {
id: ID! #id
url: String
title: String
post: Post!
}
schema.graphql
type Query {
...
}
type Mutation {
...
}
type Link {
id: ID!
url: String
title: String
post: Post!
}
type Post {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
published: Boolean!
title: String!
content: String
author: User!
}
type User {
id: ID!
email: String!
name: String
posts: [Post]!
}
I want to query all posts of a user, and all of the links for every post in the response.
How would I query this request?
user {
id
posts {
id
links {
id
}
}
}
The above code snipper would not work.
EDIT
I want to use the following:
User: {
listPosts: (parent, args, context, info) {
return context.prisma.posts().links()
}
}
So in my response (data in front-end via react-apollo Query Component), I want to map over posts AND the links in each post.
BUT the links attribute in posts is null.
Is there another way to achieve this?!
According to the docs:
Prisma client has a fluent API to query relations in your database. Meaning you can simply chain your method calls to navigate the relation properties of the returned records. This is only possible when retrieving single records, not for lists. Meaning you can not query relation fields of records that are returned in a list.
In order to get around that limitation, you can use the $fragment method:
const fragment = `
fragment UserWithPostsAndLinks on User {
id
email
name
posts {
id
title
content
links {
id
url
title
}
}
}
`
const userWithPostsAndLinks = await prisma.user({ id: args.id }).$fragment(fragment)
I have a profilePicture field on my User type that is being returned as null even though I can see the data is there in the database. I have the following setup:
// datamodel.prisma
enum ContentType {
IMAGE
VIDEO
}
type Content #embedded {
type: ContentType! #default(value: IMAGE)
url: String
publicId: String
}
type User {
id: ID! #id
name: String
username: String! #unique
profilePicture: Content
website: String
bio: String
email: String! #unique
phoneNumber: Int
gender: Gender! #default(value: NOTSPECIFIED)
following: [User!]! #relation(name: "Following", link: INLINE)
followers: [User!]! #relation(name: "Followers", link: INLINE)
likes: [Like!]! #relation(name: "UserLikes")
comments: [Comment!]! #relation(name: "UserComments")
password: String!
resetToken: String
resetTokenExpiry: String
posts: [Post!]! #relation(name: "Posts")
verified: Boolean! #default(value: false)
permissions: [Permission!]! #default(value: USER)
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
}
// schema.graphql
type User {
id: ID!
name: String!
username: String!
profilePicture: Content
website: String
bio: String
email: String!
phoneNumber: Int
gender: Gender!
following: [User!]!
followers: [User!]!
verified: Boolean
posts: [Post!]!
likes: [Like!]!
comments: [Comment!]!
permissions: [Permission!]!
}
Like I said there is data in the database but when I run the below query in Playground I get null:
// query
{
user(id: "5c8e5fb424aa9a000767c6c0") {
profilePicture {
url
}
}
}
// response
{
"data": {
"user": {
"profilePicture": null
}
}
}
Any idea why?
ctx.prisma.user(({ id }), info); doesn’t return profilePicture even though the field exists in generated/prisma.graphql
Fixed it. I had to add a field resolver for profilePicture under User. I've done this before for related fields like posts and comments and I think It's because profilePicture points to the #embedded Content type so is sort of a related field as well.
{
User: {
posts: parent => prisma.user({ id: parent.id }).posts(),
following: parent => prisma.user({ id: parent.id }).following(),
followers: parent => prisma.user({ id: parent.id }).followers(),
likes: parent => prisma.user({ id: parent.id }).likes(),
comments: parent => prisma.user({ id: parent.id }).comments(),
profilePicture: parent => prisma.user({ id: parent.id }).profilePicture()
}
}
I have a schema in graphcool with these nodes (not sure what the correct term is here... leaf? node? model? type??)
type User #model {
auth0UserId: String #isUnique
createdAt: DateTime!
id: ID! #isUnique
userIdentifier: String
bundleIdentifier: String
updatedAt: DateTime!
devices: [Device!]! #relation(name: "UserOnDevice")
tokens: [Token!]! #relation(name: "TokenOnUser")
}
type Device #model {
id: ID! #isUnique
deviceIdentifier: String!
users: [User!]! #relation(name: "UserOnDevice")
token: Token #relation(name: "DeviceOnToken")
}
I'd like to make it so that a user must be authenticated and be related to the device data to be able to query on it. So, for a query like:
query($deviceIdentifier: String!) {
device(deviceIdentifier: $deviceIdentifier) {
id
}
}
This should return null unless they are autthenticated and are a user in the specified relation. I was thinking I needed a permission query like this one:
query ($node_id: ID!, $user_id: ID!) {
SomeDeviceExists(filter: {
id: $node_id,
users: {
id: $user_id
}
})
}
But it turns out that is invalid. How do I do it?
query ($node_id: ID!, $user_id: ID!) {
SomeDeviceExists(filter: {
id: $node_id,
users_some: {
id: $user_id
}
})
}
but this does require submitting the user_id in the request.