OVO Tech Blog
OVO Tech Blog

Our journey navigating the technosphere

Lucas is a full stack developer who enjoys writing software for a living. Outside of work, he enjoys developing personal projects and drawing.

Share


Tags


Deploying AppSync with Terraform

On the 28th November ’17 AWS introduced the world to AppSync, its new fully managed GraphQL service that bridges the gap between your front-end and data-ladened backend. Most notably its features include data synchronisation (no more polling!) and offline capabilities.

However long after its launch Terraform’s AWS Provider still doesn’t have all the resources necessary to deploy a complete instance of AppSync (although they are very close to being released). Leaving some developers turning to Serverless, manually intervening after deployments or using external scripts to make up for this short-fall.

appsync-diagram

The following shows how use Terraform’s cloudformation_stack resource to create the missing resources - removing the need for the aforementioned workarounds and making future migration a doddle.

If you’d prefer to jump into a complete demonstration then head over to GitHub - SketchingDev/terraform-appsync-demo.

What AppSync resources are supported

Currently the AWS Provider supports deploying the API, an authentication key and data sources, but if you actually want to resolve a request you’re going to need a Schema and Resolver, which are both missing.

resource "aws_appsync_graphql_api" "people" {
  name                = "${var.appsync_name}"
  authentication_type = "API_KEY"
}

resource "aws_appsync_api_key" "people_api" {
  api_id  = "${aws_appsync_graphql_api.people.id}"
}

resource "aws_appsync_datasource" "people" {
  api_id           = "${aws_appsync_graphql_api.people.id}"
  name             = "${var.datasource_name}"
  service_role_arn = "${aws_iam_role.api.arn}"
  type             = "AMAZON_DYNAMODB"

  dynamodb_config {
    table_name = "${aws_dynamodb_table.people.name}"
  }
}

CloudFormation to the rescue

Luckily Terraform’s AWS Provider offers us a lifeline for dealing with missing resources thanks to its aws_cloudformation_stack resource - allowing us to harness CloudFormation templates within Terraform.

In case you’ve not come across CloudFormation before it is AWS’s native infrastructure-as-code offering that - just like Terraform - allows you to automate the provisioning of AWS services via ‘templates’. Importantly though since it is owned by AWS it supports most (if not all) of their services.

Downsides

Unfortunately though little in life comes without a price and for us it’s that Terraform hands responsibility of creation, updating and destruction to CloudFormation with some unfortunate consequences:

Terraforming the missing pieces

The aws_cloudformation_stack resource has two main arguments, the CloudFormation template and its parameters. In the example below the templates (which can be seen here) define AppSync’s schema and resolver and are stored alongside the modules files. They can then be referenced using the local_file data source, reducing duplication between resolvers.

It’s then just a case of providing the parameters and any hidden dependencies via the depends_on meta-argument.

AppSync Schema

data "local_file" "cloudformation_schema_template" {
  filename = "${path.module}/cloudformation-templates/schema.json"
}

data "local_file" "schema" {
  filename = "${path.module}/people-api/schema.graphql"
}

resource "aws_cloudformation_stack" "api_schema" {
  depends_on = ["aws_appsync_datasource.people"]
  name = "${var.appsync_name}-schema"

  parameters = {
    graphQlApiId = "${aws_appsync_graphql_api.people.id}"
    graphQlSchema = "${data.local_file.schema.content}"
  }

  template_body = "${data.local_file.cloudformation_schema_template.content}"
}

AppSync Resolver

data "local_file" "create_source_request_mapping" {
  filename = "${path.module}/people-api/resolvers/createPerson-request-mapping-template.txt"
}

data "local_file" "create_source_response_mapping" {
  filename = "${path.module}/people-api/resolvers/createPerson-response-mapping-template.txt"
}

resource "aws_cloudformation_stack" "create_person_resolver" {
  depends_on = [
    "aws_appsync_datasource.people",
    "aws_cloudformation_stack.api_schema"
  ]
  name = "${var.appsync_name}-create-person-resolver"

  parameters = {
    graphQlApiId = "${aws_appsync_graphql_api.people.id}"
    dataSourceName = "${var.datasource_name}"
    fieldName = "createPerson"
    typeName = "Mutation"
    requestMappingTemplate = "${data.local_file.create_source_request_mapping.content}"
    responseMappingTemplate = "${data.local_file.create_source_response_mapping.content}"
  }

  template_body = "${data.local_file.cloudformation_resolver_template.content}"
}

Deploying your API

You can now deploy your API with the usual terraform apply command:

$ terraform apply -auto-approve

...

Apply complete! Resources: 9 added, 0 changed, 0 destroyed.

Outputs:

api_key = <sensitive>
api_uris = {
  GRAPHQL = https://s3tfchjklnfz7d76rqqqfvjf7m.appsync-api.us-east-1.amazonaws.com/graphql
}

Check out a complete example of this at GitHub - SketchingDev/terraform-appsync-demo.

This article on deploying AppSync with Terraform was originally written for my personal blog, where I'll be writing more articles of this nature. If you have any suggestions on how I can improve it, or my writing then please let me know @SketchingDev, or leave a comment.

Lucas is a full stack developer who enjoys writing software for a living. Outside of work, he enjoys developing personal projects and drawing.

View Comments