Skip to main content

Overview

Hub uses PostgreSQL with PostGIS extension for spatial data support. The database stores venues, bookings, matches, users, and geographic data for location-based features.
PostGIS is required for geospatial queries like finding venues near a location.

Database Requirements

  • PostgreSQL: Version 16+ (recommended)
  • PostGIS: Version 3.4+
  • Extensions:
    • postgis - Spatial data support
    • pgcrypto - Cryptographic functions
    • btree_gist - B-tree and GiST index support for constraints

Quick Start with Docker

The easiest way to run PostgreSQL with PostGIS is using Docker:
1

Configure environment

Create a .env file with database credentials:
.env
POSTGRES_DB=hub_db
POSTGRES_USER=hub_user
POSTGRES_PASSWORD=your_secure_password
DB_HOST=postgres
DB_PORT=5432
DB_NAME=hub_db
DB_USER=hub_user
DB_PASSWORD=your_secure_password
DB_SSL_MODE=disable
2

Start PostgreSQL

Use the provided Docker Compose configuration:
Terminal
docker-compose up -d postgres
This starts a PostgreSQL container with PostGIS pre-installed.
3

Verify connection

Check that PostgreSQL is running:
Terminal
docker ps | grep postgres
docker logs hub-postgres
4

Run migrations

Migrations run automatically when the backend starts. Check logs for:
Flyway migration completed successfully

Docker Compose Configuration

The docker-compose.yml includes a PostgreSQL service with PostGIS:
docker-compose.yml
services:
  postgres:
    image: postgis/postgis:16-3.4
    container_name: hub-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 5s
      timeout: 3s
      retries: 10

volumes:
  postgres-data:
Data is persisted in the postgres-data volume. To reset the database, run:
docker-compose down -v

Manual PostgreSQL Setup

If you’re not using Docker, follow these steps:
1

Install PostgreSQL

Install PostgreSQL 16+ on your system:
sudo apt update
sudo apt install postgresql-16 postgresql-contrib-16
2

Install PostGIS

Install the PostGIS extension:
sudo apt install postgresql-16-postgis-3
3

Create database and user

Connect to PostgreSQL and create the database:
SQL
-- Connect as postgres superuser
psql -U postgres

-- Create user
CREATE USER hub_user WITH PASSWORD 'your_secure_password';

-- Create database
CREATE DATABASE hub_db OWNER hub_user;

-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE hub_db TO hub_user;
4

Enable extensions

Connect to the database and enable required extensions:
SQL
-- Connect to the hub database
\c hub_db

-- Enable extensions (Flyway will also run these)
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS btree_gist;

-- Verify extensions
\dx

Environment Variables

Configure these environment variables for database connectivity:
DB_HOST
string
required
Database server hostnameLocal: localhostDocker: postgres (service name)Production: your-db-host.com
DB_PORT
string
required
Database server portDefault: 5432
DB_NAME
string
required
Database nameExample: hub_db, padelhub_production
DB_USER
string
required
Database user with read/write access
DB_PASSWORD
string
required
Database user password
Store securely and never commit to version control
DB_SSL_MODE
string
SSL/TLS mode for database connectionsOptions:
  • disable - No SSL (local development)
  • require - Require SSL, don’t verify certificate
  • verify-ca - Require SSL, verify certificate authority
  • verify-full - Require SSL, verify certificate and hostname
Default: disable
Use require or higher in production

Connection Pool Configuration

The application uses HikariCP for connection pooling:
application.yaml
spring:
  datasource:
    url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=${DB_SSL_MODE:disable}&prepareThreshold=0
    username: ${DB_USER}
    password: ${DB_PASSWORD}
    hikari:
      auto-commit: false
      maximum-pool-size: 10
      minimum-idle: 2
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
auto-commit: false improves performance by allowing explicit transaction management.

Database Schema

The schema is managed by Flyway migrations located in backend/src/main/resources/db/migration/.

Migration Files

  • V1__init.sql - Initial schema: cities with PostGIS spatial data
  • V2__users.sql - User accounts and profiles
  • V3__create_venue.sql - Venues (padel clubs)
  • V4__create_resource.sql - Resources (padel courts)
  • V5__create_booking.sql - Booking system
  • V6__create_payment.sql - Payment tracking
  • V7__create_match_request.sql - Match requests and invitations
  • V8__seed_dev_data.sql - Development seed data

Key Tables

Pre-populated table with Spanish cities and coordinates:
CREATE TABLE city (
  id           BIGSERIAL PRIMARY KEY,
  name         VARCHAR(100) NOT NULL,
  country_code VARCHAR(3) NOT NULL DEFAULT 'ES',
  latitude     DOUBLE PRECISION NOT NULL,
  longitude    DOUBLE PRECISION NOT NULL,
  location     geography(Point, 4326) NOT NULL
);
Contains 40 Spanish cities with PostGIS point geometries.
User accounts synchronized with Auth0:
  • auth0_id - Links to Auth0 user
  • email - User email
  • name - Display name
  • profile_picture - Cloudinary URL
  • skill_level - Padel skill level
