Utilizzo di Aurora Serverless v2 con AWS AppSync - AWS AppSync GraphQL

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Utilizzo di Aurora Serverless v2 con AWS AppSync

Connect l'API GraphQL ai database Aurora Serverless utilizzando. AWS AppSync Questa integrazione consente di eseguire istruzioni SQL tramite query, mutazioni e sottoscrizioni GraphQL, offrendoti un modo flessibile di interagire con i tuoi dati relazionali.

Nota

In questo tutorial viene utilizzata la regione US-EAST-1.

Vantaggi
  • Perfetta integrazione tra GraphQL e database relazionali

  • Capacità di eseguire operazioni SQL tramite interfacce GraphQL

  • Scalabilità senza server con Aurora Serverless v2

  • Accesso sicuro ai dati tramite AWS Secrets Manager

  • Protezione dall'iniezione SQL tramite la sanificazione degli input

  • Funzionalità di interrogazione flessibili, tra cui operazioni di filtraggio e intervallo

Casi di utilizzo comune
  • Creazione di applicazioni scalabili con requisiti di dati relazionali

  • La creazione di APIs ciò richiede sia la flessibilità GraphQL che le funzionalità del database SQL

  • Gestione delle operazioni sui dati tramite mutazioni e query GraphQL

  • Implementazione di modelli di accesso sicuri al database

In questo tutorial, imparerai quanto segue.

  • Configura un cluster Aurora Serverless v2

  • Abilita la funzionalità Data API

  • Crea e configura strutture di database

  • Definizione degli schemi GraphQL per le operazioni del database

  • Implementa resolver per query e mutazioni

  • Proteggi l'accesso ai dati mediante un'adeguata sanificazione degli input

  • Esegui varie operazioni di database tramite interfacce GraphQL

Configurazione del cluster di database

Prima di aggiungere un'origine dati Amazon RDS a AWS AppSync, devi prima abilitare una Data API su un cluster Aurora Serverless v2 e configurare un utilizzo segreto. AWS Secrets Manager È possibile creare un cluster Aurora Serverless v2 utilizzando: AWS CLI

aws rds create-db-cluster \ --db-cluster-identifier appsync-tutorial \ --engine aurora-mysql \ --engine-version 8.0 \ --serverless-v2-scaling-configuration MinCapacity=0,MaxCapacity=1 \ --master-username USERNAME \ --master-user-password COMPLEX_PASSWORD \ --enable-http-endpoint

Verrà restituito un ARN per il cluster.

Dopo aver creato il cluster, è necessario aggiungere un'istanza Aurora Serverless v2 utilizzando il comando seguente.

aws rds create-db-instance \ --db-cluster-identifier appsync-tutorial \ --db-instance-identifier appsync-tutorial-instance-1 \ --db-instance-class db.serverless \ --engine aurora-mysql
Nota

L'attivazione di questi endpoint richiede tempo. Puoi verificarne lo stato nella console Amazon RDS nella scheda Connettività e sicurezza del cluster. Puoi anche controllare lo stato del tuo cluster con il seguente AWS CLI comando.

aws rds describe-db-clusters \ --db-cluster-identifier appsync-tutorial \ --query "DBClusters[0].Status"

È possibile creare un Secret utilizzando la AWS Secrets Manager Console o AWS CLI con un file di input come il seguente utilizzando la USERNAME e COMPLEX_PASSWORD del passaggio precedente.

{ "username": "USERNAME", "password": "COMPLEX_PASSWORD" }

Passalo come parametro a AWS CLI:

aws secretsmanager create-secret --name HttpRDSSecret --secret-string file://creds.json --region us-east-1

Verrà restituito un ARN per il segreto.

Prendi nota dell'ARN del cluster Aurora Serverless e di Secret per utilizzarli successivamente nella AppSync console durante la creazione di un'origine dati.

Abilitazione dell'API di dati

È possibile abilitare l'API di dati sul cluster seguendo le istruzioni nella documentazione di RDS. L'API Data deve essere abilitata prima di aggiungerla come fonte di dati. AppSync

Creazione di un database e di una tabella

