Production Hardening Guide
Security, validation, and best practices for production Gati deployments using @gati-framework/production-hardening.
Overview
Production hardening ensures your Gati application is secure, reliable, and performant in production environments.
Installation
bash
pnpm add @gati-framework/production-hardeningSecurity
Input Validation
typescript
import { z } from 'zod';
import type { Handler } from '@gati-framework/runtime';
const CreateUserSchema = z.object({
email: z.string().email(),
password: z.string().min(8).max(128),
name: z.string().min(1).max(100)
});
export const handler: Handler = async (req, res, gctx, lctx) => {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
lctx.logger.warn('Validation failed', { errors: result.error.errors });
return res.status(400).json({
error: 'Validation failed',
details: result.error.errors
});
}
const user = await createUser(result.data);
res.status(201).json({ user });
};Rate Limiting
typescript
import { createRateLimiter } from '@gati-framework/production-hardening';
const rateLimiter = createRateLimiter({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: 'Too many requests, please try again later'
});
app.use(rateLimiter);CORS Configuration
typescript
import { configureCORS } from '@gati-framework/production-hardening';
app.use(configureCORS({
origin: ['https://example.com', 'https://app.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400 // 24 hours
}));Helmet Security Headers
typescript
import { securityHeaders } from '@gati-framework/production-hardening';
app.use(securityHeaders({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:']
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));Secrets Management
Environment Variables
typescript
import { loadSecrets } from '@gati-framework/production-hardening';
// Load from environment
const secrets = loadSecrets({
DATABASE_URL: { required: true },
API_KEY: { required: true },
JWT_SECRET: { required: true },
REDIS_URL: { required: false, default: 'redis://localhost:6379' }
});
// Use in app
const app = createApp({
config: {
databaseUrl: secrets.DATABASE_URL,
apiKey: secrets.API_KEY
}
});AWS Secrets Manager
typescript
import { loadFromAWSSecrets } from '@gati-framework/production-hardening';
const secrets = await loadFromAWSSecrets({
secretName: 'gati/production',
region: 'us-east-1'
});Kubernetes Secrets
bash
# Create secret
kubectl create secret generic gati-secrets \
--from-literal=DATABASE_URL=postgresql://... \
--from-literal=API_KEY=secret123
# Reference in deployment
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: gati-secrets
key: DATABASE_URLError Handling
Production Error Responses
typescript
export const handler: Handler = async (req, res, gctx, lctx) => {
try {
const result = await operation();
res.json({ result });
} catch (error) {
// Log full error internally
lctx.logger.error('Operation failed', {
error,
stack: error.stack,
userId: req.params.id
});
// Return generic error to client
res.status(500).json({
error: 'Internal server error',
requestId: lctx.requestId,
timestamp: new Date().toISOString()
});
}
};Error Monitoring
typescript
import { setupErrorMonitoring } from '@gati-framework/production-hardening';
setupErrorMonitoring({
service: 'gati-app',
environment: process.env.NODE_ENV,
sentry: {
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 0.1
}
});Health Checks
Basic Health Check
typescript
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});Comprehensive Health Check
typescript
import { createHealthCheck } from '@gati-framework/production-hardening';
const healthCheck = createHealthCheck({
checks: {
database: async () => {
await db.query('SELECT 1');
return { status: 'healthy' };
},
redis: async () => {
await redis.ping();
return { status: 'healthy' };
},
externalAPI: async () => {
const response = await fetch('https://api.example.com/health');
return { status: response.ok ? 'healthy' : 'unhealthy' };
}
},
timeout: 5000
});
app.get('/health', healthCheck);Logging
Structured Logging
typescript
export const handler: Handler = async (req, res, gctx, lctx) => {
lctx.logger.info('Processing request', {
method: req.method,
path: req.path,
userId: req.params.id
});
const user = await getUser(req.params.id);
lctx.logger.info('Request completed', {
userId: user.id,
duration: Date.now() - lctx.startTime
});
res.json({ user });
};Log Levels
typescript
// Production: Only log warnings and errors
if (process.env.NODE_ENV === 'production') {
logger.level = 'warn';
}
// Development: Log everything
if (process.env.NODE_ENV === 'development') {
logger.level = 'debug';
}Performance
Connection Pooling
typescript
import { createPool } from '@gati-framework/production-hardening';
const dbPool = createPool({
host: process.env.DB_HOST,
port: 5432,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
min: 2,
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000
});Caching
typescript
import { createCache } from '@gati-framework/production-hardening';
const cache = createCache({
type: 'redis',
url: process.env.REDIS_URL,
ttl: 3600 // 1 hour
});
export const handler: Handler = async (req, res, gctx, lctx) => {
const cacheKey = `user:${req.params.id}`;
// Check cache
const cached = await cache.get(cacheKey);
if (cached) {
lctx.logger.info('Cache hit');
return res.json({ user: cached });
}
// Fetch from database
const user = await db.users.findById(req.params.id);
// Update cache
await cache.set(cacheKey, user);
lctx.logger.info('Cache miss');
res.json({ user });
};Request Timeout
typescript
import { withTimeout } from '@gati-framework/production-hardening';
export const handler: Handler = withTimeout(
async (req, res, gctx, lctx) => {
const result = await longRunningOperation();
res.json({ result });
},
5000 // 5 second timeout
);Monitoring
Metrics Collection
typescript
import { metrics } from '@gati-framework/production-hardening';
export const handler: Handler = async (req, res, gctx, lctx) => {
const start = Date.now();
try {
const result = await operation();
metrics.increment('requests.success', {
route: req.path,
method: req.method
});
res.json({ result });
} catch (error) {
metrics.increment('requests.error', {
route: req.path,
method: req.method,
error: error.name
});
throw error;
} finally {
const duration = Date.now() - start;
metrics.histogram('requests.duration', duration, {
route: req.path
});
}
};APM Integration
typescript
import { setupAPM } from '@gati-framework/production-hardening';
setupAPM({
serviceName: 'gati-app',
environment: process.env.NODE_ENV,
datadog: {
apiKey: process.env.DD_API_KEY,
site: 'datadoghq.com'
}
});Graceful Shutdown
typescript
import { setupGracefulShutdown } from '@gati-framework/production-hardening';
const app = createApp();
setupGracefulShutdown(app, {
timeout: 30000, // 30 seconds
signals: ['SIGTERM', 'SIGINT'],
onShutdown: async () => {
// Close database connections
await db.close();
// Close Redis connections
await redis.quit();
// Flush metrics
await metrics.flush();
}
});Best Practices
1. Never Log Sensitive Data
typescript
// ❌ Bad
lctx.logger.info('User login', {
email: req.body.email,
password: req.body.password // Never log passwords!
});
// ✅ Good
lctx.logger.info('User login', {
email: req.body.email,
userId: user.id
});2. Use Environment-Specific Configuration
typescript
const config = {
development: {
logLevel: 'debug',
rateLimitMax: 1000
},
production: {
logLevel: 'warn',
rateLimitMax: 100
}
}[process.env.NODE_ENV || 'development'];3. Validate All Input
typescript
// ✅ Always validate user input
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ error: 'Invalid input' });
}4. Set Resource Limits
typescript
// Kubernetes deployment
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi5. Enable HTTPS Only
typescript
// Redirect HTTP to HTTPS
app.use((req, res, next) => {
if (req.headers['x-forwarded-proto'] !== 'https') {
return res.redirect(`https://${req.headers.host}${req.url}`);
}
next();
});Checklist
Pre-Production
- [ ] All secrets stored securely (not in code)
- [ ] Input validation on all endpoints
- [ ] Rate limiting configured
- [ ] CORS configured correctly
- [ ] Security headers enabled
- [ ] Error monitoring setup
- [ ] Health checks implemented
- [ ] Logging configured
- [ ] Metrics collection enabled
- [ ] Graceful shutdown implemented
Production
- [ ] HTTPS enabled
- [ ] Database connection pooling
- [ ] Caching strategy implemented
- [ ] Resource limits set
- [ ] Auto-scaling configured
- [ ] Backup strategy in place
- [ ] Monitoring dashboards created
- [ ] Alerts configured
- [ ] Incident response plan documented
Related
- Observability Guide - Monitoring and metrics
- Error Handling - Error patterns
- Deployment Guide - Deployment strategies
- Kubernetes Guide - K8s deployment