Building 86dfrom.com: Next.js E-Commerce + Google Apps Script Integration with AWS Infrastructure

What Was Done

Deployed a full-stack t-shirt e-commerce site for 86dfrom.com integrating Next.js 14 frontend, Stripe payments, Printful print-on-demand fulfillment, and Google Apps Script backend automation. The infrastructure spans Vercel (application hosting), AWS S3 + CloudFront (static assets and redirects), Route53 (DNS), and Google Apps Script (order webhook processing).

Architecture Overview

The project uses a hybrid deployment model:

  • Application tier: Next.js 14 on Vercel with five compiled API routes handling product variants, Stripe webhooks, and order confirmation
  • Payment processing: Stripe for card transactions with webhook validation at /api/webhook
  • Print fulfillment: Printful API integration for variant retrieval and order creation via scripts/get-printful-variants.js
  • Order persistence: Google Apps Script (GAS) deployed as web app, receiving POST requests from Vercel to log orders into Google Sheets
  • Static hosting: Parallel S3 + CloudFront deployment for 86from.com (note: different domain spelling) with CloudFront Functions handling HTTP→HTTPS redirect

File Structure and Key Components

The project directory layout reflects this separation:

/Users/cb/Documents/repos/sites/86dfrom.com/
├── site/
│   ├── index.html          (static redirect/marketing page)
│   └── success.html        (order confirmation page)
├── gas/
│   ├── Code.gs             (Google Apps Script backend)
│   ├── appsscript.json     (GAS manifest with scopes)
│   └── .clasp.json         (Clasp deployment config)
├── scripts/
│   └── deploy.sh           (S3 + CloudFront deployment automation)
└── .env.local              (environment variables—not committed)

The Next.js application (deployed separately to Vercel) contains:

app/
├── page.tsx                (homepage with product grid)
├── api/
│   ├── variants/           (GET Printful variant IDs)
│   ├── create-checkout/    (POST initiate Stripe session)
│   ├── webhook/            (POST Stripe webhook handler)
│   └── success/            (POST order confirmation)
└── layout.tsx              (shared metadata + fonts)

Technical Decisions and Rationale

Why Google Apps Script Instead of a Database

Orders are logged to Google Sheets via Apps Script rather than a dedicated database. This reduces operational overhead—no database provisioning, backups, or schema migrations. Google Sheets serves as both the data store and the audit log, accessible to non-technical team members. The GAS Code.gs file exposes an HTTP endpoint that validates incoming requests and appends rows to a spreadsheet.

Dual Domain Strategy (86dfrom.com vs 86from.com)

The production e-commerce site lives at 86dfrom.com (Vercel), while a static redirect site runs at 86from.com (S3 + CloudFront) to catch typos. The redirect uses CloudFront Functions—a lightweight compute layer that intercepts requests before they hit S3, eliminating the need for S3 website hosting or Lambda@Edge. This approach costs less and has lower latency than Lambda-based redirects.

Variant ID Population via Script

Rather than hardcoding Printful variant IDs in the codebase, scripts/get-printful-variants.js queries the Printful API at deploy time. The script fetches the product (Bella+Canvas 3001 Black t-shirt) and extracts variant IDs (sizes 4016–4020), writing them to a JSON file that the build process consumes. This decouples product catalog changes from code deployments.

AWS Infrastructure for Static Content

Static assets (site/index.html, site/success.html`) are deployed to S3 bucket 86from-com-static and served through CloudFront distribution. S3 bucket policies restrict all access to CloudFront's origin access identity (OAI), preventing direct bucket access. This pattern ensures:

  • DDoS protection via CloudFront's edge locations
  • HTTPS-only traffic with ACM certificates
  • HTTP→HTTPS redirect via CloudFront Functions without additional servers
  • Cache invalidation automation post-deploy

Infrastructure Details

S3 Bucket Configuration

Bucket name: 86from-com-static

Bucket policy grants only CloudFront's OAI read access:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity [OAI-ID]"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::86from-com-static/*"
    }
  ]
}

This prevents accidental public access while allowing CloudFront to fetch objects.

CloudFront Distributions

Primary distribution (86from.com → S3):

  • Origin: S3 bucket 86from-com-static (not website endpoint—bucket endpoint via OAI)
  • ACM certificate: 86from.com with DNS validation via Route53
  • Default root object: index.html
  • Cache behaviors: Default TTL 86400s (1 day) for marketing pages
  • Compress: Enabled for text assets (HTML, CSS)

Redirect distribution (86from.com HTTP→HTTPS):

  • Origin: Custom origin (no S3 origin—headers would be ineffective)
  • CloudFront Function: Evaluates request URI and returns 301/302 redirect to HTTPS
  • Viewer protocol policy: HTTP and HTTPS allowed (function handles redirect)

Route53 DNS

Hosted zone for 86from.com contains:

  • 86from.com A record → CloudFront distribution alias (static content)
  • www.86from.com CNAME86from.com (optional, typically not used for redirects)
  • ACM validation CNAMEs (temporary, removed after certificate issuance)

Primary domain 86dfrom.com (production e-commerce) is configured separately with Vercel's nameservers or as a CNAME to Vercel's provided endpoint.

Google Apps Script Deployment

File gas/.clasp.json contains Clasp