Dopo aver abilitato la Data API, puoi assicurarti che funzioni con il aws rds-data execute-statement comando contenuto in AWS CLI. Ciò garantirà che il cluster Aurora Serverless sia configurato correttamente prima di aggiungerlo all'API. AppSync Innanzitutto, creare un database denominato TESTDB con il parametro --sql, in questo modo:

aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1" \ --region us-east-1 --sql "create DATABASE TESTDB"

Se la creazione viene eseguita senza errori, aggiungere una tabella con il comando create table:

aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1" \ --region us-east-1 \ --sql "create table Pets(id varchar(200), type varchar(200), price float)" --database "TESTDB"

Se tutto è stato eseguito senza problemi, puoi passare all'aggiunta del cluster come fonte di dati AppSync nell'API.

Schema GraphQL

Ora che l'API di Aurora Serverless è operativa e dispone di una tabella, creeremo uno schema GraphQL e collegheremo i resolver per l'esecuzione di mutazioni e sottoscrizioni. Crea una nuova API nella AWS AppSync console, vai alla pagina Schema e inserisci quanto segue:

type Mutation { createPet(input: CreatePetInput!): Pet updatePet(input: UpdatePetInput!): Pet deletePet(input: DeletePetInput!): Pet } input CreatePetInput { type: PetType price: Float! } input UpdatePetInput { id: ID! type: PetType price: Float! } input DeletePetInput { id: ID! } type Pet { id: ID! type: PetType price: Float } enum PetType { dog cat fish bird gecko } type Query { getPet(id: ID!): Pet listPets: [Pet] listPetsByPriceRange(min: Float, max: Float): [Pet] } schema { query: Query mutation: Mutation }

Salvare lo schema con Save (Salva), accedere alla pagina Data Sources (Origini dati) e creare una nuova origine dati. Selezionare Relational database (Database relazionale) come tipo di origine dati e fornire un nome intellegibile. Utilizzare il nome del database creato nell'ultima fase e l'ARN del cluster in cui tale nome è stato creato. Per il ruolo puoi AppSync creare un nuovo ruolo o crearne uno con una politica simile alla seguente:

