Add includeAllManaApps option to enable all ManaCore apps to communicate with each other without manually listing each app's domains. **Changes:** - Added MANACORE_STAGING_ORIGINS, MANACORE_PRODUCTION_ORIGINS, and MANACORE_ALL_APP_ORIGINS constants - Added includeAllManaApps flag to CorsConfigOptions interface - Updated createCorsConfig() and createCorsConfigWithCallback() to support the new flag - Updated mana-core-auth to use includeAllManaApps: true (auth needs to be accessible by all apps) - Updated documentation with usage examples and decision matrix **Benefits:** - One-line configuration enables cross-app communication - Automatically stays in sync as new apps are added - No need to manually update CORS_ORIGINS for each app - Works in both staging and production environments 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
CORS Configuration Guide
Problem
Every deployed app on staging was encountering CORS errors:
Access to fetch at 'https://chat-api.staging.manacore.ai/api/v1/...' from origin
'https://chat.staging.manacore.ai' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Root Causes
- Missing
CORS_ORIGINSenvironment variable in docker-compose.staging.yml - Inconsistent CORS configuration across backends (different patterns in each main.ts)
- No centralized CORS management leading to missing configurations during deployment
Solution
Created @manacore/shared-nestjs-cors package providing standardized CORS configuration for all backends.
Package Structure
packages/shared-nestjs-cors/
├── src/
│ ├── cors-config.ts # CORS configuration utilities
│ └── index.ts # Public exports
├── package.json
├── tsconfig.json
└── README.md
Key Features
✅ Automatic development origins - Works in dev without configuration
✅ Staging/production via env var - CORS_ORIGINS for deployed environments
✅ Cross-app communication bundle - Enable all ManaCore apps with one flag
✅ Mobile app support - Includes exp:// and custom protocols
✅ Prevents duplicates - Deduplicates origin lists
✅ Consistent security - Same methods, headers, credentials across all apps
Usage
1. Add Package Dependency
// apps/{app}/apps/backend/package.json
{
"dependencies": {
"@manacore/shared-nestjs-cors": "workspace:*"
}
}
2. Update main.ts
Option A: Basic Setup (Single App)
For apps that only need to be accessed by their own frontend:
// apps/{app}/apps/backend/src/main.ts
import { NestFactory } from '@nestjs/core';
import { createCorsConfig } from '@manacore/shared-nestjs-cors';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Enable CORS with centralized configuration
app.enableCors(
createCorsConfig({
corsOriginsEnv: process.env.CORS_ORIGINS,
})
);
await app.listen(3000);
}
bootstrap();
Option B: Cross-App Communication (Recommended for Shared Services)
For backends that need to communicate with multiple ManaCore apps:
// apps/{app}/apps/backend/src/main.ts
import { NestFactory } from '@nestjs/core';
import { createCorsConfig } from '@manacore/shared-nestjs-cors';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Enable CORS with ALL ManaCore apps (staging + production)
app.enableCors(
createCorsConfig({
corsOriginsEnv: process.env.CORS_ORIGINS,
includeAllManaApps: true, // 🎯 Enables cross-app communication
})
);
await app.listen(3000);
}
bootstrap();
When to use includeAllManaApps: true:
- ✅ Shared services (auth, notifications, file storage)
- ✅ APIs that need to be called from multiple apps
- ✅ Apps with cross-app navigation or embedded widgets
- ❌ Simple single-purpose apps with dedicated frontend (saves bandwidth)
3. Configure Staging Environment
# docker-compose.staging.yml
chat-backend:
environment:
# CORS - Allow chat web app and main web app to access backend
CORS_ORIGINS: https://chat.staging.manacore.ai,https://staging.manacore.ai,http://localhost:3000,http://localhost:5173
Default Origins
Development Origins
The utility automatically includes these development origins:
// Common development ports (always available)
[
'http://localhost:3000', // Chat web (production build)
'http://localhost:3001', // Auth service
'http://localhost:3002', // Chat backend
'http://localhost:5173', // Main web (Vite)
'http://localhost:5174-5190', // Additional Vite instances
'http://localhost:8081', // Expo mobile
'exp://localhost:8081', // Expo mobile (exp:// protocol)
]
ManaCore App Bundles
When using includeAllManaApps: true, the following origin bundles are automatically included:
Staging Bundle (MANACORE_STAGING_ORIGINS)
[
'https://staging.manacore.ai', // Main web
'https://auth.staging.manacore.ai', // Auth service
'https://chat.staging.manacore.ai', // Chat app
'https://chat-api.staging.manacore.ai',
'https://picture.staging.manacore.ai', // Picture app
'https://picture-api.staging.manacore.ai',
'https://zitare.staging.manacore.ai', // Zitare app
'https://zitare-api.staging.manacore.ai',
'https://contacts.staging.manacore.ai', // Contacts app
'https://contacts-api.staging.manacore.ai',
'https://calendar.staging.manacore.ai', // Calendar app
'https://calendar-api.staging.manacore.ai',
'https://clock.staging.manacore.ai', // Clock app
'https://clock-api.staging.manacore.ai',
'https://todo.staging.manacore.ai', // Todo app
'https://todo-api.staging.manacore.ai',
]
Production Bundle (MANACORE_PRODUCTION_ORIGINS)
[
'https://manacore.ai', // Main web
'https://auth.manacore.ai', // Auth service
'https://chat.manacore.ai', // Chat app
'https://chat-api.manacore.ai',
'https://picture.manacore.ai', // Picture app
'https://picture-api.manacore.ai',
// ... all other production apps
]
Combined Bundle (MANACORE_ALL_APP_ORIGINS)
When includeAllManaApps: true, both staging and production bundles are included automatically.
Benefits:
- 🎯 One-line configuration enables all cross-app communication
- 🔄 Automatically stays in sync as new apps are added
- 🚀 No need to manually update CORS_ORIGINS for each app
- ✅ Works in both staging and production environments
Cross-App Communication
Problem: Apps Need to Call Each Other
In a microservices architecture, apps often need to communicate:
- Chat app fetches user profiles from Contacts API
- Calendar app sends notifications via Notifications service
- Picture app uses Auth service for authentication
Without proper CORS setup, each backend would need to manually list every other app:
# ❌ OLD WAY: Manually list every app (tedious and error-prone)
chat-backend:
environment:
CORS_ORIGINS: https://chat.staging.manacore.ai,https://contacts.staging.manacore.ai,https://calendar.staging.manacore.ai,https://picture.staging.manacore.ai,https://zitare.staging.manacore.ai,...
Solution: Use includeAllManaApps: true
Enable cross-app communication with one flag:
// ✅ NEW WAY: One flag enables all ManaCore apps
app.enableCors(
createCorsConfig({
corsOriginsEnv: process.env.CORS_ORIGINS,
includeAllManaApps: true, // 🎯 Automatically includes all apps
})
);
This automatically allows requests from:
- All staging apps (
*.staging.manacore.ai) - All production apps (
*.manacore.ai) - All development ports (
localhost:*)
When to Use Cross-App Bundle
| Use Case | includeAllManaApps |
|---|---|
| Shared services (Auth, Notifications, Storage) | ✅ true |
| Public APIs called by multiple apps | ✅ true |
| Apps with embedded widgets from other apps | ✅ true |
| Simple apps with dedicated frontend only | ❌ false |
| Third-party integrations (webhooks) | ❌ false (use additionalOrigins) |
Deployment Checklist
When deploying a new app to staging, ensure:
✅ Backend Configuration
- Add
@manacore/shared-nestjs-corsdependency topackage.json - Update
main.tsto usecreateCorsConfig() - Add
CORS_ORIGINSto service environment indocker-compose.staging.yml
✅ Docker Compose
{app}-backend:
environment:
CORS_ORIGINS: https://{app}.staging.manacore.ai,https://{app}-api.staging.manacore.ai,https://staging.manacore.ai,http://localhost:5XXX,http://localhost:5173
Pattern:
- App's web frontend:
https://{app}.staging.manacore.ai - App's API endpoint:
https://{app}-api.staging.manacore.ai - Main web app:
https://staging.manacore.ai - Local development:
http://localhost:{web-port},http://localhost:5173
✅ Verification Steps
- Build and deploy the backend with new CORS config
- Open browser DevTools (Network tab)
- Navigate to
https://{app}.staging.manacore.ai - Check API requests - should show
Access-Control-Allow-Originheader - Verify no CORS errors in console
Troubleshooting
CORS error still appears after deployment
Symptoms:
- CORS errors persist after adding
CORS_ORIGINS - Container logs show correct config
Solutions:
-
Restart backend container
ssh -i ~/.ssh/hetzner_deploy_key deploy@46.224.108.214 cd ~/manacore-staging docker compose restart {app}-backend -
Verify environment variable
docker exec {app}-backend-staging printenv | grep CORS -
Check backend logs
docker logs {app}-backend-staging | grep -i cors -
Rebuild if package was added
# Rebuild Docker image with new dependency docker compose build {app}-backend docker compose up -d {app}-backend
Mobile app can't connect
Solution: Use callback-based CORS for mobile apps:
import { createCorsConfigWithCallback } from '@manacore/shared-nestjs-cors';
app.enableCors(
createCorsConfigWithCallback({
corsOriginsEnv: process.env.CORS_ORIGINS,
})
);
This allows requests with no Origin header (common for mobile apps).
Development origins not working
Verification:
# Test CORS in development
curl -H "Origin: http://localhost:5173" \
-H "Access-Control-Request-Method: POST" \
-X OPTIONS \
http://localhost:3002/api/v1/health
Should return:
Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Credentials: true
Migration from Manual CORS
Before (Manual Configuration)
// ❌ Different in every backend
const corsOrigins = process.env.CORS_ORIGINS?.split(',').map(o => o.trim()) || [
'http://localhost:3000',
'http://localhost:5173',
// Different defaults per app
];
app.enableCors({
origin: corsOrigins,
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
});
After (Centralized Configuration)
// ✅ Consistent everywhere
import { createCorsConfig } from '@manacore/shared-nestjs-cors';
app.enableCors(
createCorsConfig({
corsOriginsEnv: process.env.CORS_ORIGINS,
})
);
Updated Applications
The following backends have been migrated to use @manacore/shared-nestjs-cors:
- ✅ chat-backend (apps/chat/apps/backend)
- ✅ picture-backend (apps/picture/apps/backend)
- ✅ zitare-backend (apps/zitare/apps/backend)
- ✅ contacts-backend (apps/contacts/apps/backend)
- ✅ calendar-backend (apps/calendar/apps/backend)
- ✅ clock-backend (apps/clock/apps/backend)
- ✅ todo-backend (apps/todo/apps/backend)
Best Practices
- Always use
@manacore/shared-nestjs-corsfor new backends - Always add
CORS_ORIGINSto docker-compose when deploying - Include both app domain and API domain in CORS_ORIGINS
- Include
staging.manacore.aifor cross-app navigation - Keep localhost ports for local development testing
Related Documentation
- Staging Setup Guide - Complete staging deployment guide
- Package README - Detailed API documentation
- Deployment Architecture - Infrastructure overview
Last Updated: 2025-12-17 Author: Claude Code (Automated CORS Solution)