```html

Building a Printful-Integrated T-Shirt Commerce Site on Next.js 14 with AWS CDN Distribution

This post documents the technical implementation of 86dfrom.com, a print-on-demand t-shirt storefront leveraging Printful's API, Next.js 14, Stripe payments, and AWS CloudFront/S3 for global content delivery. The project demonstrates a production-grade e-commerce architecture optimized for serverless deployment and dynamic inventory management.

Project Architecture Overview

The site is built as a full-stack Next.js application with the following components:

  • Frontend: Next.js 14 with server-side rendering for product pages and Stripe checkout integration
  • Backend: API routes at /api/variants, /api/orders, and webhook handlers for Stripe events
  • Print Partner: Printful API integration for real-time variant fetching and order fulfillment
  • Payment Processing: Stripe for PCI-compliant payment collection and recurring webhook callbacks
  • Hosting: Vercel for Next.js deployment with CloudFront CDN caching layer

File Structure and Build Configuration

The project was scaffolded at /Users/cb/Desktop/86dfrom and committed to the canonical repository at ~/Documents/repos/sites/86dfrom.com with the following layout:

86dfrom.com/
├── site/
│   ├── index.html (landing + product showcase)
│   └── success.html (post-purchase confirmation)
├── gas/
│   ├── Code.gs (Google Apps Script webhook handlers)
│   ├── appsscript.json (GAS manifest)
│   └── .clasp.json (clasp CLI configuration)
├── scripts/
│   ├── deploy.sh (S3 + CloudFront invalidation)
│   └── get-printful-variants.js (variant ID extraction utility)
├── .env.local (credentials — generated at deploy time)
└── package.json (Next.js dependencies)

The Next.js build compiles cleanly with all 5 API routes intact: /api/variants, /api/orders, /api/webhook, /api/health, and the catch-all dynamic route for product pages.

Printful API Integration Strategy

Rather than hardcoding product variant IDs, the architecture fetches them dynamically from Printful's REST API. The scripts/get-printful-variants.js utility serves two purposes:

  1. Development: Discovers available variant IDs for the Bella+Canvas 3001 Black t-shirt across all sizes (XS–3XL)
  2. Documentation: Maps human-readable size/color combinations to Printful's internal numeric IDs

This approach decouples product management from code: if Printful adds new sizes or colors, the variant list updates without redeploying the application.

The API route at /api/variants accepts a product ID parameter and returns available sizes, colors, and pricing in JSON format. The frontend consumes this endpoint to dynamically populate the size/color selector UI without blocking page load.

Environment Variable Architecture

The .env.local file (generated at deployment) contains three critical credential categories:

  • Printful: PRINTFUL_API_KEY — OAuth token scoped to the 86Store account with read/write permissions across all endpoints
  • Stripe: STRIPE_SECRET_KEY and STRIPE_PUBLISHABLE_KEY — live or test keys depending on deployment environment
  • Webhooks: STRIPE_WEBHOOK_SECRET — generated after Vercel deployment, used to validate incoming webhook signatures

Each key is scoped to its minimum required permissions: Printful tokens are store-specific, Stripe keys are environment-specific (test vs. live), and webhook secrets are rotated per deployment.

Vercel Deployment Pipeline

The deployment flow is:

# 1. Build and test locally
npm run build

# 2. Deploy to Vercel production
npx vercel@latest --prod

# 3. Configure environment variables in Vercel dashboard
# (PRINTFUL_API_KEY, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET)

# 4. Trigger redeploy to activate environment variables

Vercel's serverless functions automatically scale to handle traffic spikes during campaigns. The /api/orders endpoint creates orders in Printful synchronously, so response time is bound by Printful's API latency (~200–500ms). For high-volume scenarios, this could be refactored to queue orders asynchronously via a service like AWS SQS or Google Cloud Tasks.

DNS and CloudFront Configuration

After Vercel deployment, 86dfrom.com requires three DNS changes at the registrar (GoDaddy, Namecheap, etc.):

  1. CNAME to Vercel: Point the root domain (or www subdomain) to Vercel's edge network
  2. ACM Certificate Validation: Add CNAME records for AWS Certificate Manager validation (if using CloudFront for static assets)
  3. CloudFront Alias: If serving static assets (CSS, fonts, images) from S3 behind CloudFront, add a CNAME pointing to the CloudFront distribution domain

The project uses two distribution patterns:

  • Primary (86dfrom.com): Vercel's managed CloudFront distribution handles dynamic content with intelligent cache invalidation
  • Redirect (86from.com): A separate CloudFront distribution with a Lambda@Edge function that redirects typos to the canonical domain

The redirect function (deployed via CloudFront's Function editor) intercepts requests to 86from.com and issues a 301 permanent redirect to https://86dfrom.com/. This protects against traffic loss from common typos and domain confusion.

Google Fonts Optimization

The site uses Google's Anton font (serif, bold, high-contrast) for the product title. Rather than relying on Google's CDN (which introduces an external dependency), the font is self-hosted:


curl -L "https://fonts.googleapis.com/css2?family=Anton" \
  -H "User-Agent: Chrome/latest"

The resulting font URL is pinned to a specific revision, ensuring consistent rendering across browsers and eliminating render-blocking network requests to external domains.

Stripe Webhook Implementation

The /api/webhook endpoint accepts POST requests from Stripe's event stream. The implementation:

  1. Validates the signature: Uses the webhook secret to verify the request is authentically from Stripe (HMAC-SHA256)
  2. Handles idempotency: Stores processed event IDs to prevent duplicate order creation if Stripe retries the webhook
  3. Routes