JSON
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "rds-data:DeleteItems", "rds-data:ExecuteSql", "rds-data:ExecuteStatement", "rds-data:GetItems", "rds-data:InsertItems", "rds-data:UpdateItems" ], "Resource": [ "arn:aws:rds:us-east-1:123456789012:cluster:mydbcluster", "arn:aws:rds:us-east-1:123456789012:cluster:mydbcluster:*" ] }, { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret", "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret:*" ] } ] }

Esistono due istruzioni in questa policy a cui viene concesso l'accesso basato sul ruolo. La prima risorsa è il cluster Aurora Serverless e la seconda è l'ARN. AWS Secrets Manager Dovrai fornire ENTRAMBI ARNs nella configurazione dell'origine AppSync dati prima di fare clic su Crea.

Passalo come parametro a AWS CLI.

aws secretsmanager create-secret \ --name HttpRDSSecret \ --secret-string file://creds.json \ --region us-east-1

Verrà restituito un ARN per il segreto. Prendi nota dell'ARN del tuo cluster Aurora Serverless e di Secret per dopo quando crei un'origine dati nella console. AWS AppSync

Costruisci la struttura del tuo database

Dopo aver abilitato la tua Data API, puoi assicurarti che funzioni con il aws rds-data execute-statement comando contenuto in AWS CLI. Ciò garantirà che il cluster Aurora Serverless v2 sia configurato correttamente prima di aggiungerlo all'API. AWS AppSync Innanzitutto, crea un database chiamato TESTDB con il --sql parametro seguente.

aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial" \ --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret" \ --region us-east-1 \ --sql "create DATABASE TESTDB"

Se viene eseguito senza errori, aggiungete una tabella con il seguente comando create table.

aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:111122223333:cluster:http-endpoint-test" \ --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333:secret:testHttp2-AmNvc1" \ --region us-east-1 \ --sql "create table Pets(id varchar(200), type varchar(200), price float)" \ --database "TESTDB"

Progetta la tua interfaccia API

Dopo che Aurora Serverless v2 Data API è attiva e funzionante con una tabella, crea uno schema GraphQL e collega i resolver per eseguire mutazioni e sottoscrizioni. Crea una nuova API nella console, vai alla pagina Schema nella AWS AppSync console e inserisci quanto segue.

type Mutation { createPet(input: CreatePetInput!): Pet updatePet(input: UpdatePetInput!): Pet deletePet(input: DeletePetInput!): Pet } input CreatePetInput { type: PetType price: Float! } input UpdatePetInput { id: ID! type: PetType price: Float! } input DeletePetInput { id: ID! } type Pet { id: ID! type: PetType price: Float } enum PetType { dog cat fish bird gecko } type Query { getPet(id: ID!): Pet listPets: [Pet] listPetsByPriceRange(min: Float, max: Float): [Pet] } schema { query: Query mutation: Mutation }

Salvare lo schema con Save (Salva), accedere alla pagina Data Sources (Origini dati) e creare una nuova origine dati. Scegli Database relazionale per il tipo di origine dati e fornisci un nome descrittivo. Utilizzare il nome del database creato nell'ultima fase e l'ARN del cluster in cui tale nome è stato creato. Per il ruolo puoi AWS AppSync creare un nuovo ruolo o crearne uno con una politica simile alla seguente.

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "rds-data:DeleteItems", "rds-data:ExecuteSql", "rds-data:ExecuteStatement", "rds-data:GetItems", "rds-data:InsertItems", "rds-data:UpdateItems" ], "Resource": [ "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster", "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster:*" ] }, { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret", "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret:*" ] } ] }

Esistono due istruzioni in questa policy a cui viene concesso l'accesso basato sul ruolo. La prima risorsa è il cluster Aurora Serverless v2 e la seconda è l'ARN. AWS Secrets Manager Dovrai fornire ENTRAMBI ARNs nella configurazione dell'origine AWS AppSync dati prima di fare clic su Crea.

Connect la tua API alle operazioni del database

Ora che abbiamo uno schema GraphQL valido e un'origine dati RDS, puoi collegare i resolver ai campi GraphQL dello schema. La nostra API offrirà le seguenti funzionalità:

  1. crea un animale domestico usando il campo Mutation.createPet

  2. aggiorna un animale domestico usando il campo Mutation.updatePet

  3. elimina un animale domestico usando il campo Mutation.deletePet

  4. ottenine un singolo utilizzo tramite il campo query.getPET

  5. elenca tutto usando il campo query.listPets

  6. elenca gli animali domestici in una fascia di prezzo utilizzando il comando Query. listPetsByPriceRangecampo

Mutation.createPet

Dall'editor dello schema nella AWS AppSync console, sul lato destro scegli Attach Resolver for. createPet(input: CreatePetInput!): Pet Scegliere l'origine dati RDS. Aggiungere il modello seguente nella sezione request mapping template (modello di mappatura della richiesta):

#set($id=$utils.autoId()) { "version": "2018-05-29", "statements": [ "insert into Pets VALUES (:ID, :TYPE, :PRICE)", "select * from Pets WHERE id = :ID" ], "variableMap": { ":ID": "$ctx.args.input.id", ":TYPE": $util.toJson($ctx.args.input.type), ":PRICE": $util.toJson($ctx.args.input.price) } }

Il sistema esegue le istruzioni SQL in sequenza, in base all'ordine nell'array delle istruzioni. I risultati verranno restituiti nello stesso ordine. Poiché si tratta di una mutazione, eseguirete un'istruzione select dopo l'inserimento per recuperare i valori confermati al fine di popolare il modello di mappatura delle risposte GraphQL.

Aggiungere il modello seguente nella sezione response mapping template (modello di mappatura della risposta):

$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])

Poiché le istruzioni contengono due query SQL, è necessario specificare il secondo risultato nella matrice restituita dal database con: $utils.rds.toJsonString($ctx.result))[1][0]).

Mutation.updatePet

