Skip to content

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! 🐳

Released under the MIT License.