amplify-category-api
amplify-category-api copied to clipboard
Amplify Mock LSI fetches base table properties but AppSync Queries don't
Before opening, please confirm:
- [X] I have installed the latest version of the Amplify CLI (see above), and confirmed that the issue still persists.
- [X] I have searched for duplicate or closed issues.
- [X] I have read the guide for submitting bug reports.
- [X] I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
- [X] I have removed any sensitive information from my code snippets and submission.
How did you install the Amplify CLI?
yarn
If applicable, what version of Node.js are you using?
14.20.1
Amplify CLI Version
10.2.3
What operating system are you using?
Mac
Did you make any manual changes to the cloud resources managed by Amplify? Please describe the changes made.
No, all new stack and CLI only
Amplify Categories
api
Amplify Commands
Not applicable
Describe the bug
Specifying a @hasMany
relationship targeting an LSI index with KEYS_ONLY
allows queries in amplify mock
that fetch from the related table base (not just keys in the LSI index). Running the same queries via AWS Console AppSync does not, returns null for additional fields, throwing errors if those fields are declared as non-nullable.
From the dynamodb docs:
When you query a local secondary index, the query can also retrieve attributes that are not projected into the index. DynamoDB automatically fetches these attributes from the base table, but at a greater latency and with higher provisioned throughput costs.
See the bottom row in this table
If you query or scan a local secondary index, you can request attributes that are not projected in to the index. DynamoDB automatically fetches those attributes from the table.
Expected behavior
I would expect the AWS Console Appsync Queries to fetch non-projected attributes from the base table, at a slightly higher latency and read cost.
Reproduction steps
-
amplify init
-
amplify add auth
(Cognito) - add user group Admin
-
amplify add api
blank schema - paste schema into
schema.graphql
- edit
cli.json
to set"secondarykeyasgsi": false,
- amplify override api, paste following
KEYS_ONLY
code intooverride.ts
import { AmplifyApiGraphQlResourceStackTemplate } from '@aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyApiGraphQlResourceStackTemplate) {
Object.values(resources.models).forEach(modelDirectiveStack => {
modelDirectiveStack.modelDDBTable?.addPropertyOverride("LocalSecondaryIndexes.0.Projection.ProjectionType", "KEYS_ONLY");
modelDirectiveStack.modelDDBTable?.addPropertyOverride("LocalSecondaryIndexes.1.Projection.ProjectionType", "KEYS_ONLY");
modelDirectiveStack.modelDDBTable?.addPropertyOverride("LocalSecondaryIndexes.2.Projection.ProjectionType", "KEYS_ONLY");
modelDirectiveStack.modelDDBTable?.addPropertyOverride("LocalSecondaryIndexes.3.Projection.ProjectionType", "KEYS_ONLY");
modelDirectiveStack.modelDDBTable?.addPropertyOverride("LocalSecondaryIndexes.4.Projection.ProjectionType", "KEYS_ONLY");
});
}
-
amplify mock
- in mock environment, run mutation
createContent
withSK: "1234"
, run mutationcreateImage
withSK: "asdf"
andLSI2: "1234"
- in mock environment, run query
listContent
, specify allitems
of the content, allitems
of the images - in AppSync Console, repeat the mutation (
PK: "_CONTENT"
for both) and query steps - notice the error fetching non-index properties (like
updatedAt
) that are non-nullable
GraphQL schema(s)
Note Content and Image share PK: _CONTENT
, and that Content @hasMany
targets Image LSI2
# Put schemas below this line
enum _CONTENT { _CONTENT }
type Content
@model(timestamps: { createdAt: "LSI1", updatedAt: "updatedAt" })
@auth(rules: [{ allow: groups, groups: ["Admin"] }]) {
PK: _CONTENT!
@primaryKey(sortKeyFields: ["SK"])
@index(name: "LSI1", sortKeyFields: ["LSI1"], queryField: "contentByLSI1")
@index(name: "LSI2", sortKeyFields: ["LSI2"], queryField: "contentByLSI2")
@index(name: "LSI3", sortKeyFields: ["LSI3"], queryField: "contentByLSI3")
@index(name: "LSI4", sortKeyFields: ["LSI4"], queryField: "contentByLSI4")
@index(name: "LSI5", sortKeyFields: ["LSI5"], queryField: "contentByLSI5")
SK: String! # ID
LSI1: String # createdAt
LSI2: String
LSI3: String
LSI4: String
LSI5: String
# Image *LSI2* match Content *PK/SK* fields
images: [Image]! @hasMany(indexName: "LSI2", fields: ["PK", "SK"])
updatedAt: AWSDateTime!
}
type Image
@model(timestamps: { createdAt: "LSI1", updatedAt: "updatedAt" })
@auth(rules: [{ allow: groups, groups: ["Admin"] }]) {
PK: _CONTENT!
@primaryKey(sortKeyFields: ["SK"])
@index(name: "LSI1", sortKeyFields: ["LSI1"], queryField: "imageByLSI1")
@index(name: "LSI2", sortKeyFields: ["LSI2"], queryField: "imageByLSI2")
@index(name: "LSI3", sortKeyFields: ["LSI3"], queryField: "imageByLSI3")
@index(name: "LSI4", sortKeyFields: ["LSI4"], queryField: "imageByLSI4")
@index(name: "LSI5", sortKeyFields: ["LSI5"], queryField: "imageByLSI5")
SK: String! # ID
LSI1: String # createdAt
LSI2: String # parent ID
LSI3: String
LSI4: String
LSI5: String
updatedAt: AWSDateTime!
}
Project Identifier
529fd62f57a7db0816abf0c159f3a791
Log output
No response
Additional information
No response