Skip to content

Repository files navigation

pokemon-tracker-api

CI

A Kotlin/Spring Boot REST API for tracking captured Pokemon, deployed to AWS (EC2, RDS) with Terraform and Docker.

Live demo

Deployed on the EC2 setup described below and left running (see Deploying to AWS) rather than torn down between visits, so the link should work whenever you click it.

Running locally

./gradlew bootRun

Requires a local Postgres reachable at localhost:5432/appdb (see src/main/resources/application.properties) and a DB_PASSWORD environment variable.

Deploying to AWS

Infrastructure lives under infra/, one subdirectory per deployment target so alternate approaches can coexist without colliding. This one, infra/ec2/, is deliberately minimal/cheap:

  • EC2 (t3.micro, default VPC) runs the API container via Docker Compose. Managed through SSM Session Manager — there's no open SSH port.
  • RDS Postgres (db.t3.micro, single-AZ, 20GB) — free-tier sized, not publicly accessible, reachable only from the EC2 instance's security group.
  • ECR holds the built API image.
  • The DB password is generated by Terraform, stored in SSM Parameter Store (SecureString), and read by the instance at deploy time via its IAM role — never embedded in code or committed anywhere.
  • An Elastic IP keeps the public address stable across redeploys/instance replacement, so the link above doesn't break.

This is intentionally not a "real" production setup (single instance, no HTTPS/custom domain, no autoscaling, no remote Terraform state) — see Possible next steps.

Prerequisites

  • terraform, aws CLI, and docker installed locally
  • AWS credentials configured (aws sts get-caller-identity should work)

First deploy

cd infra/ec2
terraform init
terraform plan     # review what will be created
terraform apply

cd ..
./scripts/build_and_push.sh   # build the image and push to ECR
./scripts/redeploy.sh         # tell the running instance to pull it and restart

terraform apply provisions the EC2 instance before an image exists in ECR, so the instance's first boot has nothing to pull — that's expected. build_and_push.sh + redeploy.sh complete the deploy.

The API's public URL is printed as the api_url Terraform output:

terraform -chdir=infra/ec2 output api_url
curl "$(terraform -chdir=infra/ec2 output -raw api_url)/pokemon"

Redeploying after a code change

./scripts/build_and_push.sh
./scripts/redeploy.sh

Tearing down

cd infra/ec2
terraform destroy

Everything (EC2, RDS, ECR repo + images, SSM parameter, IAM role, Elastic IP) is destroyable in one step — nothing has deletion protection or a final-snapshot requirement. The demo above is intentionally left running for now; only tear it down once you're done using it as a live reference.

Possible next steps

  • Remote Terraform state (S3 + DynamoDB lock) instead of local state
  • Custom domain + HTTPS: point a subdomain (e.g. pokemon-api.davenotdavid.com) at the Elastic IP and run Caddy as a reverse proxy on the instance for automatic Let's Encrypt certs — cheaper than an ALB + ACM for a single-instance setup
  • Continuous deployment: extend CI to run build_and_push.sh / redeploy.sh automatically on push to main
  • Multi-AZ RDS + automated backups once this is more than a demo

About

REST API to track captured Pokemon - Kotlin, Spring Boot, Postgres, Docker, and deployed to AWS via Terraform.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages