IaC
TIP
One of the key features is the use of a single JSON schema
not only for code generation, but also for defining the table structure in DynamoDB
using Infrastructure as Code (IaC) tools.
WARNING
Below are some example approaches for working with a JSON schema and IaC tools.
These are just a few options, and the actual implementation may vary significantly depending on your specific needs and conventions within your projects.
GoDyno does not enforce any particular use of IaC tools.
🌍 Terraform
Example of a module for describing a DynamoDB table
bash
project/
├── main.tf
└── modules/
└── dynamodb/
├── dynamo.tf
├── variables.tf
└── outputs.tf
hcl
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
# --- >
module "schema_table" {
source = "./modules/dynamodb"
schema = jsondecode(file("${path.module}/schema.json"))
}
# --- >
output "table_name" {
value = module.schema_table.table_name
}
output "table_arn" {
value = module.schema_table.table_arn
}
hcl
locals {
key_attributes = var.schema.attributes
gsi_indexes = [
for idx in var.schema.secondary_indexes : idx
if try(idx.type, "GSI") == "GSI"
]
lsi_indexes = [
for idx in var.schema.secondary_indexes : idx
if try(idx.type, "LSI") == "LSI"
]
}
# --- >
resource "aws_dynamodb_table" "this" {
name = var.schema.table_name
billing_mode = "PAY_PER_REQUEST"
hash_key = var.schema.hash_key
range_key = var.schema.range_key
dynamic "attribute" {
for_each = local.key_attributes
content {
name = attribute.value.name
type = attribute.value.type
}
}
dynamic "global_secondary_index" {
for_each = local.gsi_indexes
content {
name = global_secondary_index.value.name
hash_key = global_secondary_index.value.hash_key
range_key = try(global_secondary_index.value.range_key, null)
projection_type = global_secondary_index.value.projection_type
non_key_attributes = global_secondary_index.value.projection_type == "INCLUDE" ? global_secondary_index.value.non_key_attributes : null
}
}
dynamic "local_secondary_index" {
for_each = local.lsi_indexes
content {
name = local_secondary_index.value.name
range_key = local_secondary_index.value.range_key
projection_type = local_secondary_index.value.projection_type
non_key_attributes = local_secondary_index.value.projection_type == "INCLUDE" ? local_secondary_index.value.non_key_attributes : null
}
}
tags = {
Name = var.schema.table_name
ManagedBy = "go-dyno"
}
}
hcl
variable "schema" {
type = object({
table_name = string
hash_key = string
range_key = optional(string)
attributes = list(object({
name = string
type = string
}))
common_attributes = optional(list(object({
name = string
type = string
})), [])
secondary_indexes = optional(list(object({
name = string
type = optional(string, "GSI")
hash_key = optional(string)
range_key = optional(string)
projection_type = string
non_key_attributes = optional(list(string), [])
read_capacity = optional(number)
write_capacity = optional(number)
})), [])
})
description = "DynamoDB table schema from go-dyno JSON"
}
hcl
output "table_name" {
description = "Name of the DynamoDB table"
value = aws_dynamodb_table.this.name
}
output "table_arn" {
description = "ARN of the DynamoDB table"
value = aws_dynamodb_table.this.arn
}
output "table_id" {
description = "ID of the DynamoDB table"
value = aws_dynamodb_table.this.id
}
output "hash_key" {
description = "Hash key of the table"
value = aws_dynamodb_table.this.hash_key
}
output "range_key" {
description = "Range key of the table"
value = aws_dynamodb_table.this.range_key
}
Usage
bash
terraform init && terraform apply
Single file example
variable "schema" {
type = object({
table_name = string
hash_key = string
range_key = optional(string)
attributes = list(object({
name = string
type = string
}))
secondary_indexes = optional(list(object({
name = string
type = optional(string, "GSI")
hash_key = optional(string)
range_key = optional(string)
projection_type = string
non_key_attributes = optional(list(string))
read_capacity = optional(number)
write_capacity = optional(number)
})))
})
}
locals {
gsi_indexes = [
for idx in coalesce(var.schema.secondary_indexes, []) : idx
if lookup(idx, "type", "GSI") == "GSI"
]
lsi_indexes = [
for idx in coalesce(var.schema.secondary_indexes, []) : idx
if lookup(idx, "type", "LSI") == "LSI"
]
}
resource "aws_dynamodb_table" "this" {
name = var.schema.table_name
billing_mode = "PAY_PER_REQUEST"
hash_key = var.schema.hash_key
range_key = var.schema.range_key
dynamic "attribute" {
for_each = var.schema.attributes
content {
name = attribute.value.name
type = attribute.value.type
}
}
dynamic "global_secondary_index" {
for_each = local.gsi_indexes
content {
name = global_secondary_index.value.name
hash_key = global_secondary_index.value.hash_key
range_key = lookup(global_secondary_index.value, "range_key", null)
projection_type = global_secondary_index.value.projection_type
read_capacity = global_secondary_index.value.read_capacity
write_capacity = global_secondary_index.value.write_capacity
non_key_attributes = global_secondary_index.value.projection_type == "INCLUDE" ? global_secondary_index.value.non_key_attributes : null
}
}
dynamic "local_secondary_index" {
for_each = local.lsi_indexes
content {
name = local_secondary_index.value.name
range_key = local_secondary_index.value.range_key
projection_type = local_secondary_index.value.projection_type
non_key_attributes = local_secondary_index.value.projection_type == "INCLUDE" ? local_secondary_index.value.non_key_attributes : null
}
}
}
Usage
bash
# for example, the schema is passed dynamically via an environment variable
export TF_VAR_schema=$(cat schema.json)
terraform init && terraform apply