Production Deployment
Deploy your dcupl application to production with this comprehensive checklist and best practices guide. Covers performance optimization, security, monitoring, and common deployment scenarios.
Pre-Deployment Checklist
Code Quality
- All quality validation errors resolved
- TypeScript compilation passes without errors
- Tests passing (unit, integration)
- No console.log statements in production code
- Error handling implemented for all async operations
Configuration
- Production environment variables configured
- API keys rotated and secured
- Published version of Console configuration
- Quality validation disabled for performance
- CDN URLs verified
Performance
- Bundle size optimized (tree shaking working)
- Data loading strategy defined
- Caching strategy implemented
- Large dataset handling tested
Security
- Secrets not committed to repository
- API keys scoped to production project
- CORS configured correctly
- Content Security Policy set
Production Configuration
Optimized dcupl Setup
src/config/production.ts
import { Dcupl } from '@dcupl/core';
import { DcuplAppLoader } from '@dcupl/loader';
export function createProductionDcupl() {
const dcupl = new Dcupl({
config: {
projectId: process.env.DCUPL_PROJECT_ID!,
apiKey: process.env.DCUPL_API_KEY!,
},
quality: {
enabled: false, // Disable for performance
},
});
const loader = new DcuplAppLoader();
dcupl.loaders.add(loader);
return { dcupl, loader };
}
export async function initializeProduction() {
const { dcupl, loader } = createProductionDcupl();
try {
// Fetch published configuration
await loader.config.fetch();
await loader.process({ applicationKey: 'default' });
await dcupl.init();
return dcupl;
} catch (error) {
console.error('Failed to initialize dcupl:', error);
throw error;
}
}Environment Variables
.env.production
# Production Configuration
DCUPL_PROJECT_ID=prod-project-id
DCUPL_API_KEY=prod-api-key-xxx
NODE_ENV=productionPerformance Optimization
1. Disable Quality Validation
Quality validation adds overhead. Disable in production:
const dcupl = new Dcupl({
quality: {
enabled: false, // Production: disabled
},
});Impact: 10-30% faster initialization
2. Use Partial Updates
Avoid full data reloads when possible:
// Bad: Full reload
dcupl.data.set(allData, { model: 'Product' });
await dcupl.init();
// Good: Partial update (10-100x faster)
dcupl.data.update({
model: 'Product',
data: [{ key: 'p1', price: 999 }],
});3. Implement List Pooling
Reuse lists instead of creating new ones:
class ListPool {
private pool: Map<string, DcuplList[]> = new Map();
get(dcupl: Dcupl, modelKey: string): DcuplList {
const pool = this.pool.get(modelKey) || [];
if (pool.length > 0) {
return pool.pop()!;
}
return dcupl.lists.create({ modelKey });
}
release(list: DcuplList) {
list.catalog.query.clear();
const modelKey = list.model.key;
const pool = this.pool.get(modelKey) || [];
pool.push(list);
this.pool.set(modelKey, pool);
}
}4. Use Pagination
For large result sets:
const productList = dcupl.lists.create({
modelKey: 'Product',
pagination: {
page: 1,
pageSize: 50, // Reasonable page size
},
});5. Lazy Load Non-Critical Data
Load essential data first, supplementary data later:
async function initializeApp(dcupl: Dcupl) {
// 1. Load critical data
await loadCriticalData(dcupl);
// 2. Render initial UI
renderApp();
// 3. Load supplementary data in background
loadSupplementaryData(dcupl);
}Error Handling
Graceful Initialization
export async function initWithFallback() {
const { dcupl, loader } = createProductionDcupl();
try {
await loader.config.fetch();
await loader.process({ applicationKey: 'default' });
await dcupl.init();
return { dcupl, status: 'ready' };
} catch (error) {
console.error('dcupl initialization failed:', error);
// Option 1: Return degraded state
return { dcupl: null, status: 'error', error };
// Option 2: Retry with exponential backoff
// return retryInit(dcupl, loader);
}
}Retry Logic
async function retryInit(
dcupl: Dcupl,
loader: DcuplAppLoader,
maxRetries = 3
): Promise<Dcupl> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
await loader.config.fetch();
await loader.process({ applicationKey: 'default' });
await dcupl.init();
return dcupl;
} catch (error) {
console.error(`Init attempt ${attempt} failed:`, error);
if (attempt === maxRetries) {
throw new Error(`Failed after ${maxRetries} attempts`);
}
// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, 1000 * Math.pow(2, attempt - 1))
);
}
}
throw new Error('Unreachable');
}Query Error Handling
function safeQuery(list: DcuplList) {
try {
return list.catalog.query.items();
} catch (error) {
console.error('Query failed:', error);
return []; // Return empty array as fallback
}
}Monitoring
Performance Metrics
async function initWithMetrics(dcupl: Dcupl, loader: DcuplAppLoader) {
const startTime = performance.now();
await loader.config.fetch();
const configTime = performance.now();
await loader.process({ applicationKey: 'default' });
const processTime = performance.now();
await dcupl.init();
const initTime = performance.now();
// Log metrics
console.log({
configFetch: configTime - startTime,
process: processTime - configTime,
init: initTime - processTime,
total: initTime - startTime,
});
// Send to analytics
sendMetrics({
dcuplConfigFetch: configTime - startTime,
dcuplProcess: processTime - configTime,
dcuplInit: initTime - processTime,
});
}Quality Analytics (Development Only)
if (process.env.NODE_ENV !== 'production') {
await dcupl.init();
const analytics = dcupl.quality.values;
console.log({
loading: analytics.loading,
processing: analytics.processing,
startup: analytics.startup,
counts: analytics.counts,
errors: dcupl.quality.getErrors(),
});
}Health Check Endpoint
// Express/Node.js example
app.get('/health/dcupl', (req, res) => {
try {
// Check if dcupl is initialized
const list = dcupl.lists.create({ modelKey: 'Product' });
const count = list.catalog.query.count();
list.destroy();
res.json({
status: 'healthy',
productCount: count,
timestamp: new Date().toISOString(),
});
} catch (error) {
res.status(503).json({
status: 'unhealthy',
error: error.message,
timestamp: new Date().toISOString(),
});
}
});Deployment Strategies
Static Site (Vercel, Netlify)
src/lib/dcupl.ts
let dcuplInstance: Dcupl | null = null;
export async function getDcupl(): Promise<Dcupl> {
if (dcuplInstance) {
return dcuplInstance;
}
const dcupl = new Dcupl({
config: {
projectId: process.env.NEXT_PUBLIC_DCUPL_PROJECT_ID!,
apiKey: process.env.NEXT_PUBLIC_DCUPL_API_KEY!,
},
quality: { enabled: false },
});
const loader = new DcuplAppLoader();
dcupl.loaders.add(loader);
await loader.config.fetch();
await loader.process({ applicationKey: 'default' });
await dcupl.init();
dcuplInstance = dcupl;
return dcupl;
}Docker Container
Dockerfile
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN npm ci --only=production
# Copy app files
COPY . .
# Build
RUN npm run build
# Set environment
ENV NODE_ENV=production
# Run
CMD ["npm", "start"]docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- '3000:3000'
environment:
- NODE_ENV=production
- DCUPL_PROJECT_ID=${DCUPL_PROJECT_ID}
- DCUPL_API_KEY=${DCUPL_API_KEY}Kubernetes
k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dcupl-app
spec:
replicas: 3
selector:
matchLabels:
app: dcupl-app
template:
metadata:
labels:
app: dcupl-app
spec:
containers:
- name: app
image: your-registry/dcupl-app:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: 'production'
- name: DCUPL_PROJECT_ID
valueFrom:
secretKeyRef:
name: dcupl-secrets
key: project-id
- name: DCUPL_API_KEY
valueFrom:
secretKeyRef:
name: dcupl-secrets
key: api-key
resources:
requests:
memory: '256Mi'
cpu: '100m'
limits:
memory: '512Mi'
cpu: '500m'
livenessProbe:
httpGet:
path: /health/dcupl
port: 3000
initialDelaySeconds: 30
periodSeconds: 10Security Considerations
API Key Management
// Never hardcode API keys
const dcupl = new Dcupl({
config: {
projectId: process.env.DCUPL_PROJECT_ID!, // From environment
apiKey: process.env.DCUPL_API_KEY!, // From environment
},
});Secrets Storage
.github/workflows/deploy.yml
# GitHub Actions example
- name: Deploy
env:
DCUPL_PROJECT_ID: ${{ secrets.DCUPL_PROJECT_ID }}
DCUPL_API_KEY: ${{ secrets.DCUPL_API_KEY }}
run: npm run deployContent Security Policy
// Next.js example
const cspHeader = `
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline';
connect-src 'self' https://cdn.dcupl.com https://api.dcupl.com;
img-src 'self' blob: data:;
`;Post-Deployment Checklist
Immediate Checks
- Application loads without errors
- dcupl initialization succeeds
- Queries return expected results
- No console errors in browser
- Health check endpoint responds
Ongoing Monitoring
- Error tracking configured (Sentry, etc.)
- Performance monitoring active
- Alerting for initialization failures
- Log aggregation configured
Rollback Plan
- Previous version tagged and deployable
- Rollback procedure documented
- Team aware of rollback process
Troubleshooting Production Issues
Slow Initialization
Symptoms: Long loading time before app is usable
Solutions:
- Check network latency to CDN
- Verify configuration file size
- Implement loading indicator
- Consider progressive loading
Memory Issues
Symptoms: Out of memory errors, slow performance
Solutions:
- Reduce dataset size
- Implement pagination
- Destroy unused lists
- Monitor with
dcupl.quality.values
Stale Data
Symptoms: Old data showing after publish
Solutions:
- Verify published version is selected
- Check CDN cache invalidation
- Implement cache-busting
- Force reload configuration
What's Next?
- Performance Guide - Optimization strategies
- Caching Strategies - Cache management
- Common Mistakes - Avoid common pitfalls