Building a Printful-Integrated T-Shirt E-Commerce Site: Infrastructure, Automation, and Deployment Pipeline

What Was Done

We built a complete e-commerce infrastructure for 86dfrom.com, a print-on-demand t-shirt storefront integrated with Printful's API. The project involved setting up a Next.js 14 application with Stripe payments, configuring AWS S3 + CloudFront for static asset delivery, establishing DNS through Route53, and automating deployment across multiple environments. This post documents the technical decisions, infrastructure patterns, and deployment pipeline that powered the project.

Architecture Overview

The 86dfrom project follows a modern JAMstack pattern with these core components:

  • Frontend: Next.js 14 with API routes for backend logic
  • Payment Processing: Stripe for transactions and webhook handling
  • Inventory: Printful API for product variants and fulfillment
  • Static Hosting: AWS S3 + CloudFront CDN for site delivery
  • DNS: Route53 for domain management and SSL certificate validation
  • CI/CD: Vercel for production deployments with automatic invalidation

Project Structure and File Organization

The development session created a well-organized monorepo structure:

/Users/cb/Documents/repos/sites/86dfrom.com/
├── site/                          # Static HTML/CSS/JS
│   ├── index.html                # Main product page
│   └── success.html               # Post-purchase confirmation
├── gas/                           # Google Apps Script (legacy/backup)
│   ├── Code.gs                    # GAS functions
│   └── appsscript.json           # GAS manifest
├── scripts/
│   └── deploy.sh                  # S3/CloudFront deployment script
└── .env.local                     # Local environment variables

Parallel to this, the Next.js application was built in a separate directory with API routes for:

  • /api/variants — Fetch product variants from Printful
  • /api/orders — Create orders via Printful
  • /api/webhook — Handle Stripe webhook events

Technical Decisions and Rationale

Why Printful Integration?

Printful was chosen for several reasons. First, it handles fulfillment automatically—no inventory management overhead. Second, its API provides real-time product variants, sizes, and colors, allowing dynamic UI updates without manual maintenance. Third, the API's store-level scoping meant we could manage multiple brands (e.g., dangerouscentaur.com and 86dfrom.com) under one account with separate API tokens.

The workflow fetches Printful product variant IDs at build time using a dedicated script, scripts/get-printful-variants.js, which queries the Printful API and writes variant IDs to a configuration file. This avoids runtime API calls for product metadata and improves site performance.

Why S3 + CloudFront Instead of Pure Vercel?

While Vercel handles the Next.js backend, we use S3 + CloudFront for the static site backup for several reasons:

  • Cost: S3 storage and CloudFront egress are cheaper than Vercel bandwidth at scale
  • Control: Direct S3 bucket management allows granular caching policies and versioning
  • Redundancy: If Vercel experiences issues, the static site remains live via CloudFront
  • Flexibility: CloudFront functions can implement custom logic (redirects, geo-blocking, etc.) without redeploying code

DNS and Certificate Management

AWS Certificate Manager (ACM) was used to provision SSL certificates for both 86dfrom.com and the redirect domain 86from.com (typo variant). ACM requires DNS validation—CNAME records added to Route53 to prove domain ownership. This approach is fully automated and certificates renew automatically 30 days before expiration.

Infrastructure Details

S3 Bucket Configuration

An S3 bucket named 86dfrom.com was created with the following configuration:

  • Block Public Access: Disabled (required for CloudFront origin access)
  • Bucket Policy: Restricts access to CloudFront Origin Access Identity (OAI), preventing direct S3 URL access
  • Static Website Hosting: Not enabled (CloudFront serves directly from bucket)
  • Versioning: Enabled for rollback capability

The bucket policy follows this pattern:

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

CloudFront Distribution Setup

Two CloudFront distributions were created:

  1. Primary Distribution (86dfrom.com): Origin set to S3 bucket, accessed via OAI. Cache behaviors configured to cache static assets (images, CSS, JS) for 30 days, with /api/* bypassing cache to hit Vercel backend. Compression enabled for text assets.
  2. Redirect Distribution (86from.com): Uses a CloudFront function (Lambda@Edge equivalent) to redirect all requests to https://86dfrom.com. This handles the common typo domain without additional infrastructure.

Key CloudFront configuration parameters:

  • Viewer Protocol Policy: Redirect HTTP to HTTPS
  • Allowed HTTP Methods: GET, HEAD (POST/PUT routed to Vercel)
  • Cache TTL: 300s minimum, 31536000s maximum (1 year for versioned assets)
  • Origin Shield: Enabled to reduce origin load during traffic spikes

Route53 DNS Configuration

Two A records were created in the Route53 hosted zone:

  • 86dfrom.com → CloudFront distribution alias (primary)
  • 86from.com → CloudFront distribution alias (redirect)

Additionally, CNAME records were added for ACM certificate validation:

  • ACM validation record for 86dfrom.com
  • ACM validation record for 86from.com

Deployment Pipeline

The deployment process follows this sequence:

# 1. Build Next.js application
npm run build

# 2. Deploy to