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
/sitedirectory, compiled to static HTML with font optimization - Backend: Next.js API routes at
/apifor 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 integrationSTRIPE_SECRET_KEY— server-side only, for charge processingPRINTFUL_API_KEY— Printful authentication token for variant and order lookupsPRINTFUL_STORE_ID— identifies the "86Store" within the Printful accountPRODUCT_VARIANT_IDS— JSON array of valid size/color combinationsSTRIPE_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 CNAME→86dfrom.com(subdomain normalization)86from.com A record→ redirect CloudFront distributionACM 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