Dall'editor dello schema nella AWS AppSync console, scegli Attach Resolver for. updatePet(input: UpdatePetInput!): Pet Scegli la tua fonte di dati RDS. Nella sezione del modello di mappatura della richiesta, aggiungi il seguente modello.

{ "version": "2018-05-29", "statements": [ $util.toJson("update Pets set type=:TYPE, price=:PRICE WHERE id=:ID"), $util.toJson("select * from Pets WHERE id = :ID") ], "variableMap": { ":ID": "$ctx.args.input.id", ":TYPE": $util.toJson($ctx.args.input.type), ":PRICE": $util.toJson($ctx.args.input.price) } }

Nella sezione modello di mappatura delle risposte, aggiungi il seguente modello.

$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])

Mutation.deletePet

Dall'editor di schemi nella AWS AppSync console, scegli Attach Resolver for. deletePet(input: DeletePetInput!): Pet Scegli la tua fonte di dati RDS. Nella sezione del modello di mappatura della richiesta, aggiungi il seguente modello.

{ "version": "2018-05-29", "statements": [ $util.toJson("select * from Pets WHERE id=:ID"), $util.toJson("delete from Pets WHERE id=:ID") ], "variableMap": { ":ID": "$ctx.args.input.id" } }

Nella sezione modello di mappatura delle risposte, aggiungi il seguente modello.

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])

Query.getPet

Ora che le mutazioni sono state create per il tuo schema, collega le tre query per mostrare come ottenere singoli elementi, elenchi e applicare il filtro SQL. Dall'editor dello schema nella AWS AppSync console, scegli Attach Resolver for. getPet(id: ID!): Pet Scegli la tua fonte di dati RDS. Nella sezione del modello di mappatura della richiesta, aggiungi il seguente modello.

{ "version": "2018-05-29", "statements": [ $util.toJson("select * from Pets WHERE id=:ID") ], "variableMap": { ":ID": "$ctx.args.id" } }

Aggiungere il modello seguente nella sezione response mapping template (modello di mappatura della risposta):

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])

Query.listPets

Dall'editor dello schema nella AWS AppSync console, sul lato destro scegli Attach Resolver for. getPet(id: ID!): Pet Scegli la tua fonte di dati RDS. Nella sezione del modello di mappatura della richiesta, aggiungi il seguente modello.

{ "version": "2018-05-29", "statements": [ "select * from Pets" ] }

Nella sezione modello di mappatura delle risposte, aggiungi il seguente modello.

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])

Interrogazione. listPetsByPriceRange

Dall'editor dello schema nella AWS AppSync console, sul lato destro scegli Attach Resolver for. getPet(id: ID!): Pet Scegli la tua fonte di dati RDS. Nella sezione del modello di mappatura della richiesta, aggiungi il seguente modello.

{ "version": "2018-05-29", "statements": [ "select * from Pets where price > :MIN and price < :MAX" ], "variableMap": { ":MAX": $util.toJson($ctx.args.max), ":MIN": $util.toJson($ctx.args.min) } }

Aggiungere il modello seguente nella sezione response mapping template (modello di mappatura della risposta):

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])

Modifica i tuoi dati tramite l'API

Ora che sono stati configurati tutti i resolver con le istruzioni SQL e l'API GraphQL è stata collegata all'API di dati per Aurora Serverless, è possibile iniziare a eseguire mutazioni e query. Nella AWS AppSync console, scegli la scheda Query e inserisci quanto segue per creare un animale domestico:

mutation add { createPet(input : { type:fish, price:10.0 }){ id type price } }

La risposta dovrebbe contenere l'id, il type (tipo) e il price (prezzo), come indicato di seguito:

{ "data": { "createPet": { "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a", "type": "fish", "price": "10.0" } } }

È possibile modificare questo elemento eseguendo la mutazione updatePet:

mutation update { updatePet(input : { id: ID_PLACEHOLDER, type:bird, price:50.0 }){ id type price } }

Abbiamo utilizzato l'id che è stato restituito dall'operazione createPet precedente. Questo sarà un valore univoco per il record poiché il resolver si è basato su $util.autoId(). È possibile eliminare un record in modo analogo:

