This project showcases the comprehensive capabilities of Contentstack CMS and Contentstack Launch through a production-grade, AI-focused blog platform. Built with modern web technologies, it demonstrates advanced features including multi-language content delivery, edge computing, intelligent caching strategies, SSO authentication, and geo-location detection. The platform features diverse blog routes such as /blog/ai, /blog/generativeai, /blog/gemini, and /blog/neuralink, each implementing different rendering strategies (SSG, ISR, SSR) to highlight Contentstack Launch's flexible deployment configurations and performance optimization capabilities.
- ✅ Monorepo structure with Turborepo
- ✅ Multi-language support (English, French, Japanese) with automatic geo-detection
- ✅ Environment-specific domains with password protection
- ✅ SSO authentication via Contentstack OAuth
- ✅ IP-based access control for sensitive routes
- ✅ Edge Functions for redirects, rewrites, and asset optimization
- ✅ Intelligent caching with on-demand revalidation
- ✅ Cache priming for new content
- ✅ CloudWatch integration for log monitoring
┌─────────────────────────────────────────────────────────────┐
│ Contentstack Launch │
│ (AWS-AU Region Deployment) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Edge Functions │
│ • Geo-location & Locale Detection │
│ • Password Protection (preview domain) │
│ • IP Whitelisting │
│ • SSO Authentication │
│ • Redirects & Rewrites │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Production │ │ Preview │ │ Test │
│ Domain │ │ Domain │ │ Domain │
│ │ │ (Password) │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└─────────────────┼─────────────────┘
▼
┌──────────────────┐
│ Next.js App │
│ (Standalone) │
└──────────────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Contentstack │ │ CDN │ │ CloudWatch │
│ CMS │ │ Assets │ │ Logging │
└──────────────┘ └──────────────┘ └──────────────┘
- Monorepo managed with Turborepo for optimized builds
- Project structure:
/apps/blog- Next.js 15 application with App Router/functions- Edge Functions for proxy and CDN assets/scripts- Build-time scripts for launch.json generation
- Rendering strategies:
- SSG (Static):
/blog/ai(50s revalidation) - ISR (Incremental):
/blog/generativeai(1h revalidation),/blog/latest(40s revalidation) - SSR (Server-side):
/author-tools
- SSG (Static):
Files: apps/blog/src/app/components/LanguageSwitcher.tsx, functions/[proxy].edge.js
-
Supported Languages:
- 🇺🇸 English (
en-us) - 🇫🇷 French (
fr-fr) - 🇯🇵 Japanese (
ja-jp)
- 🇺🇸 English (
-
Automatic Detection:
- Uses
visitor-ip-countryheader from edge for geo-location - Fallback to
accept-languagebrowser header - Automatic redirect based on user location
- Uses
-
Manual Override:
- Language switcher dropdown in UI
- Query parameter:
?lang=fr-fr
File: functions/[proxy].edge.js
Production Environment:
- Domain:
blog.devcontentstackapps.com - No password protection
- Full production features enabled
Preview Environment:
- Domain:
preview-blog.devcontentstackapps.com - Password Protected with Basic Auth
- HTTP 401 response if credentials invalid
Test Environment:
- Domain:
blog-test.devcontentstackapps.com - No password protection
- Used for development testing
Files: apps/blog/src/app/login/page.tsx, functions/[proxy].edge.js
- OAuth 2.0 Flow with Contentstack SSO
- Protected route:
/author-tools - Automatic redirect to login if unauthenticated
Flow:
- User accesses
/author-tools - Edge function checks for JWT in cookies
- If missing/invalid → redirect to
/login - Login page initiates OAuth flow
- Callback handler at
/oauth/callbackexchanges code for token - JWT stored in secure cookie
- User redirected to
/author-tools
File: functions/[proxy].edge.js
-
Protected Route:
/author-tools -
Allowed IPs:
27.107.90.20627.107.175.218
-
HTTP 403 Forbidden if IP not whitelisted
-
IP detection from headers:
CF-Connecting-IP,X-Forwarded-For,X-Real-IP
Files: apps/blog/src/app/lib/config.js, apps/blog/src/app/lib/redirects.js, apps/blog/src/app/lib/rewrites.js, functions/[proxy].edge.js
Redirects:
/blog/ai→/blog/neuralink(302 temporary redirect)
Rewrites (Production Only):
/latest→/blog/latest(URL stays as/latest)/blog?page=1→/blog/latest
Environment-Specific:
- Rewrites only apply on production domain (
blog.devcontentstackapps.com) - Preview and test domains excluded via
onlyOnProdflag
Files:
apps/blog/src/app/lib/config.js- Configurationapps/blog/src/app/lib/redirects.js- Redirect processorapps/blog/src/app/lib/rewrites.js- Rewrite processor (with environment checks)functions/[proxy].edge.js- Edge function integration
File: functions/cdn-assets/[asset].js
- Custom CDN Path:
/cdn-assets/* - Image Optimization using Contentstack Image API
- Default Optimization: WebP format, 80% quality
Features:
- Proxies assets from Contentstack
- Hides original asset URLs
- Automatic format conversion (WebP)
- Custom query parameters for optimization
Files: apps/blog/next.config.ts
Cache Control Headers:
| Route | Cache Duration |
|---|---|
/blog/gemini |
50 seconds |
Files: apps/blog/src/app/api/revalidate/route.ts, apps/blog/src/app/components/RevalidateButton.tsx, apps/blog/src/app/blog/generativeai/page.tsx
Feature: /blog/generativeai page includes a "Revalidate Cache" button
Flow:
- User clicks "Revalidate Cache" button
- Calls
/api/revalidate?tag=generative-blog-post&path=/blog/generativeai - Next.js
revalidateTag()andrevalidatePath()called - Triggers Contentstack Automate webhooks for both environments:
- Cache cleared, page reloaded with fresh content
Files: scripts/generateLaunchJson.ts, scripts/lib/contentstack-server.ts
Automatic Cache
- Fetches cache priming URLs from Contentstack CMS
- Generates
launch.jsonat build time - Contentstack Launch pre-warms specified routes
Build Process:
npm run prebuild
→ Runs generate:launch script
→ Fetches blog posts with cache priming URLs
→ Generates launch.jsonCMS Field Structure:
blogpost (Content Type)
└── cache (Group)
└── cachepriming (Group)
└── urls (Multiple Text)
Files:
scripts/generateLaunchJson.ts- Main scriptscripts/lib/contentstack-server.ts- Contentstack SDK setuppackage.json-prebuildscript hook
Repository: Hosted on Bitbucket Cloud
CI/CD Setup:
- Code hosted on Bitbucket
- Automated deployment pipeline configured
- Build triggers on push to main branch
- Deploys to Contentstack Launch
Build Commands:
npm install
npm run build # Triggers prebuild → generateLaunchJsonDeployment Flow:
Git Push → Bitbucket → CI/CD Pipeline → Build → Deploy to Launch (AWS-AU)
Reference:
- Node.js 20.x or higher
- npm 10.x or higher
- Contentstack Account with API credentials
-
Clone the repository:
git clone <repository-url> cd MultiFeature-Blog-Platform
-
Install dependencies:
npm install
-
Configure environment variables:
cd apps/blog cp .env.example .env # Edit .env with your Contentstack credentials
-
Run development server:
cd ../.. npm run dev -
Open browser:
http://localhost:3000
-
Push to Bitbucket:
git add . git commit -m "Deploy updates" git push origin main
-
CI/CD automatically triggers:
- Runs
npm install - Runs
npm run build - Generates
launch.json - Deploys to selected region
- Runs
-
Access deployed application:
- Production:
https://blog.devcontentstackapps.com - Preview:
https://preview-blog.devcontentstackapps.com - Test:
https://blog-test.devcontentstackapps.com
- Production:
// apps/blog/src/app/lib/contentstack.ts
export async function getAIBlogPost(locale = "en-us") {
const Query = Stack.ContentType("blogpost").Query();
Query.where("url", "/blog/ai").language(locale);
const response = await Query.toJSON().find();
return response?.[0]?.[0] || null;
}# Conceptual pipeline (Bitbucket Pipelines)
pipelines:
default:
- step:
name: Build and Deploy
script:
- npm install
- npm run build # Runs prebuild → generateLaunchJson
- # Deploy to Contentstack Launch// package.json
{
"scripts": {
"prebuild": "npm run generate:launch",
"generate:launch": "ts-node --project tsconfig.scripts.json scripts/generateLaunchJson.ts",
"build": "turbo run build"
}
}- Contentstack Documentation
- Contentstack Launch Guide
- Next.js App Router
- Turborepo Documentation
- CloudWatch OTEL Collector Example
Happy Coding! 🚀
1633ea6 (cache priming)