March of the titan
AWS continues its march across the Infrastructure-as-a-Service (Iaas) world with new services coming out each month. When evaluating these services, Engineers and Architects are called to make difficult decisions that balance vendor lock-in, standards, likely future adoption, maintainability and cost.
Function over form
Crucially the need to deliver a product, quickly, reliably and scalably trumps the academic pursuit of the perfect tech or new-phoric love of the latest trend. If there’s an AWS service - read global, pay-as-you-go, massively scalable, low barrier to entry - that helps you to do what you’re trying to do online, then surely it’s worth evaluating.
Embrace the new
AppSync promises to do exactly that. Coupled with some good infrastructure-as-code, it reduces the complexity associated with setting up a GraphQL API and backing datastore to a few well ordered lines of Terraform code.
AppSync
GraphQL API
Let’s start with a basic type in GraphQL
# Docs
type Doc {
created: Int!
creatorAccountId: String!
id: ID!
body: String!
}
then a connection to read streams of them and some CRUD param lists
type DocConnection {
items: [Doc]
nextToken: String
}
input CreateDocInput {
creatorAccountId: String!
body: String!
}
input UpdateDocInput {
id: ID!
body: String
}
input DeleteDocInput {
id: ID!
}
We need a query to read the latest docs, mutations to update them and subscriptions to listen for changes:
type Query {
getLatestDocs(
creatorAccountId: String,
limit: Int,
nextToken: String
): DocConnection
}
type Mutation {
createDoc(input: CreateDocInput!): Doc
updateDoc(input: UpdateDocInput!): Doc
deleteDoc(input: DeleteDocInput!): Doc
}
type Subscription {
onCreateDoc(creatorAccountId: String!): Doc
@aws_subscribe(mutations: ["createDoc"])
onUpdateDoc(id: ID, body: String): Doc
@aws_subscribe(mutations: ["updateDoc"])
onDeleteDoc(id: ID, body: String): Doc
@aws_subscribe(mutations: ["deleteDoc"])
}
Finally we want a top-level schema to tell GraphQL about all these bits:
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
Terraform
Next we’re going to need some Terraform code to provision it. First we create an AppSync API:
provider "aws" {
region = "eu-west-1"
alias = "ireland"
}
resource "aws_appsync_graphql_api" "grapple" {
name = "grapple-${terraform.workspace}-${var.project_name}"
authentication_type = "API_KEY"
provider = "aws.ireland"
}
At the time of writing AppSync wasn’t available in eu-west-2 (London) so we’re using eu-west-1 (Ireland). Now we need an API key to access it:
resource "aws_appsync_api_key" "api_key" {
api_id = "${aws_appsync_graphql_api.grapple.id}"
# API keys have a maximum validity period of 365 days
expires = "2020-01-04T13:00:00Z"
provider = "aws.ireland"
}
Once we’ve got an API, we can upload our new schema to it:
# upload schema from template file
data "template_file" "grapple_schema" {
template = "${file("${path.root}/../../appsync/grapple/schema.graphql")}"
}
resource "null_resource" "grapple_upload_schema" {
provisioner "local-exec" {
command = "aws appsync start-schema-creation --region ${var.aws_region_appsync} --api-id ${aws_appsync_graphql_api.grapple.id} --definition '${data.template_file.grapple_schema.rendered}'"
}
# trigger based on changes to the schema
triggers = {
file_changes = "${md5(data.template_file.grapple_schema.rendered )}"
}
depends_on = ["aws_appsync_graphql_api.grapple"]
}
The use of a trigger here means that whenever our API file changes locally, it will be re-uploaded to AWS in the next terraform apply
.
To be continued
If you want to see more of this kind of material, just drop me an email and I’d be happy to expand it here.
Leave a comment