amplify-category-api
amplify-category-api copied to clipboard
How do we point two different types to the same DynamoDB table?
For new Amplify (>=7.5.0), how do we point two different types to the same DynamoDB table? Couldn't find this anywhere in the docs.
For example, how can I have both User and UserPublic read/mutate the same DynamoDB table? I tried mucking around with CustomResources.json, but couldn't get it to work:
type User @model {
name: String
ssn: String
}
type UserPublic @model {
name: String
}
Thank you!
Can you explain your use case a bit more. It seems like this problem could be solved by using auth to control access to a single table.
@cjihrig There are plenty of use cases that cannot be solved with @auth. Whenever my signed-in users need more than 2 groups of access, I am stuck.
Example 1:
Take a User model in a basic social network. Consider User A:
- User A needs full access to User A model:
@auth(rules: [{ provider: userPools, allow: owner }]) - Friends of User A need partial access to User A model
- Non-friends of User A need light access to User A model:
@auth(rules: [{ provider: userPools, allow: private, operations: [read] }]) - Guest users have no access to User A model:
@auth(rules: [{ allow: public, operations: [] }])
There is no way for me to setup permissions for (2) using @auth. But with a secondary type UserForFriends pointing at Users table, it's trivial:
type User @model @auth(rules: [{ provider: userPools, allow: owner }]) {
name: String
favoriteMovie: String
ssn: String
}
type UserForFriends @model @auth(rules: [{ provider: userPools, allow: private, operations: [read] }]) {
name: String
favoriteMovie: String
}
type UserPublic @model @auth(rules: [{ provider: userPools, allow: private, operations: [read] }]) {
name: String
}
Example 2:
Take a User model in a basic dating app (e.g. Tinder). Consider User A:
- User A needs full access to User A model:
@auth(rules: [{ provider: userPools, allow: owner }]) - Matched users need partial access to User A model
- Potential matches need light access to User A model:
@auth(rules: [{ provider: userPools, allow: private, operations: [read] }]) - Guest users have no access to User A model:
@auth(rules: [{ allow: public, operations: [] }])
Same as above, there is no way for me to setup permissions for (2) using @auth. But with a secondary type UserForMatched pointing at Users table, it's trivial.
I just tried another approach, using amplify override api with override.ts, but it didn't work either:
import { AmplifyApiGraphQlResourceStackTemplate } from '@aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyApiGraphQlResourceStackTemplate) {
resources.models["UserPublic"].modelDDBTable = resources.models["User"].modelDDBTable
resources.models["UserPublic"].modelIamRole = resources.models["User"].modelIamRole
resources.models["UserPublic"].dynamoDBAccess = resources.models["User"].dynamoDBAccess
resources.models["UserPublic"].modelDatasource = resources.models["User"].modelDatasource
}
How do I do this? Please, help.
There are plenty of use cases that cannot be solved with @auth. Whenever my signed-in users need more than 2 groups of access
Sorry, I was referring to field level auth. Have you looked into that at all?
@cjihrig Yes, I have looked at field level auth. Whether I use model- or field-level auth, it doesn't help when I have 3+ groups of signed-in users.
Taking example (1) above, I have the owner with allow: owner permissions, a signed-in user with allow: private permissions, but I don't have allow: friends permissions for owner's friends.
Right now, to create allow:friends permission, I have to
- Create a custom authorizer that checks whether two users are friends.
- Create a custom allow/deny fields resolver.
If I could point two types at the same AppSync datasource, then I could skip step (2) while making the interface for allow: friends permission explicit in schema.graphql.
I'm landing here because I'm trying to implement a dual approval strategy where only one user can initiate the approval process.
My idea of schema.graphql is:
type sendApproval @model(subscriptions: null, timestamps: {createdAt: "createdAt", updatedAt: "sendUpdatedAt" }) @auth(rules: [{ allow: owner, ownerField: "sendOwner"}]) {
id: ID!
sendOwner: String!
rcptOwner: String!
contractId: String!
sendStatus: sendStatusType!
createdAt: AWSDateTime
sendUpdatedAt: AWSDateTime
}
type rcptApproval @model(subscriptions: null, timestamps: {createdAt: "rcptCreatedAt", updatedAt: "rcptUpdatedAt" }) @auth(rules: [{ allow: owner, operations: [read], ownerField: "rcptOwner"}]) {
id: ID! @auth(rules: [{ allow: owner, operations: [read]}])
rcptOwner: String! @auth(rules: [{ allow: owner, operations: [read]}])
rcptStatus: String @auth(rules: [{ allow: owner, operations: [read,update]}])
rcptUpdatedAt: AWSDateTime @auth(rules: [{ allow: owner, operations: [read,update]}])
sendOwner: String @auth(rules: [{ allow: owner, operations: [read]}])
}
What I've tried is to tweak the table name with override.ts, but deploy fails with "table already exists" error because amplify is trying to create two tables with the same name.
export function override(resources: AmplifyApiGraphQlResourceStackTemplate, amplifyProjectInfo: AmplifyProjectInfo) {
resources.models["sendApproval"].modelDDBTable.tableName = ApprovalTableName;
resources.models["rcptApproval"].modelDDBTable.tableName = ApprovalTableName;
...
}
Having the possibility to use the same table would be great.