mutation delete { deletePet(input : {id:ID_PLACEHOLDER}){ id type price } }

Creare di alcuni record con la prima mutazione con valori diversi per price (prezzo), quindi eseguire alcune query.

Recupera i tuoi dati

Sempre nella scheda Queries della console, usa la seguente istruzione per elencare tutti i record che hai creato.

query allpets { listPets { id type price } }

Sfrutta il predicato SQL WHERE presente where price > :MIN and price < :MAX nel nostro modello di mappatura per Query. listPetsByPriceRangecon la seguente query GraphQL:

query petsByPriceRange { listPetsByPriceRange(min:1, max:11) { id type price } }

Si dovrebbero visualizzare solo i record con price (prezzo) superiore a $1 o inferiore a $10. Infine, è possibile eseguire le query per recuperare singoli record, nel modo seguente:

query onePet { getPet(id:ID_PLACEHOLDER){ id type price } }

Proteggi l'accesso ai dati

L'iniezione SQL è una vulnerabilità di sicurezza nelle applicazioni di database. Si verifica quando gli aggressori inseriscono codice SQL dannoso attraverso i campi di input dell'utente. Ciò può consentire l'accesso non autorizzato ai dati del database. Si consiglia di convalidare e disinfettare attentamente tutti gli input dell'utente prima dell'elaborazione, utilizzandoli variableMap per la protezione dagli attacchi di SQL injection. Se non vengono utilizzate mappe variabili, è responsabilità dell'utente sanificare gli argomenti delle loro operazioni GraphQL. Un possibile modo è fornire fasi di convalida specifiche dell'input nel modello di mappatura della richiesta prima dell'esecuzione di un'istruzione SQL sull'API di dati. Vediamo come possiamo modificare il modello di mappatura della richiesta dell'esempio listPetsByPriceRange. Anziché basarsi esclusivamente sull'input dell'utente, è possibile procedere nel modo seguente:

#set($validMaxPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.maxPrice)) #set($validMinPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.minPrice)) #if (!$validMaxPrice || !$validMinPrice) $util.error("Provided price input is not valid.") #end { "version": "2018-05-29", "statements": [ "select * from Pets where price > :MIN and price < :MAX" ], "variableMap": { ":MAX": $util.toJson($ctx.args.maxPrice), ":MIN": $util.toJson($ctx.args.minPrice) } }

Un altro modo per proteggersi da input anomali durante l'esecuzione di resolver sull'API di dati consiste nell'utilizzare istruzioni preparate assieme a procedure memorizzate e input parametrici. Ad esempio, nel resolver per listPets, definire la procedura seguente che esegue il select come istruzione preparata:

CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END

Crealo nella tua istanza Aurora Serverless v2.

aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:xxxxxxxxxxxx:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:xxxxxxxxxxxx:secret:httpendpoint-xxxxxx" \ --region us-east-1 --database "DB_NAME" \ --sql "CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END"

Il codice del resolver risultante per listPets è semplificato poiché ora è sufficiente chiamare la procedura memorizzata. Come minimo, qualsiasi input di stringa deve avere le virgolette singole tra caratteri di escape.

#set ($validType = $util.isString($ctx.args.type) && !$util.isNullOrBlank($ctx.args.type)) #if (!$validType) $util.error("Input for 'type' is not valid.", "ValidationError") #end { "version": "2018-05-29", "statements": [ "CALL listPets(:type)" ] "variableMap": { ":type": $util.toJson($ctx.args.type.replace("'", "''")) } }

Utilizzo delle stringhe di escape

Usa virgolette singole per contrassegnare l'inizio e la fine delle stringhe letterali in un'istruzione SQL, ad esempio.. 'some string value'. Per consentire l'utilizzo di valori stringa con uno o più caratteri virgolette singole (') all'interno di una stringa, ciascuna virgoletta deve essere sostituita con due virgolette singole (''). Ad esempio, se la stringa di input è Nadia's dog, inserisci il carattere di escape per l'istruzione SQL come segue

update Pets set type='Nadia''s dog' WHERE id='1'