Padel clubs and facilities:
  • name - Venue name
  • city_id - References city table
  • location - PostGIS point for geospatial queries
  • photos - Array of Cloudinary URLs
  • manager_id - User who manages the venue
Individual padel courts:
  • venue_id - Parent venue
  • name - Court name (e.g., “Court 1”)
  • surface_type - Court surface
  • is_indoor - Indoor/outdoor flag
Court reservations:
  • resource_id - Court being booked
  • user_id - User making the booking
  • start_time - Booking start
  • end_time - Booking end
  • status - PENDING, CONFIRMED, CANCELLED
  • Constraint: No overlapping bookings per resource

Flyway Migrations

Flyway automatically manages database schema versions.

Configuration

application.yaml
spring:
  jpa:
    hibernate:
      ddl-auto: validate  # Ensures schema matches entities
  flyway:
    enabled: true
    locations: classpath:db/migration
Never set ddl-auto to create or update in production. Always use Flyway migrations.

Creating Migrations

To add a new migration:
1

Create SQL file

Create a new file following Flyway naming convention:
V{version}__{description}.sql
Example: V9__add_user_preferences.sql
2

Write migration

V9__add_user_preferences.sql
ALTER TABLE users ADD COLUMN notification_enabled BOOLEAN DEFAULT true;
ALTER TABLE users ADD COLUMN preferred_language VARCHAR(5) DEFAULT 'es';

CREATE INDEX idx_users_language ON users(preferred_language);
3

Test migration

Restart the application. Flyway will detect and run the new migration:
INFO : Migrating schema "public" to version "9 - add user preferences"
INFO : Successfully applied 1 migration

Migration Best Practices

  • Make migrations idempotent when possible
  • Use IF NOT EXISTS for CREATE statements
  • Test migrations on a copy of production data
  • Never modify existing migration files
  • Include rollback scripts for complex migrations

Geospatial Queries

PostGIS enables location-based features:

Find Venues Near Location

-- Find venues within 10km of coordinates
SELECT 
  v.id,
  v.name,
  ST_Distance(v.location, ST_MakePoint(-3.7038, 40.4168)::geography) as distance_meters
FROM venue v
WHERE ST_DWithin(
  v.location,
  ST_MakePoint(-3.7038, 40.4168)::geography,
  10000  -- 10km in meters
)
ORDER BY distance_meters;

Find Nearest City

-- Find closest city to coordinates
SELECT 
  c.name,
  ST_Distance(c.location, ST_MakePoint(-3.7038, 40.4168)::geography) as distance_meters
FROM city c
ORDER BY c.location <-> ST_MakePoint(-3.7038, 40.4168)::geography
LIMIT 1;
The <-> operator uses the spatial index for fast nearest-neighbor queries.

Database Tools

pgAdmin (Included with Docker)

A web-based PostgreSQL administration tool is included:
# Start pgAdmin
docker-compose up -d pgadmin

# Access at http://localhost:5050
# Email: [email protected]
# Password: admin
Connect to database:
  • Host: postgres (or host.docker.internal from host machine)
  • Port: 5432
  • Database: hub_db
  • Username: From POSTGRES_USER
  • Password: From POSTGRES_PASSWORD

Command Line Tools

docker exec -it hub-postgres psql -U hub_user -d hub_db

Production Configuration

Security Checklist

1

Enable SSL

Set DB_SSL_MODE=require or higher
2

Use strong passwords

Generate cryptographically secure passwords (min 32 characters)
3

Restrict network access

Use firewalls to limit database access to application servers only
4

Enable connection pooling

Configure appropriate pool sizes based on load:
hikari:
  maximum-pool-size: 20
  minimum-idle: 5
5

Regular backups

Implement automated backups with point-in-time recovery
6

Monitor performance

Use PostgreSQL’s pg_stat_statements extension to identify slow queries

Managed Database Providers

For production, consider managed PostgreSQL services:
  • AWS RDS for PostgreSQL (with PostGIS)
  • Azure Database for PostgreSQL
  • Google Cloud SQL for PostgreSQL
  • DigitalOcean Managed Databases
  • Supabase (includes PostGIS by default)
Ensure your managed database provider supports PostGIS extension.

Monitoring

Health Check

The application exposes a health check endpoint:
curl http://localhost:8080/actuator/health
Response:
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "PostgreSQL",
        "validationQuery": "isValid()"
      }
    }
  }
}

Common Issues

Error: Connection to localhost:5432 refusedSolutions:
  • Check PostgreSQL is running: docker ps | grep postgres
  • Verify DB_HOST and DB_PORT are correct
  • Check firewall rules
Error: password authentication failed for userSolutions:
  • Verify DB_USER and DB_PASSWORD match PostgreSQL user
  • Check pg_hba.conf authentication rules
Error: extension "postgis" does not existSolution:
CREATE EXTENSION postgis;
Error: Validate failed: Migration checksum mismatchSolution: Never modify existing migrations. Create a new migration to fix issues.

Next Steps

Integrations

Configure Cloudinary and Brevo

API Reference

Explore API endpoints

Data Models

Understand data structure

Deployment

Deploy to production

Build docs developers (and LLMs) love