Docker Support
PGRestify is fully optimized for Docker and containerized applications. It automatically detects Docker environments and adjusts its configuration for optimal performance in containers.
CLI Docker Configuration
Generate Docker Setup
The PGRestify CLI can generate complete Docker configurations:
bash
# Generate basic Docker Compose setup
pgrestify api config docker
# Generate production-ready setup
pgrestify api config docker --env production
# Custom PostgreSQL version
pgrestify api config docker --db-version 14 --postgrest-version v11.2.0
# Append to existing docker-compose.yml
pgrestify api config docker --append-to existing-compose.yml
# Database-only setup (no PostgREST)
pgrestify api config docker --include-db=false
Generated docker-compose.yml:
yaml
# Generated by: pgrestify api config docker --env development
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: pgrestify-postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: pgrestify_dev
volumes:
- postgres_data:/var/lib/postgresql/data
- ./sql:/docker-entrypoint-initdb.d
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
postgrest:
image: postgrest/postgrest:latest
container_name: pgrestify-postgrest
environment:
PGRST_DB_URI: postgres://postgres:password@postgres:5432/pgrestify_dev
PGRST_DB_SCHEMAS: api
PGRST_DB_ANON_ROLE: web_anon
PGRST_DB_PRE_REQUEST: authenticator
PGRST_SERVER_HOST: "0.0.0.0"
PGRST_SERVER_PORT: 3000
PGRST_JWT_SECRET: generated-secure-jwt-secret
depends_on:
postgres:
condition: service_healthy
ports:
- "3000:3000"
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
networks:
default:
name: pgrestify-network
Why PGRestify ❤️ Docker
- 🔍 Automatic Detection - Detects Docker environments and optimizes automatically
- ⚡ Network Optimization - Longer timeouts and better retry logic for container networks
- 🏥 Health Checks - Built-in health check support for container orchestration
- 📝 Environment Variables - Complete configuration via environment variables
- 🔒 Docker Secrets - Native support for Docker Swarm secrets
- 🌐 Service Discovery - Works seamlessly with Docker Compose and Kubernetes
Quick Start with Docker
Basic Docker Usage
typescript
import { createSimpleClient } from '@webcoded/pgrestify';
// PGRestify automatically detects Docker and optimizes itself!
const client = createSimpleClient('http://postgrest:3000');
// Docker environment variables
const client = createSimpleClient(process.env.POSTGREST_URL!, {
// Longer timeouts for container networks (automatically applied in Docker)
timeout: 30000,
// Enhanced retry logic for container networking
retry: {
attempts: 5,
delay: 2000,
shouldRetry: (error) => {
// Retry on common Docker network errors
return error.message.includes('ECONNREFUSED') ||
error.message.includes('ETIMEDOUT');
}
}
});
Docker Compose Integration
Create a complete development environment with Docker Compose:
docker-compose.yml
yaml
version: '3.8'
services:
# Your application
app:
build: .
environment:
# PGRestify configuration
- POSTGREST_URL=http://postgrest:3000
- JWT_SECRET=your-jwt-secret-key
- REDIS_URL=redis://redis:6379
- NODE_ENV=development
depends_on:
- postgrest
- redis
ports:
- "8080:8080"
# PostgREST API server
postgrest:
image: postgrest/postgrest:latest
environment:
PGRST_DB_URI: postgres://postgres:password@postgres:5432/myapp
PGRST_DB_SCHEMAS: public,auth
PGRST_DB_ANON_ROLE: anonymous
PGRST_JWT_SECRET: your-jwt-secret-key
depends_on:
- postgres
ports:
- "3000:3000"
# PostgreSQL database
postgres:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
# Redis for caching
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
Your Application (app.ts)
typescript
import { createSimpleClient } from '@webcoded/pgrestify';
// Configuration from environment variables
const client = createSimpleClient(process.env.POSTGREST_URL!, {
cache: {
enabled: process.env.REDIS_URL ? true : false,
ttl: 300,
},
auth: {
jwtSecret: process.env.JWT_SECRET,
autoRefreshToken: true,
}
});
// Your application logic
const users = await client.from('users').find();
Environment Variables
PGRestify supports comprehensive configuration via environment variables:
bash
# Core Configuration
POSTGREST_URL=http://postgrest:3000
DB_SCHEMA=public
# Authentication
JWT_SECRET=your-jwt-secret-key
JWT_ISSUER=postgrest
# Caching (Redis)
REDIS_URL=redis://redis:6379
CACHE_ENABLED=true
CACHE_TTL=300
# Performance
REQUEST_TIMEOUT=30000
MAX_RETRIES=5
DB_POOL_SIZE=10
# Security
ALLOWED_ORIGINS=https://app.example.com,https://admin.example.com
CORS_ENABLED=true
# Monitoring
MONITORING_ENABLED=true
LOG_LEVEL=info
# Docker-specific
DOCKER_CONTAINER=true
CONTAINER_NAME=myapp
Dockerfile Best Practices
Multi-stage Dockerfile
dockerfile
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY pnpm-lock.yaml ./
# Install dependencies
RUN npm install -g pnpm
RUN pnpm install --frozen-lockfile
# Copy source code
COPY . .
# Build application
RUN pnpm build
# Production stage
FROM node:18-alpine AS production
# Add non-root user for security
RUN addgroup -g 1001 -S nodejs
RUN adduser -S pgrestify -u 1001
WORKDIR /app
# Copy built application
COPY --from=builder --chown=pgrestify:nodejs /app/dist ./dist
COPY --from=builder --chown=pgrestify:nodejs /app/node_modules ./node_modules
COPY --chown=pgrestify:nodejs package*.json ./
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD node health-check.js
# Switch to non-root user
USER pgrestify
# Expose port
EXPOSE 8080
# Start application
CMD ["node", "dist/server.js"]
Health Check Script
javascript
// health-check.js
const { createSimpleClient } = require('pgrestify');
const client = createSimpleClient(process.env.POSTGREST_URL);
async function healthCheck() {
try {
// Simple database connectivity test
const startTime = Date.now();
const response = await client
.from('users')
.select('count')
.limit(1)
.execute();
const responseTime = Date.now() - startTime;
if (response.error) {
console.error('Health check failed:', response.error);
process.exit(1);
}
// Check if response time is reasonable
if (responseTime > 5000) {
console.error(`Health check too slow: ${responseTime}ms`);
process.exit(1);
}
console.log(`Health check passed in ${responseTime}ms`);
process.exit(0);
} catch (error) {
console.error('Health check error:', error.message);
process.exit(1);
}
}
healthCheck();
Kubernetes Deployment
Deployment Manifest
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pgrestify-app
labels:
app: pgrestify-app
spec:
replicas: 3
selector:
matchLabels:
app: pgrestify-app
template:
metadata:
labels:
app: pgrestify-app
spec:
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
env:
- name: POSTGREST_URL
value: "http://postgrest-service:3000"
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: app-secrets
key: jwt-secret
- name: REDIS_URL
value: "redis://redis-service:6379"
# Health checks
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
# Resource limits
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
---
apiVersion: v1
kind: Service
metadata:
name: pgrestify-service
spec:
selector:
app: pgrestify-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
Docker Secrets
Using Docker Swarm Secrets
typescript
// utils/secrets.ts
import { readFileSync } from 'fs';
function readDockerSecret(secretName: string): string | null {
try {
const secretPath = `/run/secrets/${secretName}`;
return readFileSync(secretPath, 'utf8').trim();
} catch (error) {
console.warn(`Failed to read Docker secret '${secretName}':`, error.message);
return null;
}
}
// app.ts
import { createSimpleClient } from '@webcoded/pgrestify';
import { readDockerSecret } from './utils/secrets';
const client = createSimpleClient(process.env.POSTGREST_URL!, {
auth: {
// Try Docker secret first, then environment variable
jwtSecret: readDockerSecret('jwt_secret') || process.env.JWT_SECRET,
}
});
docker-compose.yml with secrets
yaml
version: '3.8'
services:
app:
image: myapp:latest
secrets:
- jwt_secret
- db_password
environment:
- POSTGREST_URL=http://postgrest:3000
secrets:
jwt_secret:
external: true
db_password:
external: true
Network Configuration
Service Discovery
typescript
// Automatic service discovery based on environment
const isDockerEnvironment = process.env.DOCKER_CONTAINER === 'true' ||
process.env.KUBERNETES_SERVICE_HOST;
const postgrestUrl = isDockerEnvironment
? 'http://postgrest:3000' // Docker service name
: 'http://localhost:3000'; // Development
const client = createSimpleClient(postgrestUrl, {
// Longer timeout for container networks
timeout: isDockerEnvironment ? 30000 : 10000,
// Enhanced retry logic for Docker networking
retry: {
attempts: isDockerEnvironment ? 5 : 3,
delay: 2000,
backoff: 1.5,
shouldRetry: (error) => {
// Common Docker network errors
const networkErrors = [
'ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND', 'ENETUNREACH'
];
return networkErrors.some(err => error.message.includes(err));
}
}
});
Production Configuration
Production-Ready Setup
typescript
// config/production.ts
export const productionConfig = {
postgrest: {
url: process.env.POSTGREST_URL!,
timeout: 15000,
retries: 3,
},
cache: {
enabled: true,
ttl: 300,
redis: {
url: process.env.REDIS_URL,
maxRetries: 3,
retryDelay: 1000,
}
},
security: {
cors: {
origins: process.env.ALLOWED_ORIGINS?.split(',') || [],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
},
rateLimit: {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // requests per window
}
},
monitoring: {
enabled: process.env.NODE_ENV === 'production',
metrics: {
endpoint: process.env.METRICS_ENDPOINT,
interval: 60000, // 1 minute
},
logging: {
level: process.env.LOG_LEVEL || 'info',
format: 'json', // Structured logging for containers
}
}
};
// app.ts
import { createClientWithAuth } from '@webcoded/pgrestify';
import { productionConfig } from './config/production';
const client = createClientWithAuth(productionConfig.postgrest.url, {
timeout: productionConfig.postgrest.timeout,
retry: { attempts: productionConfig.postgrest.retries },
cache: productionConfig.cache,
cors: productionConfig.security.cors,
// Production monitoring
interceptors: {
request: async (config) => {
// Add correlation ID for tracing
config.headers['X-Correlation-ID'] = generateCorrelationId();
// Log requests in production
console.log(JSON.stringify({
level: 'info',
message: 'Database request',
method: config.method,
url: config.url,
correlationId: config.headers['X-Correlation-ID'],
timestamp: new Date().toISOString(),
}));
return config;
},
response: async (response) => {
const duration = Date.now() - response.config.requestStartTime;
// Send metrics to monitoring service
if (productionConfig.monitoring.enabled) {
await sendMetrics({
service: 'pgrestify',
method: response.config.method,
statusCode: response.status,
duration,
timestamp: new Date(),
});
}
return response;
}
}
});
Monitoring and Logging
Structured Logging
typescript
import { createSimpleClient } from '@webcoded/pgrestify';
const client = createSimpleClient(process.env.POSTGREST_URL!, {
onRequest: (config) => {
console.log(JSON.stringify({
level: 'info',
message: 'PGRestify request',
method: config.method,
url: config.url,
container: process.env.HOSTNAME,
timestamp: new Date().toISOString(),
}));
},
onResponse: (response, duration) => {
console.log(JSON.stringify({
level: response.status >= 400 ? 'error' : 'info',
message: 'PGRestify response',
status: response.status,
duration: `${duration}ms`,
container: process.env.HOSTNAME,
timestamp: new Date().toISOString(),
}));
},
onError: (error, context) => {
console.error(JSON.stringify({
level: 'error',
message: 'PGRestify error',
error: error.message,
stack: error.stack,
context,
container: process.env.HOSTNAME,
timestamp: new Date().toISOString(),
}));
}
});
Troubleshooting
Common Docker Issues
Connection Refused
bash
# Debug: Check if services can communicate
docker exec app-container ping postgrest
docker exec app-container nslookup postgrest
# Solution: Ensure proper service names and networks
Slow Performance
typescript
// Increase timeouts for Docker networks
const client = createSimpleClient(url, {
timeout: 30000, // 30 seconds for containers
});
Service Discovery Issues
bash
# Check Docker networks
docker network ls
docker network inspect your-network
# Verify service names in docker-compose.yml match connection URLs
Debugging Commands
bash
# Container logs
docker logs pgrestify-app
# Container shell
docker exec -it pgrestify-app sh
# Network connectivity
docker exec pgrestify-app wget -qO- http://postgrest:3000/users
# Environment variables
docker exec pgrestify-app env | grep POSTGREST
Best Practices
✅ Do's
- ✅ Use environment variables for all configuration
- ✅ Implement proper health checks
- ✅ Use Docker service names for internal communication
- ✅ Configure appropriate timeouts for container networks
- ✅ Implement retry logic for network failures
- ✅ Use structured logging with correlation IDs
- ✅ Monitor container resource usage
- ✅ Use Docker secrets for sensitive data
- ✅ Follow principle of least privilege (non-root user)
❌ Don'ts
- ❌ Don't hardcode URLs in your application
- ❌ Don't use localhost URLs in containers
- ❌ Don't ignore container health checks
- ❌ Don't expose secrets in environment variables
- ❌ Don't use default timeouts in container networks
- ❌ Don't skip error handling for network issues
- ❌ Don't run containers as root
PGRestify makes Docker deployment seamless and production-ready! 🐳