AWS AppSync: Desenvolvimento centrado em código com AWS CloudFormation
O AWS AppSync é um serverless GraphQL backend-as-a-service. A maioria dos artigos online mostra seu uso através do console da AWS e configurando via interface web, o que não é uma abordagem centrada em código. Eu gostaria de mostrar uma abordagem orientada a código, empregando uma técnica chamada Infraestrutura como Código usando o AWS CloudFormation. O CloudFormation permite que você especifique todos os recursos da AWS na forma de código e automatize suas implantações por meio da linha de comando. Desta forma, todos os recursos de infraestrutura necessários para o seu aplicativo se tornam parte do repositório git — controlados por versão. Você pode reproduzir a stack inteira (conjunto de recursos relacionados) na nuvem com um único comando, ao invés de repetir as etapas manualmente por meio da GUI. Depois que a stack inicial é criada, você pode implantar facilmente alterações em sua infraestrutura, modificando o arquivo de modelo e, em seguida, executando comandos por meio da linha de comando. Essa abordagem oferece controle total direto por meio de código e é muito flexível para se integrar ao restante do ecossistema da AWS.
Mostrarei passo a passo como criar e implantar uma API AppSync GraphQL de nível de produção usando o DynamoDB, tudo a partir do conforto de seu editor e terminal.
1. Definindo o esquema GraphQL
Iremos criar um esquema de exemplo com uma consulta para obter contatos e uma mutação para adicionar um novo contato e salvar o arquivo como schema.graphql no diretório principal do projeto.
mkidr exemplo-appsync
cd exemplo-appsync
touch schema.graphql
vim schema.graphql
Adicionamos:
type Contact {
username: String!
fullName: String!
email: String!
phone: String
}input ContactInput {
fullName: String!
email: String!
phone: String
}type Query {
getContacts: [Contact!]!
}type Mutation {
addContact(username: String! contact: ContactInput!): Contact
}type Schema {
query: Query
mutation: Mutation
}
2. Adicionar modelos de mapeamento para os Resolvers
O modelo de mapeamento de resolução informa ao AppSync como converter uma solicitação de GraphQL recebida em instruções para sua fonte de dados do back-end e como traduzir a resposta dessa origem de dados de volta para uma resposta do GraphQL. Criaremos modelos de mapeamento de solicitação e resposta separados em arquivos diferentes para cada resolver.
$ mkdir mapping-templates
$ cd mapping-templates
$ touch addContact.request.vm
$ touch addContact.response.vm
$ touch getContacts.request.vm
$ touch getContacts.response.vm
Em addContact.request.vm
, adicione:
## mapeamento de resolução da requisição da mutação `addContact`, mapeando para a operação `PutItem` do DynamoDB.
{
"version": "2017-02-28",
"operation": "PutItem",
"key": {
"username": $util.dynamodb.toDynamoDBJson($ctx.args.username)
},
"attributeValues": $util.dynamodb.toMapValuesJson($ctx.args.contact)
}
Em addContact.response.vm
, adicione:
## mapeamento de resolução da resposta da mutação `addContact`, retornando o novo objeto de contato adicionado.
$util.toJson($ctx.result)
Em getContacts.request.vm
, adicione:
## mapeamento de resolução da requisição da query `getContacts`, mapeando para a operação `Scan` do DynamoDB.
{
"version": "2017-02-28",
"operation": "Scan"
}
Em getContacts.response.vm
, adicione:
## mapeamento de resolução da resposta da query `getContacts`, retornando todos os contatos.
$util.toJson($ctx.result.items)
3. Especifique recursos no modelo do CloudFormation
Aqui criaremos o modelo CloudFormation no formato YAML, que é mais legível do que sua contraparte JSON. Ele especifica detalhes sobre nossa AppSync API, API Key, o esquema GraphQL, a tabela do DynamoDB, nossas Fontes de Dados para a API AppSync, os Resolvers do GraphQL e o acesso à Tabela do DynamoDB usando uma IAM Role.
$ touch template.yml
$ vim template.yml
E em seguida:
AWSTemplateFormatVersion: '2010-09-09'
Description: Create AppSync GraphQL API using DynamoDBParameters: ApiName:
Type: String
Description: Name of the API - used to generate unique names for resources
MinLength: 3
MaxLength: 25
AllowedPattern: '^[a-zA-Z][a-zA-Z0-9\-]*$'Resources: GraphQLApi:
Type: AWS::AppSync::GraphQLApi
Properties:
Name: !Sub ${ApiName}-graphql-api
AuthenticationType: API_KEY ApiKey:
Type: AWS::AppSync::ApiKey
Properties:
ApiId: !GetAtt GraphQLApi.ApiId GraphQLSchema:
Type: AWS::AppSync::GraphQLSchema
Properties:
ApiId: !GetAtt GraphQLApi.ApiId
# caminho para o esquema graphql
DefinitionS3Location: schema.graphql ContactsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub ${ApiName}-contacts
KeySchema:
- AttributeName: username
KeyType: HASH
AttributeDefinitions:
- AttributeName: username
AttributeType: S
BillingMode: PAY_PER_REQUEST ApiRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- appsync.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: !Sub ${ApiName}-dynamo-access
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:DeleteItem
- dynamodb:UpdateItem
- dynamodb:Query
- dynamodb:Scan
- dynamodb:BatchGetItem
- dynamodb:BatchWriteItem
Resource: !GetAtt ContactsTable.Arn ContactsTableDataSource:
Type: AWS::AppSync::DataSource
Properties:
Name: ContactsTableDataSource
Type: AMAZON_DYNAMODB
ServiceRoleArn: !GetAtt ApiRole.Arn
DynamoDBConfig:
AwsRegion: !Ref AWS::Region
TableName: !Ref ContactsTable
ApiId: !GetAtt GraphQLApi.ApiId QueryGetContactsResolver:
Type: AWS::AppSync::Resolver
Properties:
TypeName: Query
FieldName: getContacts
DataSourceName: !GetAtt ContactsTableDataSource.Name
# caminho para o arquivo de mapeamento de resolução da requisição
RequestMappingTemplateS3Location: mapping-templates/getContacts.request.vm
# caminho para o arquivo de mapeamento de resolução da resposta
ResponseMappingTemplateS3Location: mapping-templates/getContacts.response.vm
ApiId: !GetAtt GraphQLApi.ApiId MutationAddContactResolver:
Type: AWS::AppSync::Resolver
Properties:
TypeName: Mutation
FieldName: addContact
DataSourceName: !GetAtt ContactsTableDataSource.Name
# caminho para o arquivo de mapeamento de resolução da requisição
RequestMappingTemplateS3Location: mapping-templates/addContact.request.vm
# caminho para o arquivo de mapeamento de resolução da resposta
ResponseMappingTemplateS3Location: mapping-templates/addContact.response.vm
ApiId: !GetAtt GraphQLApi.ApiIdOutputs: GraphQLApiEndpoint:
Description: GraphQL API URL
Value: !GetAtt GraphQLApi.GraphQLUrl ApiKey:
Description: API Key
Value: !GetAtt ApiKey.ApiKey
4. Deploy usando o AWS CLI
Primeiro, instale o AWS CLI e configure-o especificando as credenciais de segurança e as informações de região da sua conta. Forneça a opção de perfil em todos os comandos se você quiser usar uma configuração de perfil específica, caso contrário, usará o perfil padrão.
Crie um bucket do S3 para fazer o upload de arquivos de código:
aws s3 mb s3://contacts-dev-codes --profile faisal
Execute o comando package para empacotar os artefatos locais referenciados no arquivo CloudFormation que criamos, template.yml, para o bucket S3 especificado. Isso gerará um novo arquivo, que se irá usar referências aos arquivos no bucket S3 ao invés dos arquivos locais.
aws cloudformation package \
--s3-bucket contacts-dev-codes \
--template-file template.yml \
--output-template-file packaged-template.yml \
--profile faisal
Execute o comando deploy utilizando o arquivo recém-gerado para criar todos os recursos de infraestrutura. Além disso, não esqueça de fornecer o nome da stack e os parâmetros necessários do template:
aws cloudformation deploy \
--stack-name contacts-dev \
--template-file packaged-template.yml \
--capabilities CAPABILITY_IAM \
--parameter-overrides ApiName=contacts-dev \
--profile faisal
Agora você tem uma API AppSync GraphQL totalmente funcional com DynamoDB na AWS. Você pode usar os comandos package e deploy repetidamente sempre que fizer alterações no template, ele criará automaticamente o conjunto de mudanças e o executará. Se você estiver usando o Node.js, poderá adicionar esses comandos como scripts do NPM ao package.json de um projeto, por conveniência.
Eu criei o repositório GitHub que contém todo o código padrão com a configuração completa necessária para o kickstart do projeto AppSync.
Crédito
- AWS AppSync code-centric development using CloudFormation, escrito originalmente por Faisal Hasnain