```html

Building a Printful-Integrated T-Shirt Commerce Site on Next.js 14 with AWS CloudFront and Stripe

Over the past development session, we built out a complete e-commerce infrastructure for 86dfrom.com, a print-on-demand t-shirt site powered by Printful, Stripe, and deployed across Vercel and AWS CloudFront. This post covers the full technical stack, infrastructure decisions, and deployment patterns used.

Project Architecture Overview

The site is a Next.js 14 application with the following structure:

  • Frontend: React components in /site directory, compiled to static HTML with font optimization
  • Backend: Next.js API routes at /api for Printful integration and Stripe webhooks
  • Infrastructure: Vercel for primary deployment, AWS S3 + CloudFront for static assets and redirects, Route53 for DNS
  • Print Partner: Printful API for variant lookups and order fulfillment
  • Payment: Stripe for PCI-compliant payment processing

Build Verification and Route Compilation

The first critical step was validating that all application routes compiled cleanly. The Next.js build process confirmed five distinct routes without errors:

npm run build

This clean compile was essential because the site handles dynamic data from external APIs (Printful variants, Stripe keys) that would otherwise cause build-time failures if misconfigured. The production build output was verified before any infrastructure setup.

Printful Integration and Variant Management

The Printful API integration required fetching product variant IDs for the Bella+Canvas 3001 Black t-shirt. Rather than hardcoding variant IDs, we created a script at scripts/get-printful-variants.js that:

  • Authenticates to Printful using Bearer token authentication
  • Queries the product catalog for the Bella+Canvas 3001 model in black color
  • Extracts variant IDs across multiple size options (S, M, L, XL, 2XL)
  • Outputs JSON suitable for environment variable configuration

This approach decouples variant configuration from code, allowing updates to the Printful store without redeployment. The script validates API connectivity before writing the config, preventing silent failures.

Environment Configuration Strategy

All sensitive and environment-specific data lives in .env.local with the following variables:

  • NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY — publicly exposed for client-side Stripe integration
  • STRIPE_SECRET_KEY — server-side only, for charge processing
  • PRINTFUL_API_KEY — Printful authentication token for variant and order lookups
  • PRINTFUL_STORE_ID — identifies the "86Store" within the Printful account
  • PRODUCT_VARIANT_IDS — JSON array of valid size/color combinations
  • STRIPE_WEBHOOK_SECRET — HMAC signing key for webhook validation (populated post-deployment)

This separation of public and private keys follows the principle of least privilege — the client never receives the Stripe secret key, and Printful credentials are never exposed to the browser.

AWS Infrastructure: S3, CloudFront, and Route53

While the main application deploys to Vercel, we configured a supporting AWS infrastructure for static asset delivery and domain management:

S3 Bucket Configuration

Created S3 bucket 86dfrom.com (in us-east-1, AWS requirement for CloudFront origin) with the following configuration:

  • Bucket Policy: Restricts access to CloudFront origin access identity (OAI), preventing direct bucket access
  • No Public ACLs: Block all public access, enforce private by default
  • Versioning: Enabled for rollback capability

The bucket policy pattern used here is the AWS-recommended approach for CloudFront distributions — users access content only through CloudFront's edge network, never directly from S3.

CloudFront Distribution for 86dfrom.com

Created primary distribution with domain 86dfrom.com:

  • Origin: S3 bucket with OAI authentication
  • TTL: 86400 seconds (24 hours) for HTML, 31536000 (1 year) for versioned assets
  • Compression: Gzip enabled for text, CSS, JavaScript
  • Cache Key: Includes Host header to support multi-domain scenarios
  • Viewer Protocol: HTTPS only, with automatic HTTP→HTTPS redirect
  • ACM Certificate: Requested for 86dfrom.com, validated via DNS CNAME records in Route53

CloudFront Function for Domain Redirect

Created a second CloudFront distribution for domain redirect (typo mitigation) at 86from.com (missing 'd'). This distribution uses a CloudFront Function that executes at the edge:

function handler(event) {
  var request = event.request;
  var host = request.headers.host.value;
  
  if (host === '86from.com') {
    return {
      statusCode: 301,
      statusDescription: 'Moved Permanently',
      headers: {
        location: { value: 'https://86dfrom.com' + request.uri }
      }
    };
  }
  return request;
}

This function runs at every edge location with sub-millisecond latency, eliminating the need for a separate origin server to handle redirects. The 301 status code signals permanent redirection to search engines and browsers.

Route53 DNS Architecture

Updated Route53 hosted zone with the following records:

  • 86dfrom.com A record → CloudFront distribution alias (no charge, automatic failover)
  • www.86dfrom.com CNAME86dfrom.com (subdomain normalization)
  • 86from.com A record → redirect CloudFront distribution
  • ACM validation CNAMEs → automated DNS validation for both domains' certificates

Using Route53 alias records instead of CNAME records for the root domain follows AWS best practices — alias records integrate with CloudFront health checks and incur no query charges.

Deployment to Vercel

The Next.js application deploys to Vercel using:

npx vercel@latest --prod

Environment variables are configured via Vercel's dashboard under Settings → Environment Variables. This decouples secrets from the codebase — developers never commit API keys to version control. Vercel's deployment preview system automatically provisions