Building a Printful-Integrated T-Shirt Site on Next.js 14: Infrastructure, Architecture, and Deployment Patterns
Overview: From Monorepo to Production
This post documents the complete build and deployment of 86dfrom.com, a Next.js 14 e-commerce site integrated with Printful's print-on-demand API and Stripe for payments. The project demonstrates modern serverless architecture patterns: decoupled frontend (Vercel), backend API routes (Next.js serverless functions), inventory management (Printful), and payment processing (Stripe). All infrastructure is code-driven and reproducible.
Project Structure and Setup
The project lives at /Users/cb/Documents/repos/sites/86dfrom.com with a clean directory tree:
86dfrom.com/
├── site/
│ ├── index.html # Static landing page (pre-rendered)
│ └── success.html # Post-purchase confirmation
├── gas/
│ ├── Code.gs # Google Apps Script for webhook handling
│ ├── appsscript.json # GAS manifest
│ └── .clasp.json # Clasp project config
├── scripts/
│ ├── deploy.sh # S3 + CloudFront deployment script
│ └── get-printful-variants.js # Variant ID fetcher
├── .env.local # Runtime credentials (git-ignored)
└── [Next.js app root] # Next.js 14 app with /api routes
The monorepo structure allows us to version control static assets, deployment automation, and cloud functions alongside the Next.js application. Google Apps Script is used as a lightweight webhook receiver for asynchronous order processing, decoupling the payment flow from the fulfillment pipeline.
Build Verification and Compilation
Before any deployment, we validated the Next.js build pipeline:
cd ~/Documents/repos/sites/86dfrom.com && npm run build
The build compiled cleanly with all five API routes resolving without errors:
/api/products— Fetch product catalog with variant metadata from Printful/api/checkout— Create Stripe checkout sessions/api/webhook— Stripe webhook receiver for payment events/api/orders— Order history and status/api/health— Health check endpoint
This modular API design follows microservices principles: each route has a single responsibility, can be independently scaled by Vercel's serverless platform, and integrates cleanly with external services (Printful, Stripe, Google Sheets for order logging).
Printful Integration: Variant Fetching
The script scripts/get-printful-variants.js queries the Printful API to extract product variant IDs for the Bella+Canvas 3001 Black t-shirt. This is a critical step because Printful requires exact variant IDs in the fulfillment payload:
node scripts/get-printful-variants.js
The script makes authenticated calls to https://api.printful.com/ using the store API token (scoped to the "86Store" within the dangerouscentaur.com Printful account). Variant IDs returned are burned into environment variables, ensuring the checkout flow always orders the correct product configuration.
Why Printful? Print-on-demand eliminates inventory risk. Orders are created in Printful only after successful Stripe payment, ensuring zero upfront capital for stock. The API is REST-based and synchronous, making it trivial to call from Next.js serverless functions with sub-second latency.
Environment and Secrets Management
The .env.local file is never committed and is structured as:
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
PRINTFUL_API_TOKEN=
PRINTFUL_VARIANT_IDS=4016,4017,4018,4019,4020
GAS_WEBHOOK_URL=https://script.google.com/macros/d/.../usercontent
NEXT_PUBLIC_* prefixed variables are safe to expose to the browser (Stripe public key); others remain server-only. Vercel's UI allows environment variable injection at build and runtime, eliminating the need to commit secrets to git.
Stripe Configuration and Webhook Setup
Stripe integration follows the standard SCA (Strong Customer Authentication) flow:
- Frontend calls
/api/checkoutwith cart data - Backend creates a
PaymentIntentand returns a client secret - Frontend redirects to Stripe Checkout (managed session)
- Post-payment, Stripe POSTs to
/api/webhookwith event signed usingwhsec_*secret - Webhook verifies signature and triggers fulfillment via Printful API
The webhook is the critical integration point. Its signature is verified server-side using the webhook secret, preventing spoofed payment events. Once verified, the handler calls Printful to create an order and optionally logs to Google Sheets via Apps Script.
Static Assets and CDN Distribution
The site/ directory contains static HTML and is served independently of the Next.js app for resilience:
bash scripts/deploy.sh
This script syncs site/ to an S3 bucket and invalidates the CloudFront cache, following the pattern established in other projects:
- S3 bucket:
86dfrom-com-site(created during setup) - CloudFront distribution: Primary domain distribution for
86dfrom.com - Route53 hosted zone: Manages DNS for
86dfrom.com(created during initial infrastructure setup)
Static files are served with cache headers; CloudFront cache invalidation ensures updates propagate within seconds. This separation of static and dynamic content is a best practice: static assets can be cached aggressively, while API routes respond dynamically.
Vercel Deployment
The Next.js application is deployed to Vercel production:
npx vercel@latest --prod
This command builds the app, uploads it to Vercel's edge network, and assigns a unique production URL. All environment variables are injected from Vercel's dashboard (no .env.local file needed on CI/CD).
The Vercel deployment is fronted by the custom domain 86dfrom.com, configured via Route53 CNAME/alias records pointing to Vercel's nameservers. This architecture provides:
- Global edge distribution: Requests route to nearest Vercel PoP
- Automatic HTTPS: Vercel provisions SSL certs automatically
- Seamless CI/CD: Git pushes trigger rebuilds (if integrated with GitHub)
- Serverless functions