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.