Building a Printful-Integrated T-Shirt Commerce Site: Infrastructure Setup for 86dfrom.com
What Was Done
This session focused on establishing the complete infrastructure stack for 86dfrom.com, a Next.js 14 e-commerce application that integrates Printful's API for on-demand t-shirt fulfillment. We scaffolded a production-ready environment spanning AWS S3, CloudFront, Route53, ACM certificates, Google Apps Script for webhook handling, and Vercel deployment—all coordinated through environment configuration and shell automation.
Project Architecture Overview
The project structure separates concerns into three distinct domains:
- Frontend (
/site): Static HTML landing page with embedded Stripe payment form and product variant selection - Backend (
/gas): Google Apps Script deployment handling webhook callbacks from Printful and Stripe - Deployment (
/scripts): Bash automation for S3 sync, CloudFront invalidation, and environment setup
The Next.js application runs on Vercel for the dynamic API routes (/api/variants, /api/checkout, /api/webhook), while the static site and Google Apps Script handle supplementary responsibilities.
Infrastructure Stack Decisions
AWS S3 + CloudFront for Static Assets
While the main application lives on Vercel, we created an S3 bucket (86dfrom.com) paired with CloudFront distribution for potential static content distribution and as a reference pattern. The bucket policy restricts public access and uses Origin Access Control (OAC) to allow CloudFront reads only—a security best practice that prevents direct S3 URL enumeration.
Why this approach: Separates content delivery (CloudFront CDN at edge locations) from origin (S3), providing DDoS mitigation, caching headers control, and geographic redundancy without exposing bucket URLs.
Route53 for DNS Management
All domains (86dfrom.com and the redirect alias 86from.com) use Route53 hosted zones for centralized DNS control. This enables programmatic DNS validation during ACM certificate issuance and allows for future routing policies (geolocation, latency-based, failover).
DNS structure:
86dfrom.com → CloudFront distribution ALIAS record
86from.com → CloudFront redirect distribution ALIAS record
*.86dfrom.com → Inherited from zone apex (optional wildcard)
ACM Certificates with DNS Validation
We requested two ACM certificates:
86dfrom.com: Primary domain certificate86from.com: Typo-variant redirect certificate
Both use DNS validation (not email), which is automated via Route53 CNAME records. This approach avoids manual email confirmation and integrates cleanly into CI/CD workflows.
Validation process:
- Request ACM certificate for domain
- Extract DNS validation CNAME records from AWS console
- Add CNAME records to Route53 hosted zone
- Poll ACM API until certificate status changes to
ISSUED
CloudFront Distribution with Lambda@Edge Redirect Function
The redirect from 86from.com to 86dfrom.com is handled by a CloudFront Function (not Lambda@Edge—lighter weight) that inspects the Host header and issues a 301 permanent redirect if the request matches 86from.com.
Function code pattern:
function handler(event) {
var request = event.request;
var headers = request.headers;
if (headers['host'] && headers['host'].value.includes('86from.com')) {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
'location': { value: 'https://86dfrom.com' + request.uri }
}
};
}
return request;
}
Why CloudFront Functions: Sub-millisecond latency (executes before CloudFront cache lookup), no cold starts, integrated billing. Lambda@Edge would be overkill for simple redirects.
Deployment Automation
The scripts/deploy.sh script encapsulates the S3 sync and CloudFront cache invalidation pattern:
#!/bin/bash
set -e
export AWS_PROFILE=default
export DISTRIBUTION_ID="E1A2B3C4D5E6F7" # CloudFront dist ID
aws s3 sync ./site s3://86dfrom.com --delete --region us-east-1
aws cloudfront create-invalidation --distribution-id $DISTRIBUTION_ID --paths "/*"
echo "Deployment complete"
This script:
- Syncs the
./sitedirectory to the S3 bucket, removing files not present locally - Creates a CloudFront invalidation for all paths (
/*) to purge edge cache - Requires AWS credentials configured via environment variables or
~/.aws/credentials
Environment Configuration
The .env.local file (not committed to git) contains:
NEXT_PUBLIC_PRINTFUL_STORE_ID: Store identifier for Printful API callsPRINTFUL_API_KEY: Secret key for server-side variant fetchingSTRIPE_SECRET_KEY: Live or test secret key for payment processingNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: Client-side key for Stripe.jsSTRIPE_WEBHOOK_SECRET: Signing secret for webhook verification (added post-deployment)
The NEXT_PUBLIC_* prefix exposes variables to the browser safely (no secrets), while others remain server-side only.
Google Apps Script Integration
The gas/Code.gs file handles webhook callbacks that Stripe or Printful cannot directly post to Vercel due to network restrictions or for augmented logging. The appsscript.json manifest defines the deployment as a web app with execute-as permissions.
Why Apps Script: No infrastructure to manage, built-in logging to Google Sheets, easy to modify without redeploying code, and integrates with Google Workspace for audit trails.
Next Steps
Before production launch:
- Populate variant IDs: Run the Printful API integration script to fetch Bella+Canvas 3001 Black variant IDs (sizes S–3XL) and validate them against the store.
- Vercel Deployment: Deploy to production via
npx vercel@latest --prod, adding all environment variables to Vercel's Project Settings. - Webhook Configuration: Create a webhook endpoint at