Building a Printful-Integrated T-Shirt Store on Next.js 14: Infrastructure Setup for 86dfrom.com
This post documents the full-stack setup of a print-on-demand t-shirt e-commerce site built on Next.js 14, integrated with Printful for fulfillment and Stripe for payments. We'll cover the architecture decisions, infrastructure provisioning, and deployment pipeline that enables seamless product variant management and order processing.
Project Overview & Architecture
86dfrom.com is a Printful-backed t-shirt store where customers can purchase custom apparel with dynamic pricing and inventory sync. The stack consists of:
- Frontend: Next.js 14 with App Router, static HTML fallback for the storefront
- API Layer: Next.js API routes for Printful sync and Stripe webhook handling
- CDN: CloudFront with S3 origin for static asset delivery
- DNS: Route53 for domain management and traffic routing
- Payment: Stripe for PCI-compliant payment processing
- Fulfillment: Printful API for variant data, order placement, and tracking
The architecture prioritizes separation of concerns: static content via CloudFront/S3, dynamic API routes via Vercel, and a flexible webhook endpoint for asynchronous order updates.
Build Configuration & Verification
Before infrastructure deployment, we confirmed the Next.js build compiles cleanly with all five API routes:
/api/route.ts // Health check
/api/variants // Fetch Printful variant data
/api/orders // Order creation endpoint
/api/webhook // Stripe webhook listener
/api/callback // Printful callback handler
The next build command executed without errors, confirming no circular dependencies, missing imports, or TypeScript issues before touching infrastructure.
Environment Configuration & Secrets Management
The project uses a three-tier secrets approach:
- .env.local (Git-ignored): Local development secrets
- Vercel Environment Variables: Production secrets injected at build/deploy time
- AWS Secrets Manager (optional): For rotation and audit logging
Required environment variables for 86dfrom.com:
NEXT_PUBLIC_STORE_NAME=86Store
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
PRINTFUL_API_KEY=...
PRINTFUL_STORE_ID=...
The NEXT_PUBLIC_* prefix ensures client-side availability without exposing secrets; the Stripe and Printful keys remain server-only. The webhook secret is provisioned after the Vercel deployment endpoint is live.
Printful Variant Data Pipeline
Product variants (sizes, colors, SKUs) are fetched from Printful's API and cached. The workflow:
- Script
scripts/get-printful-variants.jscalls Printful's/products/{product_id}endpoint - Extracts variant IDs for the Black Bella+Canvas 3001 blank (items 4016–4020)
- Writes variant mappings to
site/variants.jsonfor client-side rendering - API route
/api/variantsserves this cached data without re-querying Printful on every pageload
This caching strategy reduces API calls to Printful (rate-limited at 60 req/min for standard accounts) and keeps product pages fast. The variant JSON structure includes:
{
"size": "L",
"color": "Black",
"printful_variant_id": 4018,
"sku": "86DFROM-BK-L",
"price": 2499 // in cents for Stripe
}
DNS & CDN Infrastructure
86dfrom.com uses a hybrid CDN approach:
- S3 Bucket:
86dfrom-com-site(us-east-1) stores static assets (CSS, JS, fonts) - CloudFront Distribution: Caches S3 objects with 1-hour TTL for media, 24-hour for code
- Route53 Hosted Zone:
86dfrom.comwith CNAME record pointing to CloudFront - ACM Certificate: TLS 1.2+ for HTTPS, auto-renewed by AWS
The S3 bucket policy restricts access to CloudFront only (Origin Access Identity), preventing direct S3 URL leakage. The CloudFront distribution includes a Lambda@Edge function for conditional redirects (e.g., 86from.com → 86dfrom.com).
Why this setup? S3 + CloudFront is significantly cheaper than hosting on Vercel for large media files, and the Origin Access Identity ensures users can't bypass the cache by guessing S3 URLs. The Route53 health checks can trigger failover if the origin becomes unhealthy.
Vercel Deployment Configuration
The Next.js app is deployed to Vercel with:
npx vercel@latest --prod --env-file .env.local
Vercel's build output:
- Static routes (index, success pages) → Edge Cache (global CDN)
- API routes → Serverless Functions (auto-scaled)
- ISR routes (optional) → Revalidation at request time
All environment variables are injected into the Vercel project's settings, ensuring they're available during build and runtime without committing them to Git.
Stripe Integration & Webhook Setup
The webhook endpoint at /api/webhook listens for Stripe events:
payment_intent.succeeded→ Place order in Printfulpayment_intent.payment_failed→ Log failure, notify usercharge.refunded→ Cancel order in Printful
After deploying to Vercel, the webhook URL is registered in Stripe Dashboard → Developers → Webhooks with the signing secret returned by Stripe. The webhook secret is then added to Vercel's environment variables.
The webhook handler verifies the request signature using stripe.webhooks.constructEvent(body, signature, secret) before processing. This prevents spoofed webhook calls.
Deployment Script & Automation
The deployment script scripts/deploy.sh orchestrates the full pipeline:
#!/bin/bash
# 1. Build Next.js
npm run build
# 2. Export static routes to site/
npm run export
# 3. Sync site/ to S3
aws s3 sync site/ s3://86dfrom-com-site/
# 4. Invalidate CloudFront cache
aws cloudfront create-