aws-amplify / amplify-category-api

The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development. This plugin provides functionality for the API category, allowing for the creation and management of GraphQL and REST based backends for your amplify project.
https://docs.amplify.aws/
Apache License 2.0
88 stars 76 forks source link

How to add a local secondary index with amplify-cli and transformers v2? #757

Open guy-a opened 2 years ago

guy-a commented 2 years ago

The category is: amplify cli transformers v2

How to create a LSI on DynamoDB table creation? This code will generate a GSI "lsiLastName"

type User @model @auth(rules: [{allow: owner, identityClaim: "sub"}]) {
  id: ID!
  owner: String! @primaryKey(sortKeyFields: [“firstName"]) @index(name: “lsiLastName”, sortKeyFields: [“lastName"])
  firstName: String
  lastName: String
}

Amplify version 9.2.1

guy-a commented 2 years ago

I have tried adding the LSI using amplify override api but I get this error, and the LSI is never created.

error: An attribute referenced in a KeySchema element is not defined in AttributeDefinitions

// extend.ts
import { AmplifyApiGraphQlResourceStackTemplate } from '@aws-amplify/cli-extensibility-helper';
import * as dynamodb from '@aws-cdk/aws-dynamodb';

const owner: dynamodb.CfnGlobalTable.KeySchemaProperty = {
  attributeName: 'owner',
  keyType: 'HASH',
};

const lastName: dynamodb.CfnGlobalTable.KeySchemaProperty = {
  attributeName: 'lastName',
  keyType: 'RANGE',
};

const projection: dynamodb.CfnGlobalTable.ProjectionProperty = {
  projectionType: 'ALL'
}

export function override(resources: AmplifyApiGraphQlResourceStackTemplate) {

  resources.models['User'].modelDDBTable.localSecondaryIndexes = [
    {
      indexName: 'lsiLastName',
      keySchema: [owner, lastName],
      projection
    },
  ]
}

Using this schema:

# schema.graphql
type User @model @auth(rules: [{allow: owner, identityClaim: "sub"}]) {
  id: ID!
  owner: String! @primaryKey(sortKeyFields: ["firstName"])
  firstName: String!
  lastName: String
}
josefaidt commented 2 years ago

Hey @guy-a :wave: thanks for raising this and apologies for the delay! I think you have the right approach with the override. Using the following schema and override file I was able to change the GSI to an LSI

# schema.graphql
type User @model {
  id: ID!
  owner: String!
    @primaryKey(sortKeyFields: ["firstName"])
    @index(name: "byLastName", sortKeyFields: ["lastName"])
  firstName: String!
  lastName: String
}
import { AmplifyApiGraphQlResourceStackTemplate } from '@aws-amplify/cli-extensibility-helper'

export function override(resources: AmplifyApiGraphQlResourceStackTemplate) {
  const gsi = resources.models['User'].modelDDBTable.globalSecondaryIndexes
  resources.models['User'].modelDDBTable.localSecondaryIndexes = gsi
  delete resources.models['User'].modelDDBTable.globalSecondaryIndexes
}
guy-a commented 2 years ago

Thank you @josefaidt This is indeed working perfectly when first creating the api, but any later changes to the schema, same or different models, or just an empty line - will yield this error message:

Removing an LSI requires replacement of the underlying DynamoDB table.
This update will replace table(s) [UderTable]
ALL EXISTING DATA IN THESE TABLES WILL BE LOST!
If this is intended, rerun the command with '--allow-destructive-graphql-schema-updates'.

Running amplify push --allow-destructive-graphql-schema-updates will give these errors, and eventually rollback the update:

CREATE_FAILED      SubscriptiononCreateUserauth0FunctionSubscriptiononCreateUserauth0FunctionAppSyncFunction26F2963D AWS::AppSync::FunctionConfiguration
CREATE_FAILED      UserTable     AWS::DynamoDB::Table   Thu Sep 08 2022    Resource handler returned message: "Number of attributes in KeySchema does not exactly match number of attributes defined in AttributeDefinitions" (RequestToken: e93f324f-ebcf-4556-bf60-2f25f3f4317c, HandlerErrorCode: InvalidRequest)
⠸ Rolling back (3 of 2)🛑 An error occurred during the push operation: /
["Index: 1 State: {\"deploy\":\"waitingForDeployment\"} Message: Resource is not in the state stackUpdateComplete"]
josefaidt commented 1 year ago

Hey @guy-a apologies for the delay here. Are you still experiencing this issue?

guy-a commented 1 year ago

Hi @josefaidt , I switched to GSIs for now so I haven't tried it since than. It would be useful to be able to easily use LSIs using Amplify in future tables and projects.