Building a Print-on-Demand T-Shirt Store: Next.js 14, Printful, Stripe, and AWS CloudFront Infrastructure

This post documents the complete build and deployment of 86dfrom.com, a custom t-shirt e-commerce site powered by Next.js 14, integrated with Printful for inventory management and Stripe for payments. The project spans full-stack development with multiple deployment strategies across Vercel and AWS infrastructure.

Project Overview

The 86dfrom project is a print-on-demand (POD) storefront selling Bella+Canvas 3001 Black t-shirts with custom designs. The architecture includes:

  • Frontend/API: Next.js 14 deployed on Vercel (production)
  • Print Partner: Printful API for variant management and order fulfillment
  • Payments: Stripe for credit card processing and webhook handling
  • Domain/CDN: Route53 + CloudFront for DNS and static asset delivery
  • Static Mirror: S3 bucket for fallback/caching layer

Technical Architecture Decisions

Why Next.js 14 with API Routes?

Next.js 14 provides several critical advantages for a POD storefront:

  • Unified Codebase: Frontend and backend coexist in /app, reducing deployment complexity and context-switching.
  • Server Components by Default: Pages render server-side, reducing JavaScript bundle size and improving Core Web Vitals—critical for e-commerce conversion.
  • Built-in API Routes: Routes like /app/api/webhook/route.js handle Stripe webhooks without external serverless infrastructure.
  • Incremental Static Regeneration (ISR): Product pages can be prerendered and revalidated on-demand without full rebuilds.

The build pipeline compiles all five routes cleanly with zero errors, confirming the architecture is sound before deployment.

Printful API Integration Pattern

The project uses Printful's REST API to:

  1. Fetch Variant IDs: A Node.js script (scripts/get-printful-variants.js) queries the Printful store and extracts variant IDs for the Bella+Canvas 3001 Black t-shirt across sizes (XS–3XL).
  2. Store in Environment Variables: Variant IDs are stored in .env.local and accessed at build time to populate product pages.
  3. Order Submission: API route /app/api/orders/route.js accepts POST requests from the checkout form, submits orders to Printful, and logs transaction IDs.

This approach decouples inventory from the application code. If the Bella+Canvas 3001 is discontinued, only variant IDs change—no code redeploy required.

Stripe Payment Flow

The Stripe integration follows this sequence:

  1. Publishable Key: Embedded in the frontend form at /site/index.html or Next.js page component, enabling client-side token generation.
  2. Charge Creation: The /app/api/checkout/route.js endpoint creates a Stripe charge using the secret key server-side.
  3. Webhook Verification: Stripe POSTs to /app/api/webhook/route.js with a signed payload. The endpoint verifies the signature using the webhook secret, preventing replay attacks.
  4. Order Finalization: On successful payment confirmation, the webhook triggers Printful order submission and sends a confirmation email.

This two-stage architecture—client-side tokenization + server-side charge creation—is PCI-DSS compliant and reduces fraud risk.

Infrastructure Setup

Vercel Deployment

The Next.js application is deployed to Vercel production using:

npx vercel@latest --prod

Environment variables are injected into Vercel via the dashboard or CLI:

  • PRINTFUL_API_KEY
  • STRIPE_SECRET_KEY
  • STRIPE_WEBHOOK_SECRET
  • STRIPE_PUBLISHABLE_KEY

Vercel handles HTTPS, CDN edge caching, and auto-scaling—no manual infrastructure management.

Route53 + CloudFront Setup

For the domain 86dfrom.com, the infrastructure follows this pattern:

  1. Route53 Hosted Zone: Created for 86dfrom.com with nameserver records pointing to AWS.
  2. ACM Certificate: Requested for *.86dfrom.com and 86dfrom.com with DNS validation (CNAME records added to Route53).
  3. CloudFront Distributions:
    • Primary: Origin points to Vercel (e.g., 86dfrom.vercel.app), caches static assets, and forwards dynamic requests.
    • Redirect (86from.com): A second distribution with a CloudFront Function that redirects 86from.com86dfrom.com at the edge.
  4. S3 Bucket: Optional fallback origin for static assets (stylesheets, fonts, images) to reduce Vercel load and improve cache hit rates.

Why CloudFront over direct Vercel CNAME? CloudFront provides edge caching, geo-latency routing, DDoS protection via AWS Shield, and the ability to run Lambda@Edge functions for redirects and request transformation.

S3 + CloudFront Static Layer

A parallel S3 bucket (86dfrom-static or similar) stores:

  • CSS/JS bundles (post-build from Next.js)
  • Fonts (e.g., Google Fonts Anton woff2 files)
  • Images and product previews

A separate CloudFront distribution points to the S3 bucket origin, allowing the primary distribution to serve HTML from Vercel while delegating asset delivery to S3. This reduces bandwidth costs and improves Time to First Byte (TTFB) for static assets.

Key Infrastructure Decisions

Variant ID Caching

Rather than fetching variant IDs on every page load, they're retrieved once via scripts/get-printful-variants.js and stored in .env.local:

NEXT_PUBLIC_VARIANT_XS=4016
NEXT_PUBLIC_VARIANT_S=4017
NEXT_PUBLIC_VARIANT_M=4018
NEXT_PUBLIC_VARIANT_L=4019
NEXT_PUBLIC_VARIANT_XL=4020

Trade-off: If Printful inventory changes, the app