```html

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

Over the past development session, we built out a complete print-on-demand t-shirt commerce platform at 86dfrom.com. This post documents the full stack: Next.js 14 API routes, Printful inventory integration, Stripe payment processing, and a multi-region AWS infrastructure spanning S3, CloudFront, Route53, and ACM.

Architecture Overview

The platform is structured as a modern full-stack Next.js application with serverless compute on Vercel, static asset delivery via CloudFront, and DNS management through Route53. The core workflow is:

  • Frontend: Next.js pages and API routes deployed to Vercel
  • Print fulfillment: Printful API integration for real-time inventory and order submission
  • Payment processing: Stripe for checkout and webhook-based order confirmation
  • Static hosting: S3 bucket for site assets with CloudFront distribution for global delivery
  • DNS: Route53 for domain management and traffic routing

Next.js Build and Route Structure

The application compiles cleanly with zero build errors. The five core routes are:

  • / – Homepage with product catalog and variant selector
  • /api/variants – Endpoint to fetch available Printful product variants (sizes, colors, prices)
  • /api/checkout – Creates a Stripe checkout session; validates variant IDs against Printful inventory
  • /api/webhook – Stripe webhook endpoint to handle checkout.session.completed and payment_intent.succeeded events
  • /success – Post-purchase confirmation page

Each route is defined as a file in the /pages or /pages/api directory. The API routes use Next.js request/response handlers to call external services (Printful, Stripe) server-side, keeping API keys secure and avoiding client-side token exposure.

Printful API Integration Strategy

Printful serves as the inventory and fulfillment backend. Rather than hardcoding variant IDs, we implemented a dynamic discovery pattern:

  1. Script-based variant fetching: A Node.js script at scripts/get-printful-variants.js authenticates to Printful using an API key and queries the product catalog for a specific product (in this case, Bella+Canvas 3001 Black t-shirt).
  2. Variant ID extraction: The script parses Printful's response and extracts the numeric variant IDs (e.g., 4016, 4017, 4018, 4019, 4020 for sizes XS through 3XL).
  3. Environment-based configuration: These IDs are stored in .env.local as comma-separated values in a variable like PRINTFUL_VARIANT_IDS, ensuring they're versioned separately from code and easily rotatable.

The /api/variants endpoint reads these IDs from the environment, then calls Printful's GET /products/{id} endpoint to fetch real-time pricing, availability, and metadata. This decouples our application from Printful's product ID scheme—if we want to add more variants or switch products, we update the environment variable and re-run the discovery script.

Stripe Payment Webhook Architecture

The /api/webhook endpoint is critical for order fulfillment. The flow is:

  • User completes checkout on Stripe-hosted checkout page; Stripe sends a signed webhook event to our endpoint.
  • We verify the webhook signature using the Stripe CLI secret (obtained after Vercel deployment).
  • On checkout.session.completed, we extract the session metadata (variant ID, size, quantity) and submit an order to Printful via POST /orders.
  • Printful confirms the order, assigns a tracking number, and begins production.

The webhook secret is environment-specific (test vs. live), so we store it in .env.local under STRIPE_WEBHOOK_SECRET. Stripe's CLI will output this value after we configure the endpoint URL in the Stripe dashboard.

AWS Infrastructure: S3, CloudFront, and Route53

To ensure global availability and fast asset delivery, we built a redundant infrastructure:

  • S3 bucket: 86dfrom-site stores static assets (HTML, CSS, fonts, images). The bucket policy is restricted to CloudFront only, preventing direct public access.
  • CloudFront distribution: Created with origin pointing to the S3 bucket. The distribution uses edge locations worldwide for sub-100ms latency on asset delivery.
  • ACM certificate: We requested a wildcard certificate for *.86dfrom.com and a certificate for the base domain. Validation was done via DNS CNAME records added to Route53.
  • Route53 hosted zone: Created for 86dfrom.com with NS records pointing to Route53 nameservers. All DNS traffic for the domain is now routable through AWS.
  • Redirect distribution: A separate CloudFront distribution handles the typo domain 86from.com, redirecting requests to the canonical domain via a Lambda@Edge function.

The redirect function is published as an edge Lambda, invoked on every request viewer-facing event. This ensures SEO-friendly redirects (HTTP 301) without exposing the redirect logic in the origin bucket.

Environment Configuration and Secrets Management

The .env.local file contains:

  • PRINTFUL_API_KEY – Authenticated token for Printful API calls
  • PRINTFUL_VARIANT_IDS – Comma-separated variant IDs for the target product
  • STRIPE_SECRET_KEY – Stripe API secret for backend payments (test or live)
  • STRIPE_PUBLISHABLE_KEY – Public key for frontend redirect to Stripe checkout
  • STRIPE_WEBHOOK_SECRET – Secret to verify incoming webhook signatures

After Vercel deployment, these values are also added to the Vercel project settings via the dashboard or CLI, ensuring encrypted storage and automatic injection into the runtime environment.

Deployment Pipeline

The deployment script at scripts/deploy.sh orchestrates the full stack:

  1. Build Next.js application locally to verify compilation.
  2. Deploy to Vercel using npx vercel@latest --prod, which automatically triggers a build and assigns a production URL.
  3. Sync static assets to the S3 bucket using AWS CLI with the --delete flag to remove stale files.
  4. Invalidate the CloudFront distribution cache with a wildcard invalidation path (/*), ensuring users receive fresh assets immediately.

This pipeline ensures that API route updates on Vercel and static asset updates on S3/CloudFront