π Production Deployment Guide
Complete guide for deploying GoCharting SDK in production environments with best practices, performance optimization, and monitoring.
π Pre-Deployment Checklist
β License and Legal
- Valid production license key obtained
- License domain matches production domain
- Terms of service reviewed and accepted
- Compliance requirements verified
β Technical Requirements
- Node.js 16+ installed (for build process)
- Modern browser support verified
- CDN/hosting infrastructure ready
- SSL certificate configured
- Monitoring tools set up
β Performance Testing
- Load testing completed
- Memory usage profiled
- Network latency measured
- Error handling tested
- Fallback mechanisms verified
ποΈ Deployment Architecture
Recommended Architecture
Infrastructure Components
- CDN: Serve static SDK files
- Load Balancer: Distribute traffic
- Web Servers: Host your application
- Data APIs: Provide market data
- WebSocket Servers: Real-time data feeds
- Monitoring: Track performance and errors
π§ Build Configuration
Production Build Setup
// webpack.config.prod.js
const path = require("path");
module.exports = {
mode: "production",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[contenthash].js",
publicPath: "/static/",
},
optimization: {
splitChunks: {
chunks: "all",
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all",
},
gocharting: {
test: /[\\/]node_modules[\\/]gocharting-sdk[\\/]/,
name: "gocharting",
chunks: "all",
},
},
},
},
externals: {
// Use CDN versions for better caching
react: "React",
"react-dom": "ReactDOM",
},
};Environment Configuration
// config/production.js
module.exports = {
gocharting: {
licenseKey: process.env.GOCHARTING_LICENSE_KEY,
apiBaseUrl: process.env.API_BASE_URL,
websocketUrl: process.env.WEBSOCKET_URL,
// Performance settings
performance: {
maxCandles: 10000,
updateFrequency: 100,
animations: false, // Disable for better performance
preloadData: true,
},
// Error handling
errorReporting: {
enabled: true,
endpoint: process.env.ERROR_REPORTING_URL,
},
// Monitoring
monitoring: {
enabled: true,
endpoint: process.env.MONITORING_URL,
sampleRate: 0.1, // 10% sampling
},
},
};π CDN and Caching Strategy
CDN Configuration
# nginx.conf
server {
listen 443 ssl http2;
server_name your-domain.com;
# SSL configuration
ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;
# Gzip compression
gzip on;
gzip_types text/css application/javascript application/json;
# Static assets with long cache
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header X-Content-Type-Options nosniff;
}
# GoCharting SDK files
location /sdk/ {
expires 30d;
add_header Cache-Control "public";
add_header Access-Control-Allow-Origin "*";
}
# API endpoints
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# CORS headers
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
}
}Cache Headers
// Express.js cache configuration
app.use(
"/sdk",
express.static("node_modules/gocharting-sdk/dist", {
maxAge: "30d",
etag: true,
lastModified: true,
setHeaders: (res, path) => {
if (path.endsWith(".js")) {
res.setHeader("Cache-Control", "public, max-age=2592000"); // 30 days
}
if (path.endsWith(".css")) {
res.setHeader("Cache-Control", "public, max-age=2592000"); // 30 days
}
},
})
);β‘ Performance Optimization
Bundle Optimization
// Lazy load SDK for better initial page load
const loadChart = async () => {
const { createChart, DataProvider } = await import("gocharting-sdk");
return { createChart, DataProvider };
};
// Use dynamic imports
const initChart = async () => {
const { createChart } = await loadChart();
const chart = createChart("#chart", {
symbol: "AAPL",
interval: "1D",
datafeed: myDatafeed,
licenseKey: "YOUR_LICENSE_KEY",
// ... other configuration
});
return chart;
};Memory Management
class ChartManager {
constructor() {
this.charts = new Map();
this.cleanupInterval = setInterval(() => {
this.cleanup();
}, 300000); // Cleanup every 5 minutes
}
createChart(id, config) {
// Destroy existing chart if any
if (this.charts.has(id)) {
this.destroyChart(id);
}
const chart = new ProfessionalChart(config);
this.charts.set(id, {
instance: chart,
lastUsed: Date.now(),
});
return chart;
}
destroyChart(id) {
const chartData = this.charts.get(id);
if (chartData) {
chartData.instance.destroy();
this.charts.delete(id);
}
}
cleanup() {
const now = Date.now();
const maxAge = 30 * 60 * 1000; // 30 minutes
for (const [id, chartData] of this.charts) {
if (now - chartData.lastUsed > maxAge) {
this.destroyChart(id);
}
}
}
destroy() {
clearInterval(this.cleanupInterval);
for (const id of this.charts.keys()) {
this.destroyChart(id);
}
}
}Data Optimization
class OptimizedDataProvider extends DataProvider {
constructor() {
super();
this.cache = new Map();
this.maxCacheSize = 100;
}
async getBars(symbolInfo, resolution, periodParams) {
const cacheKey = `${symbolInfo.name}-${resolution}-${periodParams.from}-${periodParams.to}`;
// Check cache first
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
// Fetch data
const data = await this.fetchBarsFromAPI(
symbolInfo,
resolution,
periodParams
);
// Cache with size limit
if (this.cache.size >= this.maxCacheSize) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(cacheKey, data);
return data;
}
// Implement data compression for large datasets
compressData(bars) {
return bars.map((bar) => ({
t: bar.time,
o: Math.round(bar.open * 100) / 100,
h: Math.round(bar.high * 100) / 100,
l: Math.round(bar.low * 100) / 100,
c: Math.round(bar.close * 100) / 100,
v: bar.volume,
}));
}
}π Monitoring and Analytics
Error Tracking
// Error tracking setup
class ErrorTracker {
constructor(config) {
this.config = config;
this.setupGlobalErrorHandling();
}
setupGlobalErrorHandling() {
window.addEventListener("error", (event) => {
this.reportError({
type: "javascript",
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error?.stack,
});
});
window.addEventListener("unhandledrejection", (event) => {
this.reportError({
type: "promise",
message: event.reason?.message || "Unhandled promise rejection",
stack: event.reason?.stack,
});
});
}
reportError(error) {
if (!this.config.enabled) return;
fetch(this.config.endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
...error,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
url: window.location.href,
}),
}).catch(console.error);
}
}
// Initialize error tracking
const errorTracker = new ErrorTracker({
enabled: true,
endpoint: "/api/errors",
});Performance Monitoring
// Performance monitoring
class PerformanceMonitor {
constructor() {
this.metrics = {};
this.startTime = performance.now();
}
mark(name) {
this.metrics[name] = performance.now() - this.startTime;
}
measure(name, startMark, endMark) {
const start = this.metrics[startMark] || 0;
const end = this.metrics[endMark] || performance.now() - this.startTime;
this.metrics[name] = end - start;
}
report() {
// Send metrics to monitoring service
fetch("/api/metrics", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
metrics: this.metrics,
timestamp: new Date().toISOString(),
}),
});
}
}
// Usage
const monitor = new PerformanceMonitor();
monitor.mark("chart-init-start");
const chart = new ProfessionalChart(config);
chart.onChartReady(() => {
monitor.mark("chart-init-end");
monitor.measure(
"chart-initialization",
"chart-init-start",
"chart-init-end"
);
monitor.report();
});π Security Best Practices
Content Security Policy
<!-- CSP headers -->
<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self';
script-src 'self' 'unsafe-inline' https://gocharting.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.yourbroker.com wss://ws.yourbroker.com;
img-src 'self' data: https:;
"
/>API Security
// Secure API configuration
class SecureDataProvider extends DataProvider {
constructor(config) {
super();
this.apiKey = config.apiKey;
this.baseUrl = config.baseUrl;
this.rateLimiter = new RateLimiter(100, 60000); // 100 requests per minute
}
async makeRequest(endpoint, options = {}) {
// Rate limiting
await this.rateLimiter.acquire();
// Add authentication
const headers = {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
...options.headers,
};
// Request timeout
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
try {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers,
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(
`HTTP ${response.status}: ${response.statusText}`
);
}
return await response.json();
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}
}π Deployment Scripts
Automated Deployment
#!/bin/bash
# deploy.sh
set -e
echo "Starting GoCharting SDK deployment..."
# Build application
echo "Building application..."
npm run build
# Run tests
echo "Running tests..."
npm test
# Deploy to staging
echo "Deploying to staging..."
rsync -avz --delete dist/ staging-server:/var/www/app/
# Run smoke tests
echo "Running smoke tests..."
npm run test:smoke
# Deploy to production
echo "Deploying to production..."
rsync -avz --delete dist/ production-server:/var/www/app/
# Verify deployment
echo "Verifying deployment..."
curl -f https://your-domain.com/health || exit 1
echo "Deployment completed successfully!"Health Check Endpoint
// health.js
app.get("/health", (req, res) => {
const health = {
status: "healthy",
timestamp: new Date().toISOString(),
version: process.env.APP_VERSION,
checks: {
database: "healthy",
api: "healthy",
gocharting: "healthy",
},
};
// Check GoCharting SDK availability
try {
// Verify license key
const licenseValid = validateLicense(
process.env.GOCHARTING_LICENSE_KEY
);
health.checks.gocharting = licenseValid ? "healthy" : "unhealthy";
} catch (error) {
health.checks.gocharting = "unhealthy";
health.status = "unhealthy";
}
const statusCode = health.status === "healthy" ? 200 : 503;
res.status(statusCode).json(health);
});π Scaling Considerations
Horizontal Scaling
# docker-compose.yml
version: "3.8"
services:
app:
image: your-app:latest
replicas: 3
environment:
- GOCHARTING_LICENSE_KEY=${GOCHARTING_LICENSE_KEY}
- API_BASE_URL=${API_BASE_URL}
ports:
- "3000-3002:3000"
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- appLoad Testing
// load-test.js
const { check } = require("k6");
const http = require("k6/http");
export let options = {
stages: [
{ duration: "2m", target: 100 }, // Ramp up
{ duration: "5m", target: 100 }, // Stay at 100 users
{ duration: "2m", target: 200 }, // Ramp up to 200 users
{ duration: "5m", target: 200 }, // Stay at 200 users
{ duration: "2m", target: 0 }, // Ramp down
],
};
export default function () {
const response = http.get("https://your-domain.com/chart");
check(response, {
"status is 200": (r) => r.status === 200,
"response time < 2s": (r) => r.timings.duration < 2000,
});
}π Related Documentation
- Configuration Guide - Chart configuration options
- Licensing Guide - License management
- API Reference - Complete API documentation
- Troubleshooting - Common issues and solutions
Follow these guidelines for a successful production deployment of GoCharting SDK!
Last updated on