Set up production-ready database connections in Deno applications with PostgreSQL and Redis, including SSL configuration, connection pooling, authentication, and comprehensive error handling for high-performance TypeScript applications.
Prerequisites
- Root or sudo access
- PostgreSQL 12+ installed
- Redis 6+ installed
- Basic TypeScript knowledge
What this solves
Modern Deno applications require efficient database connections to handle production workloads. This tutorial configures PostgreSQL and Redis connections with connection pooling, SSL encryption, authentication, and proper error handling. You'll establish reliable database connectivity that scales with your application demands and maintains security standards for production deployments.
Step-by-step configuration
Install Deno runtime
First, install the latest version of Deno on your system if not already present.
curl -fsSL https://deno.land/install.sh | sh
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
curl -fsSL https://deno.land/install.sh | sh
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
Install PostgreSQL and Redis servers
Install PostgreSQL and Redis database servers for local development and testing.
sudo apt update
sudo apt install -y postgresql postgresql-contrib redis-server
sudo dnf install -y postgresql postgresql-server postgresql-contrib redis
sudo postgresql-setup --initdb
Configure PostgreSQL authentication
Set up PostgreSQL with a dedicated database and user for your Deno application.
sudo systemctl enable --now postgresql
sudo -u postgres psql -c "CREATE USER denoapp WITH PASSWORD 'secure_db_password123';"
sudo -u postgres psql -c "CREATE DATABASE denoapp_db OWNER denoapp;"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE denoapp_db TO denoapp;"
Configure Redis authentication
Enable Redis authentication and set a strong password for secure connections.
bind 127.0.0.1
requirepass redis_secure_password456
maxmemory 256mb
maxmemory-policy allkeys-lru
tcp-keepalive 300
sudo systemctl enable --now redis-server
sudo systemctl restart redis-server
Create project structure
Set up the basic Deno project structure with TypeScript configuration and database modules.
mkdir deno-db-app && cd deno-db-app
mkdir -p src/{config,database,models,utils}
touch src/main.ts src/config/database.ts src/database/postgresql.ts src/database/redis.ts
Configure database connection settings
Create centralized database configuration with environment variable support and connection pooling settings.
export interface DatabaseConfig {
postgresql: {
hostname: string;
port: number;
database: string;
username: string;
password: string;
pool: {
max: number;
min: number;
idle_timeout: number;
connect_timeout: number;
};
ssl: {
enabled: boolean;
ca?: string;
cert?: string;
key?: string;
};
};
redis: {
hostname: string;
port: number;
password?: string;
pool: {
max: number;
min: number;
idle_timeout: number;
};
ssl: {
enabled: boolean;
};
cluster: {
enabled: boolean;
nodes?: string[];
};
};
}
export const databaseConfig: DatabaseConfig = {
postgresql: {
hostname: Deno.env.get("PG_HOST") || "localhost",
port: parseInt(Deno.env.get("PG_PORT") || "5432"),
database: Deno.env.get("PG_DATABASE") || "denoapp_db",
username: Deno.env.get("PG_USERNAME") || "denoapp",
password: Deno.env.get("PG_PASSWORD") || "secure_db_password123",
pool: {
max: parseInt(Deno.env.get("PG_POOL_MAX") || "20"),
min: parseInt(Deno.env.get("PG_POOL_MIN") || "5"),
idle_timeout: parseInt(Deno.env.get("PG_POOL_IDLE_TIMEOUT") || "10000"),
connect_timeout: parseInt(Deno.env.get("PG_CONNECT_TIMEOUT") || "5000"),
},
ssl: {
enabled: Deno.env.get("PG_SSL_ENABLED") === "true",
ca: Deno.env.get("PG_SSL_CA"),
cert: Deno.env.get("PG_SSL_CERT"),
key: Deno.env.get("PG_SSL_KEY"),
},
},
redis: {
hostname: Deno.env.get("REDIS_HOST") || "localhost",
port: parseInt(Deno.env.get("REDIS_PORT") || "6379"),
password: Deno.env.get("REDIS_PASSWORD") || "redis_secure_password456",
pool: {
max: parseInt(Deno.env.get("REDIS_POOL_MAX") || "10"),
min: parseInt(Deno.env.get("REDIS_POOL_MIN") || "2"),
idle_timeout: parseInt(Deno.env.get("REDIS_POOL_IDLE_TIMEOUT") || "30000"),
},
ssl: {
enabled: Deno.env.get("REDIS_SSL_ENABLED") === "true",
},
cluster: {
enabled: Deno.env.get("REDIS_CLUSTER_ENABLED") === "true",
nodes: Deno.env.get("REDIS_CLUSTER_NODES")?.split(","),
},
},
};
Implement PostgreSQL connection manager
Create a PostgreSQL connection pool with SSL support, health checks, and proper error handling.
import { Pool, PoolClient } from "https://deno.land/x/postgres@v0.19.3/mod.ts";
import { databaseConfig } from "../config/database.ts";
export class PostgreSQLManager {
private static instance: PostgreSQLManager;
private pool: Pool;
private isConnected = false;
private constructor() {
const config = databaseConfig.postgresql;
this.pool = new Pool({
hostname: config.hostname,
port: config.port,
database: config.database,
user: config.username,
password: config.password,
connection: {
attempts: 3,
interval: 1000,
},
tls: config.ssl.enabled ? {
enabled: true,
enforce: false,
caCertificates: config.ssl.ca ? [await Deno.readTextFile(config.ssl.ca)] : undefined,
} : undefined,
}, config.pool.max, true);
this.setupEventHandlers();
}
public static getInstance(): PostgreSQLManager {
if (!PostgreSQLManager.instance) {
PostgreSQLManager.instance = new PostgreSQLManager();
}
return PostgreSQLManager.instance;
}
private setupEventHandlers(): void {
this.pool.addEventListener("error", (event) => {
console.error("PostgreSQL pool error:", event.error);
this.isConnected = false;
});
this.pool.addEventListener("connect", () => {
console.log("PostgreSQL connection established");
this.isConnected = true;
});
this.pool.addEventListener("end", () => {
console.log("PostgreSQL connection ended");
this.isConnected = false;
});
}
public async connect(): Promise
Implement Redis connection manager
Create a Redis connection pool with clustering support, authentication, and automatic reconnection.
import { connect, Redis } from "https://deno.land/x/redis@v0.32.3/mod.ts";
import { databaseConfig } from "../config/database.ts";
export class RedisManager {
private static instance: RedisManager;
private connections: Redis[] = [];
private currentIndex = 0;
private isConnected = false;
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
private constructor() {
this.setupConnectionPool();
}
public static getInstance(): RedisManager {
if (!RedisManager.instance) {
RedisManager.instance = new RedisManager();
}
return RedisManager.instance;
}
private async setupConnectionPool(): Promise
Create database initialization module
Implement a centralized database initialization and health monitoring system.
import { PostgreSQLManager } from "./postgresql.ts";
import { RedisManager } from "./redis.ts";
export class DatabaseManager {
private static instance: DatabaseManager;
private postgresql: PostgreSQLManager;
private redis: RedisManager;
private healthCheckInterval?: number;
private constructor() {
this.postgresql = PostgreSQLManager.getInstance();
this.redis = RedisManager.getInstance();
}
public static getInstance(): DatabaseManager {
if (!DatabaseManager.instance) {
DatabaseManager.instance = new DatabaseManager();
}
return DatabaseManager.instance;
}
public async initialize(): Promise
Create example application
Build a sample Deno application demonstrating database connections, error handling, and connection pooling.
import { Application, Router } from "https://deno.land/x/oak@v16.1.0/mod.ts";
import { DatabaseManager } from "./database/index.ts";
const app = new Application();
const router = new Router();
const db = DatabaseManager.getInstance();
// Initialize database connections
try {
await db.initialize();
} catch (error) {
console.error("Failed to initialize databases:", error);
Deno.exit(1);
}
// Health check endpoint
router.get("/health", async (ctx) => {
try {
const health = await db.runHealthChecks();
const stats = db.getStats();
ctx.response.body = {
status: "ok",
timestamp: new Date().toISOString(),
databases: health,
stats: stats,
};
} catch (error) {
ctx.response.status = 500;
ctx.response.body = {
status: "error",
message: error.message,
};
}
});
// PostgreSQL example endpoint
router.get("/users/:id", async (ctx) => {
try {
const userId = ctx.params.id;
const postgresql = db.getPostgreSQL();
// Example query with parameter binding
const users = await postgresql.query(
"SELECT id, username, email, created_at FROM users WHERE id = $1",
[parseInt(userId)]
);
if (users.length === 0) {
ctx.response.status = 404;
ctx.response.body = { error: "User not found" };
return;
}
ctx.response.body = {
user: users[0],
cached: false,
};
} catch (error) {
console.error("Database query error:", error);
ctx.response.status = 500;
ctx.response.body = {
error: "Internal server error",
details: error.message,
};
}
});
// Redis caching example endpoint
router.get("/cache/:key", async (ctx) => {
try {
const key = ctx.params.key;
const redis = db.getRedis();
const value = await redis.get(`app:cache:${key}`);
if (value) {
ctx.response.body = {
key: key,
value: JSON.parse(value),
cached: true,
timestamp: new Date().toISOString(),
};
} else {
ctx.responseAutomated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Production-quality Deno PostgreSQL/Redis setup script
# Supports Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS, RHEL
# Color codes
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m'
# Configuration
readonly PROJECT_DIR="${1:-deno-db-app}"
readonly PG_USER="denoapp"
readonly PG_DB="denoapp_db"
readonly PG_PASSWORD="${2:-$(openssl rand -base64 32)}"
readonly REDIS_PASSWORD="${3:-$(openssl rand -base64 32)}"
# Logging functions
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Error handling and cleanup
cleanup() {
log_error "Installation failed. Cleaning up..."
systemctl stop postgresql redis-server 2>/dev/null || true
rm -rf "/home/$SUDO_USER/$PROJECT_DIR" 2>/dev/null || true
}
trap cleanup ERR
# Usage
usage() {
cat << EOF
Usage: $0 [PROJECT_DIR] [PG_PASSWORD] [REDIS_PASSWORD]
PROJECT_DIR - Project directory name (default: deno-db-app)
PG_PASSWORD - PostgreSQL password (default: auto-generated)
REDIS_PASSWORD - Redis password (default: auto-generated)
Example: $0 my-deno-app mySecurePass123 redisPass456
EOF
exit 1
}
# Check prerequisites
check_prerequisites() {
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root or with sudo"
exit 1
fi
if [[ -z "${SUDO_USER:-}" ]]; then
log_error "SUDO_USER not set. Run with sudo, not as root directly"
exit 1
fi
command -v curl >/dev/null || { log_error "curl is required"; exit 1; }
command -v openssl >/dev/null || { log_error "openssl is required"; exit 1; }
}
# Detect OS and package manager
detect_os() {
if [[ ! -f /etc/os-release ]]; then
log_error "Cannot detect OS - /etc/os-release not found"
exit 1
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
PG_SERVICE="postgresql"
REDIS_SERVICE="redis-server"
REDIS_CONFIG="/etc/redis/redis.conf"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf makecache"
PKG_INSTALL="dnf install -y"
PG_SERVICE="postgresql"
REDIS_SERVICE="redis"
REDIS_CONFIG="/etc/redis/redis.conf"
;;
fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf makecache"
PKG_INSTALL="dnf install -y"
PG_SERVICE="postgresql"
REDIS_SERVICE="redis"
REDIS_CONFIG="/etc/redis/redis.conf"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum makecache"
PKG_INSTALL="yum install -y"
PG_SERVICE="postgresql"
REDIS_SERVICE="redis"
REDIS_CONFIG="/etc/redis.conf"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
}
# Install Deno
install_deno() {
log_info "[1/8] Installing Deno runtime..."
sudo -u "$SUDO_USER" bash -c 'curl -fsSL https://deno.land/install.sh | sh'
# Add to PATH for the user
if ! grep -q '.deno/bin' "/home/$SUDO_USER/.bashrc"; then
sudo -u "$SUDO_USER" bash -c 'echo "export PATH=\"\$HOME/.deno/bin:\$PATH\"" >> ~/.bashrc'
fi
log_info "Deno installed successfully"
}
# Install databases
install_databases() {
log_info "[2/8] Installing PostgreSQL and Redis..."
$PKG_UPDATE
case "$PKG_MGR" in
apt)
$PKG_INSTALL postgresql postgresql-contrib redis-server
;;
dnf|yum)
$PKG_INSTALL postgresql postgresql-server postgresql-contrib redis
if [[ "$ID" =~ ^(almalinux|rocky|centos|rhel)$ ]]; then
postgresql-setup --initdb 2>/dev/null || true
fi
;;
esac
log_info "Databases installed successfully"
}
# Configure PostgreSQL
configure_postgresql() {
log_info "[3/8] Configuring PostgreSQL..."
systemctl enable "$PG_SERVICE"
systemctl start "$PG_SERVICE"
# Wait for PostgreSQL to start
sleep 3
# Create user and database
sudo -u postgres psql -c "CREATE USER $PG_USER WITH PASSWORD '$PG_PASSWORD';" || true
sudo -u postgres psql -c "CREATE DATABASE $PG_DB OWNER $PG_USER;" || true
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $PG_DB TO $PG_USER;" || true
# Configure authentication
PG_VERSION=$(sudo -u postgres psql -t -c "SELECT version();" | grep -oP '\d+\.\d+' | head -1)
PG_CONFIG_DIR="/etc/postgresql/$PG_VERSION/main"
if [[ ! -d "$PG_CONFIG_DIR" ]]; then
PG_CONFIG_DIR="/var/lib/pgsql/data"
fi
log_info "PostgreSQL configured successfully"
}
# Configure Redis
configure_redis() {
log_info "[4/8] Configuring Redis..."
# Backup original config
cp "$REDIS_CONFIG" "$REDIS_CONFIG.backup"
# Update Redis configuration
cat > "$REDIS_CONFIG" << EOF
bind 127.0.0.1
port 6379
requirepass $REDIS_PASSWORD
maxmemory 256mb
maxmemory-policy allkeys-lru
tcp-keepalive 300
save 900 1
save 300 10
save 60 10000
dir /var/lib/redis
logfile /var/log/redis/redis-server.log
EOF
# Set proper permissions
chown redis:redis "$REDIS_CONFIG"
chmod 640 "$REDIS_CONFIG"
systemctl enable "$REDIS_SERVICE"
systemctl restart "$REDIS_SERVICE"
log_info "Redis configured successfully"
}
# Create project structure
create_project() {
log_info "[5/8] Creating project structure..."
PROJECT_PATH="/home/$SUDO_USER/$PROJECT_DIR"
sudo -u "$SUDO_USER" bash -c "
mkdir -p '$PROJECT_PATH/src/{config,database,models,utils}'
touch '$PROJECT_PATH/src/main.ts'
touch '$PROJECT_PATH/src/config/database.ts'
touch '$PROJECT_PATH/src/database/postgresql.ts'
touch '$PROJECT_PATH/src/database/redis.ts'
"
log_info "Project structure created"
}
# Generate configuration files
generate_config() {
log_info "[6/8] Generating configuration files..."
PROJECT_PATH="/home/$SUDO_USER/$PROJECT_DIR"
# Database configuration
cat > "$PROJECT_PATH/src/config/database.ts" << 'EOF'
export interface DatabaseConfig {
postgresql: {
hostname: string;
port: number;
database: string;
username: string;
password: string;
pool: {
max: number;
min: number;
idle_timeout: number;
connect_timeout: number;
};
ssl: { enabled: boolean; };
};
redis: {
hostname: string;
port: number;
password?: string;
pool: { max: number; min: number; idle_timeout: number; };
ssl: { enabled: boolean; };
};
}
export const databaseConfig: DatabaseConfig = {
postgresql: {
hostname: Deno.env.get("PG_HOST") || "localhost",
port: parseInt(Deno.env.get("PG_PORT") || "5432"),
database: Deno.env.get("PG_DATABASE") || "denoapp_db",
username: Deno.env.get("PG_USERNAME") || "denoapp",
password: Deno.env.get("PG_PASSWORD") || "",
pool: {
max: parseInt(Deno.env.get("PG_POOL_MAX") || "20"),
min: parseInt(Deno.env.get("PG_POOL_MIN") || "5"),
idle_timeout: parseInt(Deno.env.get("PG_POOL_IDLE_TIMEOUT") || "10000"),
connect_timeout: parseInt(Deno.env.get("PG_CONNECT_TIMEOUT") || "5000"),
},
ssl: { enabled: Deno.env.get("PG_SSL_ENABLED") === "true" },
},
redis: {
hostname: Deno.env.get("REDIS_HOST") || "localhost",
port: parseInt(Deno.env.get("REDIS_PORT") || "6379"),
password: Deno.env.get("REDIS_PASSWORD") || "",
pool: {
max: parseInt(Deno.env.get("REDIS_POOL_MAX") || "10"),
min: parseInt(Deno.env.get("REDIS_POOL_MIN") || "2"),
idle_timeout: parseInt(Deno.env.get("REDIS_POOL_IDLE_TIMEOUT") || "30000"),
},
ssl: { enabled: Deno.env.get("REDIS_SSL_ENABLED") === "true" },
},
};
EOF
# Environment file
cat > "$PROJECT_PATH/.env" << EOF
PG_HOST=localhost
PG_PORT=5432
PG_DATABASE=$PG_DB
PG_USERNAME=$PG_USER
PG_PASSWORD=$PG_PASSWORD
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=$REDIS_PASSWORD
EOF
# Set proper ownership
chown -R "$SUDO_USER:$SUDO_USER" "$PROJECT_PATH"
chmod 600 "$PROJECT_PATH/.env"
log_info "Configuration files generated"
}
# Configure firewall
configure_firewall() {
log_info "[7/8] Configuring firewall..."
if command -v ufw >/dev/null; then
ufw allow 5432/tcp comment "PostgreSQL"
ufw allow 6379/tcp comment "Redis"
elif command -v firewall-cmd >/dev/null; then
firewall-cmd --permanent --add-port=5432/tcp
firewall-cmd --permanent --add-port=6379/tcp
firewall-cmd --reload
fi
log_info "Firewall configured"
}
# Verify installation
verify_installation() {
log_info "[8/8] Verifying installation..."
# Check services
systemctl is-active "$PG_SERVICE" >/dev/null || { log_error "PostgreSQL not running"; return 1; }
systemctl is-active "$REDIS_SERVICE" >/dev/null || { log_error "Redis not running"; return 1; }
# Test connections
sudo -u postgres psql -d "$PG_DB" -c "SELECT 1;" >/dev/null || { log_error "PostgreSQL connection failed"; return 1; }
redis-cli -a "$REDIS_PASSWORD" ping | grep -q PONG || { log_error "Redis connection failed"; return 1; }
log_info "All services verified successfully"
}
# Main execution
main() {
check_prerequisites
detect_os
log_info "Starting Deno database setup for $ID..."
install_deno
install_databases
configure_postgresql
configure_redis
create_project
generate_config
configure_firewall
verify_installation
log_info "Installation completed successfully!"
log_info "Project location: /home/$SUDO_USER/$PROJECT_DIR"
log_info "PostgreSQL: $PG_USER@localhost:5432/$PG_DB"
log_info "Redis: localhost:6379 (password protected)"
log_warn "Passwords saved in $PROJECT_DIR/.env - keep secure!"
}
# Run if not sourced
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
Review the script before running. Execute with: bash install.sh