# Connect to PostgreSQL as superuserpsql -U postgres
Then create the database and user:
-- Create userCREATE USER brautcloud_user WITH PASSWORD 'your_secure_password';-- Create databaseCREATE DATABASE brautcloud OWNER brautcloud_user;-- Grant privilegesGRANT ALL PRIVILEGES ON DATABASE brautcloud TO brautcloud_user;-- Connect to the database\c brautcloud-- Grant schema privilegesGRANT ALL ON SCHEMA public TO brautcloud_user;
Replace your_secure_password with a strong, randomly generated password. Store this securely as you’ll need it for the backend configuration.
CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, last_name VARCHAR(255), first_name_couple_one VARCHAR(255), first_name_couple_two VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, email VARCHAR(255) UNIQUE NOT NULL, email_verified BOOLEAN DEFAULT FALSE, password VARCHAR(255) NOT NULL, role VARCHAR(50) DEFAULT 'ROLE_USER' NOT NULL);
Key fields:
id - Auto-incrementing primary key
email - Unique email address for authentication
password - Bcrypt hashed password
role - User role (default: ROLE_USER)
first_name_couple_one / first_name_couple_two - Names of the couple
CREATE TABLE IF NOT EXISTS events ( id SERIAL PRIMARY KEY, event_name VARCHAR(255) NOT NULL, location VARCHAR(255), date TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, password VARCHAR(255), qr_code TEXT, user_id INTEGER NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE);
Key fields:
id - Auto-incrementing primary key
event_name - Name of the wedding event
password - Optional password for guest access
qr_code - QR code for easy event sharing
user_id - Foreign key to users table (cascade delete)
CREATE TABLE IF NOT EXISTS images ( id SERIAL PRIMARY KEY, image_key TEXT NOT NULL, is_visible BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, event_id INTEGER NOT NULL, FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE);
Key fields:
id - Auto-incrementing primary key
image_key - S3 object key for the stored image
is_visible - Visibility flag for moderation
event_id - Foreign key to events table (cascade delete)
Actual image files are stored in AWS S3. The database only stores metadata and S3 object keys.
CREATE TABLE IF NOT EXISTS refresh_tokens ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL UNIQUE, token VARCHAR(255) NOT NULL UNIQUE, expires_at TIMESTAMP NOT NULL, CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE);
Key fields:
id - Auto-incrementing primary key
user_id - One-to-one relationship with users (unique)
The Spring Boot application automatically initializes the database using the schema.sql file when started:
Automatic schema creation: Tables are created if they don’t exist
JPA entity mapping: Spring Data JPA validates entity mappings against the schema
Migration safe: Uses CREATE TABLE IF NOT EXISTS to prevent errors on restart
The spring.sql.init.mode=always setting runs schema initialization on every startup. For production, consider using a dedicated migration tool like Flyway or Liquibase.
-- Index on user email for login queriesCREATE INDEX idx_users_email ON users(email);-- Index on event user_id for user's events lookupCREATE INDEX idx_events_user_id ON events(user_id);-- Index on image event_id for event gallery queriesCREATE INDEX idx_images_event_id ON images(event_id);-- Index on refresh token for authenticationCREATE INDEX idx_refresh_tokens_token ON refresh_tokens(token);
2
Configure connection pooling
Optimize database connections in application.properties:
# HikariCP connection pool settingsspring.datasource.hikari.maximum-pool-size=10spring.datasource.hikari.minimum-idle=5spring.datasource.hikari.connection-timeout=20000spring.datasource.hikari.idle-timeout=300000spring.datasource.hikari.max-lifetime=1200000