Full-Stack AWS Platform

A Terraform-managed AWS platform for a static web app and private API.

By Himan MandujaSource reviewed Reviewed commit fbc4000 ↗

On this page
  1. A public edge, a private service
  2. Following an API request
  3. Private networking and egress
  4. Three delivery workflows
  5. Security and availability tradeoffs
  6. Source files

This project connects a static Next.js frontend to a private FastAPI service on AWS. Terraform defines the infrastructure, while separate GitHub Actions workflows release the frontend, backend, and infrastructure changes.

The architectural focus is the boundary between public entry points and private application resources. S3 stores the frontend without making the bucket public. API Gateway accepts API traffic without giving the Fargate tasks public IP addresses. The supporting Terraform makes those boundaries inspectable.

A public edge, a private service

CloudFront serves the static export from S3 using Origin Access Control with signed origin requests. S3 public access is blocked. Viewer HTTP requests are redirected to HTTPS, and a CloudFront Function handles path rewriting for the static export.

The public API entry point is an HTTP API Gateway. Its routes connect through a VPC Link to an internal Application Load Balancer. The load balancer forwards to Fargate tasks in private subnets, using IP targets and a /health check. The environment wiring connects the networking, frontend, API, compute, and database modules.

Static delivery and the private AWS API pathThe frontend uses a public API entry point; the ALB and Fargate tasks stay inside the VPC. Components: Browser, Static app + API calls; CloudFront, Origin Access Control; S3, Private static assets; API Gateway, Public HTTP API; VPC Link, Private integration; Internal ALB, Health: /health; Fargate tasks, No public IP; DynamoDB, Gateway endpoint.PRIVATE SUBNETSAPI callsBrowserStatic app + API callsCloudFrontOrigin Access ControlS3Private static assetsAPI GatewayPublic HTTP APIVPC LinkPrivate integrationInternal ALBHealth: /healthFargate tasksNo public IPDynamoDBGateway endpoint
Architecture reconstructed from the linked implementation.Swipe to inspect →
The frontend uses a public API entry point; the ALB and Fargate tasks stay inside the VPC.
AWS API Gateway VPC Link details and private subnet assignments
The captured VPC Link is marked Available and lists two private subnets. It documents the private integration at the time of capture. Repository source ↗

Following an API request

The frontend demo reads its API address from NEXT_PUBLIC_API_URL. A request enters API Gateway, crosses the VPC Link, reaches the internal ALB, and then arrives at the FastAPI process. This keeps public API routing separate from task placement.

The backend implements item creation, reads, updates, and deletion through /items and /items/{id}. DynamoDB records use a key shaped as ITEM#<uuid> with a metadata sort key. This is a small CRUD workload, but it supplies a real request to follow through the infrastructure instead of an architecture made only of AWS service names.

Private networking and egress

The network module creates public and private subnets across the configured availability zones. Fargate tasks do not receive public IP addresses. S3 and DynamoDB gateway endpoints, plus ECR and CloudWatch Logs interface endpoints, provide private paths for several AWS dependencies.

One NAT Gateway sits in the first public subnet, and the private route table uses it for general outbound traffic. This reduces the number of NAT gateways to pay for, but makes egress dependent on that gateway’s availability zone. Multiple private subnets alone do not remove that dependency.

Three delivery workflows

The backend workflow assumes an AWS role through GitHub OIDC, builds commit-tagged and latest images, pushes to ECR, renders a new ECS task definition, and waits for the updated service to stabilize. Using a commit tag supplies a specific artifact identity for the release.

The frontend workflow builds the static site, syncs it to S3, and invalidates CloudFront. It distinguishes long-lived caching for static assets from HTML that should be revalidated. Infrastructure changes have their own Terraform format, initialization, validation, and plan stages; qualifying main-branch pushes apply the plan. Terraform state is stored remotely with locking resources defined through the bootstrap setup.

GitHub Actions backend deployment run with a successful build and deploy job
A recorded backend release includes the container image, cluster, and service in its deployment summary. Repository source ↗

Security and availability tradeoffs

Private tasks, a private bucket, OIDC federation, and a task role scoped to the DynamoDB table are implemented controls. They should be evaluated individually rather than being collapsed into a broad claim of production readiness.

There are still explicit lab assumptions. The backend allows wildcard CORS origins with credentials enabled and includes a comment to restrict that for production. The single NAT remains an egress dependency, and this review did not establish ECS autoscaling. The repository’s CloudFront and API outputs are historical deployment records from December 2025, not a current availability claim.

The project’s value is the complete, source-visible path from a static client to a private service and data store. A next iteration could narrow the browser-origin policy, exercise failure recovery, and justify egress redundancy against the workload’s budget and availability requirements.

Source files