[](https://www.npmjs.com/package/@aryanbansal-launch/edge-utils) [](https://opensource.org/licenses/MIT)
npm install @aryanbansal-launch/edge-utils

A comprehensive toolkit for Contentstack Launch (the high-performance frontend hosting service by Contentstack). This library provides production-ready utilities to simplify Edge Functions development, security, and performance optimization.
---
- Quick Start
- Usage Flow
- Complete API Reference
- Real-World Examples
- CLI Commands
- Platform Support
---
``bash`
npm install @aryanbansal-launch/edge-utils
Run this command from your project root (where package.json is located):
`bash`
npx create-launch-edge
This automatically creates:
- functions/ directoryfunctions/[proxy].edge.js
- with production-ready boilerplate
1. Open functions/[proxy].edge.js
2. Customize the utilities based on your needs
3. Deploy to Contentstack Launch
Need help? Run:
`bash`
npx launch-help
---
Edge Functions in Contentstack Launch act as middleware that runs before requests reach your origin server. Think of them as a chain of checks and transformations:
``
User Request ā Edge Function ā Your Application
Every utility follows this pattern:
`javascript`
const result = utilityFunction(request, options);
if (result) return result; // Early return if condition met
// Continue to next check...
Here's how utilities work together in functions/[proxy].edge.js:
`javascript
import {
blockDefaultDomains,
handleNextJS_RSC,
blockAICrawlers,
ipAccessControl,
protectWithBasicAuth,
redirectIfMatch,
getGeoHeaders,
jsonResponse,
passThrough
} from "@aryanbansal-launch/edge-utils";
export default async function handler(request, context) {
// 1ļøā£ SECURITY LAYER
// Block default domains (SEO best practice)
const domainCheck = blockDefaultDomains(request);
if (domainCheck) return domainCheck;
// Block AI bots and crawlers
const botCheck = blockAICrawlers(request);
if (botCheck) return botCheck;
// IP-based access control
const ipCheck = ipAccessControl(request, {
allow: ["203.0.113.10", "198.51.100.5"]
});
if (ipCheck) return ipCheck;
// 2ļøā£ AUTHENTICATION LAYER
// Protect staging environments
const auth = await protectWithBasicAuth(request, {
hostnameIncludes: "staging.myapp.com",
username: "admin",
password: "securepass123"
});
if (auth && auth.status === 401) return auth;
// 3ļøā£ FRAMEWORK FIXES
// Fix Next.js RSC header issues
const rscCheck = await handleNextJS_RSC(request, {
affectedPaths: ["/shop", "/products", "/about"]
});
if (rscCheck) return rscCheck;
// 4ļøā£ ROUTING LAYER
// Handle redirects
const redirect = redirectIfMatch(request, {
path: "/old-page",
to: "/new-page",
status: 301
});
if (redirect) return redirect;
// 5ļøā£ PERSONALIZATION
// Get user's location
const geo = getGeoHeaders(request);
if (geo.country === "US") {
console.log(US visitor from ${geo.city}, ${geo.region});
}
// 6ļøā£ CUSTOM API ENDPOINTS
const url = new URL(request.url);
if (url.pathname === "/api/health") {
return jsonResponse({
status: "healthy",
region: geo.region,
timestamp: Date.now()
});
}
// 7ļøā£ DEFAULT: Pass to origin
return passThrough(request);
}
`
---
#### blockAICrawlers(request, bots?)
Block AI crawlers and bots from accessing your site.
Parameters:
- request (Request) - The incoming request objectbots
- (string[], optional) - Custom list of bot user-agents to block
Returns: Response | null403 Forbidden
- Returns if bot detectednull
- Returns if no bot detected (continue processing)
Default Blocked Bots:
The following bots are blocked by default (case-insensitive):
- claudebot - Anthropic's Claude AI crawlergptbot
- - OpenAI's GPT crawlergooglebot
- - Google's web crawlerbingbot
- - Microsoft Bing's crawlerahrefsbot
- - Ahrefs SEO crawleryandexbot
- - Yandex search engine crawlersemrushbot
- - SEMrush SEO tool crawlermj12bot
- - Majestic SEO crawlerfacebookexternalhit
- - Facebook's link preview crawlertwitterbot
- - Twitter's link preview crawler
Example:
`javascript
// Use default bot list
const response = blockAICrawlers(request);
if (response) return response;
// Custom bot list
const response = blockAICrawlers(request, [
"gptbot",
"claudebot",
"my-custom-bot"
]);
if (response) return response;
`
Use Cases:
- Protect content from AI scraping
- Reduce server load from aggressive crawlers
- Comply with content usage policies
---
#### blockDefaultDomains(request, options?)
Block access via default Launch domains (*.contentstackapps.com).
Parameters:
- request (Request) - The incoming request objectoptions
- (object, optional)domainToBlock
- (string) - Custom domain to block (default: "contentstackapps.com")
Returns: Response | null403 Forbidden
- Returns if default domain detectednull
- Returns if custom domain used
Example:
`javascript
// Block default Launch domain
const response = blockDefaultDomains(request);
if (response) return response;
// Block custom domain
const response = blockDefaultDomains(request, {
domainToBlock: "myolddomain.com"
});
if (response) return response;
`
Use Cases:
- Force users to custom domain for SEO
- Prevent duplicate content indexing
- Professional branding
Learn More: Blocking Default Launch Domains
---
#### ipAccessControl(request, options)
Whitelist or blacklist IPs at the edge.
Parameters:
- request (Request) - The incoming request objectoptions
- (object)allow
- (string[], optional) - Whitelist of allowed IPsdeny
- (string[], optional) - Blacklist of denied IPs
Returns: Response | null403 Forbidden
- Returns if IP blockednull
- Returns if IP allowed
Example:
`javascript
// Whitelist specific IPs (only these can access)
const response = ipAccessControl(request, {
allow: ["203.0.113.10", "198.51.100.5"]
});
if (response) return response;
// Blacklist specific IPs
const response = ipAccessControl(request, {
deny: ["192.0.2.100", "192.0.2.101"]
});
if (response) return response;
// Combine both (deny takes precedence)
const response = ipAccessControl(request, {
allow: ["203.0.113.0/24"], // Allow subnet
deny: ["203.0.113.50"] // Except this one
});
if (response) return response;
`
Use Cases:
- Restrict admin panels to office IPs
- Block malicious IPs
- Create staging environment access control
Learn More: IP-Based Access Control
---
#### protectWithBasicAuth(request, options)
Add Basic Authentication to protect environments.
Parameters:
- request (Request) - The incoming request objectoptions
- (object)hostnameIncludes
- (string) - Protect URLs containing this hostnameusername
- (string) - Username for authenticationpassword
- (string) - Password for authenticationrealm
- (string, optional) - Auth realm name (default: "Protected Area")
Returns: Promise401 Unauthorized
- Returns if auth failsnull
- Returns authenticated response if credentials valid
- Returns if hostname doesn't match
Example:
`javascript
// Protect staging environment
const auth = await protectWithBasicAuth(request, {
hostnameIncludes: "staging.myapp.com",
username: "admin",
password: "securepass123",
realm: "Staging Environment"
});
if (auth && auth.status === 401) return auth;
// Protect specific path pattern
const url = new URL(request.url);
if (url.pathname.startsWith("/admin")) {
const auth = await protectWithBasicAuth(request, {
hostnameIncludes: url.hostname,
username: "admin",
password: "adminpass"
});
if (auth && auth.status === 401) return auth;
}
`
Use Cases:
- Protect staging/dev environments
- Quick password protection for demos
- Restrict access to admin areas
Security Note: For production, use proper authentication systems. Basic Auth credentials are base64-encoded, not encrypted.
Learn More: Password Protection for Environments
---
#### redirectIfMatch(request, options)
Conditional redirects based on path and method.
Parameters:
- request (Request) - The incoming request objectoptions
- (object)path
- (string) - Path to matchto
- (string) - Destination pathmethod
- (string, optional) - HTTP method to match (e.g., "GET", "POST")status
- (number, optional) - HTTP status code (default: 301)
Returns: Response | nullnull
- Returns redirect response if path matches
- Returns if no match
Example:
`javascript
// Simple redirect
const redirect = redirectIfMatch(request, {
path: "/old-page",
to: "/new-page",
status: 301 // Permanent redirect
});
if (redirect) return redirect;
// Temporary redirect
const redirect = redirectIfMatch(request, {
path: "/maintenance",
to: "/coming-soon",
status: 302 // Temporary redirect
});
if (redirect) return redirect;
// Method-specific redirect
const redirect = redirectIfMatch(request, {
path: "/api/old-endpoint",
method: "POST",
to: "/api/v2/endpoint",
status: 308 // Permanent redirect (preserves method)
});
if (redirect) return redirect;
`
Use Cases:
- SEO-friendly URL changes
- Migrate old URLs to new structure
- A/B testing redirects
- Maintenance mode redirects
Learn More: Edge URL Redirects
---
Contentstack Launch offers two ways to handle redirects. Choose the right approach based on your needs:
#### Option 1: Config-Based Redirects (launch.json)
Best for: Static, predictable redirects that don't require logic
Pros:
- ā” Faster: No edge function execution overhead
- šÆ Simpler: Pure configuration, no code needed
- š¦ Bulk-friendly: Easy to manage hundreds/thousands of redirects
- š§ Easy updates: Use npx launch-config CLI with CSV/JSON import
Cons:
- ā No dynamic logic (can't check cookies, headers, geo, etc.)
- ā No conditional redirects based on request data
- ā Limited to exact path or wildcard matching
Setup:
`bashInteractive CLI
npx launch-config
Example
launch.json:
`json
{
"redirects": [
{
"source": "/old-blog/:slug",
"destination": "/blog/:slug",
"statusCode": 301
},
{
"source": "/products/old-*",
"destination": "/products/new-*",
"statusCode": 308
}
]
}
`When to use:
- ā
SEO migrations with hundreds of URL changes
- ā
Simple path rewrites (e.g.,
/old-path ā /new-path)
- ā
Bulk product SKU redirects
- ā
Static site restructuring
- ā
Predictable, rule-based redirects---
#### Option 2: Edge Function Redirects (This Package)
Best for: Dynamic redirects requiring logic or request inspection
Pros:
- š§ Smart: Access cookies, headers, geo-location, user-agent
- šØ Flexible: Complex conditional logic
- š Dynamic: Redirect based on A/B tests, feature flags, user data
- š Contextual: Different redirects per country/region
Cons:
- š Slightly slower (edge function execution)
- š§ Requires code changes
- š More complex for simple redirects
Setup:
`javascript
import { redirectIfMatch } from '@aryanbansal-launch/edge-utils';export default async function handler(request) {
// Dynamic redirect based on cookie
const cookie = request.headers.get('cookie');
if (cookie?.includes('beta=true')) {
return Response.redirect(new URL('/beta-features', request.url), 302);
}
// Geo-based redirect
const country = request.headers.get('x-cs-country');
if (country === 'FR') {
return Response.redirect('https://fr.mysite.com', 302);
}
// Conditional redirect
const redirect = redirectIfMatch(request, {
path: "/old-page",
to: "/new-page",
method: "GET",
status: 301
});
if (redirect) return redirect;
return passThrough(request);
}
`When to use:
- ā
Geo-location based redirects
- ā
A/B testing redirects
- ā
Cookie/session-based routing
- ā
User-agent specific redirects (mobile vs desktop)
- ā
Feature flag redirects
- ā
Maintenance mode with exceptions
- ā
Complex conditional logic
---
#### Quick Decision Guide
| Scenario | Use Config | Use Edge Function |
|----------|-----------|-------------------|
| 500+ simple URL redirects | ā
| ā |
| SEO migration from old site | ā
| ā |
| Redirect based on country | ā | ā
|
| A/B test routing | ā | ā
|
| Cookie-based redirects | ā | ā
|
| Mobile vs desktop routing | ā | ā
|
| Simple path changes | ā
| ā |
| Maintenance mode (all users) | ā
| ā |
| Maintenance mode (except admins) | ā | ā
|
| Bulk product SKU changes | ā
| ā |
š” Pro Tip: You can use both approaches together! Use
launch.json for bulk static redirects and edge functions for dynamic logic.Example Combined Approach:
`javascript
// launch.json - handles 1000+ static SEO redirects
{
"redirects": [
{ "source": "/old-blog/:slug", "destination": "/blog/:slug", "statusCode": 301 }
// ... 1000 more redirects
]
}// functions/[proxy].edge.js - handles dynamic logic
export default async function handler(request) {
// Geo-based redirect (dynamic)
const country = request.headers.get('x-cs-country');
if (country === 'FR') {
return Response.redirect('https://fr.mysite.com', 302);
}
// Static redirects handled by launch.json automatically
return passThrough(request);
}
`---
$3
####
handleNextJS_RSC(request, options)Fix Next.js React Server Components header issues.
Parameters:
-
request (Request) - The incoming request object
- options (object)
- affectedPaths (string[]) - Array of paths with RSC issuesReturns:
Promise
- Returns modified response if RSC issue detected
- Returns null if no issueExample:
`javascript
// Fix specific pages
const rsc = await handleNextJS_RSC(request, {
affectedPaths: ["/shop", "/products", "/about"]
});
if (rsc) return rsc;// Fix all dynamic routes
const rsc = await handleNextJS_RSC(request, {
affectedPaths: ["/blog", "/products", "/categories"]
});
if (rsc) return rsc;
`What It Does:
Next.js RSC uses a special
rsc: 1 header. Sometimes, caches incorrectly serve RSC data (JSON) when a full page load is expected. This utility detects these cases and strips the header to ensure correct response type.Use Cases:
- Fix "JSON showing instead of page" issues
- Resolve RSC caching problems
- Ensure proper page hydration
Learn More: Handling Next.js RSC Issues
---
$3
####
getGeoHeaders(request)Extract geo-location data from Launch headers.
Parameters:
-
request (Request) - The incoming request objectReturns: Object with geo data
`typescript
{
country: string | null, // ISO country code (e.g., "US")
region: string | null, // Region/state code (e.g., "CA")
city: string | null, // City name (e.g., "San Francisco")
latitude: string | null, // Latitude coordinate
longitude: string | null // Longitude coordinate
}
`Example:
`javascript
// Get user location
const geo = getGeoHeaders(request);// Country-based logic
if (geo.country === "US") {
console.log(
US visitor from ${geo.city}, ${geo.region});
}// Region-specific content
if (geo.country === "US" && geo.region === "CA") {
// Show California-specific content
}
// Distance-based logic
if (geo.latitude && geo.longitude) {
const userLat = parseFloat(geo.latitude);
const userLon = parseFloat(geo.longitude);
// Calculate distance to store, etc.
}
// Redirect based on location
const geo = getGeoHeaders(request);
if (geo.country === "FR") {
return Response.redirect("https://fr.mysite.com", 302);
}
`Use Cases:
- Personalize content by location
- Show region-specific pricing
- Redirect to country-specific sites
- Display nearest store locations
- Comply with regional regulations
Learn More: Geolocation Headers in Launch
---
$3
####
jsonResponse(body, init?)Create JSON responses easily.
Parameters:
-
body (object) - JSON-serializable object
- init (ResponseInit, optional) - Additional response options (status, headers, etc.)Returns:
Response with Content-Type: application/jsonExample:
`javascript
// Simple JSON response
return jsonResponse({ status: "ok", message: "Success" });// With custom status
return jsonResponse(
{ error: "Not found" },
{ status: 404 }
);
// With custom headers
return jsonResponse(
{ data: [...] },
{
status: 200,
headers: {
"Cache-Control": "max-age=3600",
"X-Custom-Header": "value"
}
}
);
// API endpoint example
const url = new URL(request.url);
if (url.pathname === "/api/user") {
const geo = getGeoHeaders(request);
return jsonResponse({
user: "john_doe",
location: {
country: geo.country,
city: geo.city
},
timestamp: Date.now()
});
}
`Use Cases:
- Create API endpoints at the edge
- Return structured error messages
- Build serverless functions
---
####
passThrough(request)Forward request to origin server.
Parameters:
-
request (Request) - The incoming request objectReturns:
Promise from originExample:
`javascript
// Default: pass everything through
return passThrough(request);// After all checks
export default async function handler(request, context) {
// ... all your checks ...
// If nothing matched, pass to origin
return passThrough(request);
}
`Use Cases:
- Default fallback after edge logic
- Forward requests that don't need edge processing
---
$3
####
generateLaunchConfig(options)Generate
launch.json configuration programmatically.Parameters:
-
options (object)
- redirects (LaunchRedirect[], optional)
- rewrites (LaunchRewrite[], optional)
- cache (object, optional)
- cachePriming (object)
- urls (string[])Returns:
LaunchConfig objectTypes:
`typescript
interface LaunchRedirect {
source: string;
destination: string;
statusCode?: number;
response?: {
headers?: Record;
};
}interface LaunchRewrite {
source: string;
destination: string;
}
`Example:
`javascript
import { generateLaunchConfig } from "@aryanbansal-launch/edge-utils";
import fs from "fs";const config = generateLaunchConfig({
redirects: [
{
source: "/old-blog/:slug",
destination: "/blog/:slug",
statusCode: 301
},
{
source: "/products",
destination: "/shop",
statusCode: 308
}
],
rewrites: [
{
source: "/api/:path*",
destination: "https://api.mybackend.com/:path*"
}
],
cache: {
cachePriming: {
urls: ["/", "/about", "/products", "/contact"]
}
}
});
// Write to launch.json
fs.writeFileSync("launch.json", JSON.stringify(config, null, 2));
`Use Cases:
- Generate config from CMS data
- Automate bulk redirects
- Dynamic configuration management
---
šÆ Real-World Examples
$3
`javascript
export default async function handler(request, context) {
const url = new URL(request.url);
const geo = getGeoHeaders(request); // 1. Block bots to reduce costs
const botCheck = blockAICrawlers(request);
if (botCheck) return botCheck;
// 2. Geo-based redirects
if (geo.country === "UK" && !url.hostname.includes("uk.")) {
return Response.redirect(
https://uk.myshop.com${url.pathname}, 302);
} // 3. Maintenance mode for specific regions
if (geo.country === "US" && url.pathname.startsWith("/checkout")) {
return jsonResponse(
{ error: "Checkout temporarily unavailable in your region" },
{ status: 503 }
);
}
// 4. Product redirects
const redirect = redirectIfMatch(request, {
path: "/products/old-sku-123",
to: "/products/new-sku-456",
status: 301
});
if (redirect) return redirect;
return passThrough(request);
}
`$3
`javascript
export default async function handler(request, context) {
const url = new URL(request.url); // 1. Protect staging with Basic Auth
if (url.hostname.includes("staging")) {
const auth = await protectWithBasicAuth(request, {
hostnameIncludes: "staging",
username: "team",
password: process.env.STAGING_PASSWORD || "defaultpass"
});
if (auth && auth.status === 401) return auth;
}
// 2. Restrict dev environment to office IPs
if (url.hostname.includes("dev")) {
const ipCheck = ipAccessControl(request, {
allow: ["203.0.113.0/24"] // Office IP range
});
if (ipCheck) return ipCheck;
}
// 3. Block default domain on production
if (url.hostname.includes("myapp.com")) {
const domainCheck = blockDefaultDomains(request);
if (domainCheck) return domainCheck;
}
return passThrough(request);
}
`$3
`javascript
export default async function handler(request, context) {
const url = new URL(request.url);
const geo = getGeoHeaders(request); // 1. Fix RSC issues on dynamic pages
const rscCheck = await handleNextJS_RSC(request, {
affectedPaths: ["/blog", "/products", "/categories"]
});
if (rscCheck) return rscCheck;
// 2. Edge API endpoints
if (url.pathname === "/api/geo") {
return jsonResponse({
country: geo.country,
region: geo.region,
city: geo.city
});
}
if (url.pathname === "/api/health") {
return jsonResponse({
status: "healthy",
timestamp: Date.now(),
region: geo.region
});
}
// 3. Block bots from expensive pages
if (url.pathname.startsWith("/search")) {
const botCheck = blockAICrawlers(request);
if (botCheck) return botCheck;
}
return passThrough(request);
}
`---
š ļø CLI Commands
$3
Initialize edge functions with production-ready boilerplate.
What it does:
1. Checks you're in project root (where
package.json exists)
2. Creates functions/ directory if needed
3. Generates functions/[proxy].edge.js with example codeUsage:
`bash
cd /path/to/your/project
npx create-launch-edge
`Output:
`
š create-launch-edge: Contentstack Launch Initializer⨠New: Created /functions directory
⨠New: Created /functions/[proxy].edge.js
š Setup Complete!
Next Steps:
1. Open functions/[proxy].edge.js
2. Customize your redirects, auth, and RSC paths
3. Deploy your project to Contentstack Launch
`---
$3
Interactive CLI to manage
launch.json configuration with support for bulk imports.What it does:
- Add/manage redirects (one-by-one or bulk import)
- Configure rewrites
- Set up cache priming URLs
- Preserves existing configuration
Usage:
`bash
npx launch-config
`Interactive Prompts:
`
š Launch Configuration GeneratorHow would you like to add redirects?
1) One by one (interactive)
2) Bulk import from CSV file
3) Bulk import from JSON file
4) Skip
Choose (1-4): 2
Enter CSV file path (e.g., ./redirects.csv): ./redirects.csv
ā Imported 150 redirects from CSV.
Do you want to add a Rewrite? (y/n): y
Source path (e.g., /api/): /api/
Destination URL: https://backend.myapp.com/api/*
ā Rewrite added.
Do you want to add Cache Priming URLs? (y/n): y
Note: Only relative paths are supported. No Regex/Wildcards.
Enter URLs separated by commas (e.g., /home,/about,/shop): /,/about,/products
ā
Successfully updated launch.json!
`#### Bulk Import Formats
CSV Format (
redirects.csv):
`csv
source,destination,statusCode
/old-blog/post-1,/blog/post-1,301
/old-blog/post-2,/blog/post-2,301
/products/old-sku-123,/products/new-sku-456,308
/legacy/,/new/,301
`JSON Format (
redirects.json):
`json
[
{
"source": "/old-blog/post-1",
"destination": "/blog/post-1",
"statusCode": 301
},
{
"source": "/old-blog/post-2",
"destination": "/blog/post-2",
"statusCode": 301
},
{
"source": "/products/old-sku-123",
"destination": "/products/new-sku-456",
"statusCode": 308
}
]
`Use Cases for Bulk Import:
- Migrating from another platform with hundreds of URLs
- SEO redirects from spreadsheet/database exports
- Bulk product SKU changes
- Content restructuring with many path changes
Example Files:
See
examples/redirects.csv and examples/redirects.json in this package for ready-to-use templates.Learn More:
- Static Redirects
- Cache Priming
---
$3
Display complete reference guide with all methods and examples.
Usage:
`bash
npx launch-help
`Shows:
- All available methods with parameters
- Return types and examples
- CLI commands
- Quick links to documentation
---
š Platform Support
This library is exclusively optimized for Contentstack Launch. It assumes an environment where:
- Standard Web APIs (
Request, Response, fetch) are available
- Edge runtime environment
- Contentstack Launch geo-location headersNot compatible with:
- Node.js servers
- Traditional hosting platforms
- Other edge platforms (Cloudflare Workers, Vercel Edge, etc.)
---
š¤ Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
Repository: https://github.com/AryanBansal-launch/launch-edge-utils
---
š License
Distributed under the MIT License. See
LICENSE` for more information.---
- Contentstack Launch Documentation
- Edge Functions Guide
- NPM Package
- GitHub Repository
---
Made with ā¤ļø for the Contentstack Launch community