Skip to main content

Overview

Portfolio Hub API is configured entirely through environment variables, following the 12-Factor App methodology. This approach allows the same application build to be deployed across different environments without code changes.
All environment variables can be set in a .env file for local development or configured directly in your deployment platform.

Database Configuration

These variables configure the MySQL database connection.

MYSQL_HOST

MYSQL_HOST
string
required
The hostname or IP address of your MySQL server.Default: None (required)Examples:
  • localhost - Local development
  • mysql-db - Docker Compose service name
  • db.example.com - Remote database server
  • 10.0.1.5 - IP address
MYSQL_HOST=mysql-db

MYSQL_PORT

MYSQL_PORT
integer
default:"3306"
The port number on which MySQL is listening.Default: 3306Note: This is optional. If not provided, defaults to 3306.
MYSQL_PORT=3306

MYSQL_DATABASE

MYSQL_DATABASE
string
required
The name of the database schema to use.Default: None (required)Expected Value: studiostkoh.portafolio
MYSQL_DATABASE=studiostkoh.portafolio
The database schema must exist before starting the application. Flyway will handle table creation, but not schema creation.

MYSQL_USER

MYSQL_USER
string
required
The MySQL username for authentication.Default: None (required)Permissions Required: The user must have:
  • SELECT, INSERT, UPDATE, DELETE on all tables
  • CREATE, ALTER, DROP for Flyway migrations
MYSQL_USER=portfolio_user

MYSQL_PASSWORD

MYSQL_PASSWORD
string
required
The password for the MySQL user.Default: None (required)Security: Use strong passwords (minimum 16 characters, mix of letters, numbers, symbols)
MYSQL_PASSWORD=your_secure_password_here

MYSQL_ROOT_PASSWORD

MYSQL_ROOT_PASSWORD
string
Required only for Docker Compose to initialize the MySQL container.Default: NoneUsage: Only needed when running MySQL in Docker. Not used by the application.
MYSQL_ROOT_PASSWORD=root_password_here

JWT Security Configuration

These variables configure JSON Web Token (JWT) authentication.

JWT_TOKEN

JWT_TOKEN
string
required
The secret key used to sign and verify JWT tokens.Default: None (required)Security Requirements:
  • Minimum 256 bits (32 characters) for HS256 algorithm
  • Use cryptographically secure random strings
  • Never commit to version control
  • Rotate regularly in production
JWT_TOKEN=your_very_long_secret_key_for_jwt_tokens_minimum_256_bits
Use one of these methods to generate a secure random key:
OpenSSL
openssl rand -base64 32
Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
Python
python -c "import secrets; print(secrets.token_urlsafe(32))"

JWT_EXPIRATION_TIME

JWT_EXPIRATION_TIME
integer
required
Token expiration time in minutes.Default: None (required)Recommended Values:
  • 15-30 minutes for high-security applications
  • 60 minutes for standard applications
  • 480 (8 hours) for development environments
JWT_EXPIRATION_TIME=60
Shorter expiration times increase security but require more frequent re-authentication. Consider implementing refresh tokens for better user experience.

Google Drive Integration

These variables configure the Google Drive API for file uploads (avatars, resumes, project covers, certificates).

OAuth 2.0 Credentials

1

Create Google Cloud Project

Go to Google Cloud Console and create a new project.
2

Enable Google Drive API

Enable the Google Drive API for your project.
3

Create OAuth 2.0 Credentials

Create OAuth 2.0 credentials and obtain the Client ID, Client Secret, and Refresh Token.

DRIVE_OAUTH_CLIENT_ID

DRIVE_OAUTH_CLIENT_ID
string
required
The OAuth 2.0 Client ID from Google Cloud Console.Default: None (required)Format: xxxxxxxxxxxxx.apps.googleusercontent.com
DRIVE_OAUTH_CLIENT_ID=123456789012-abcdefghijklmnop.apps.googleusercontent.com

DRIVE_OAUTH_CLIENT_SECRET

DRIVE_OAUTH_CLIENT_SECRET
string
required
The OAuth 2.0 Client Secret from Google Cloud Console.Default: None (required)Security: Keep this secret secure and never expose it publicly.
DRIVE_OAUTH_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxxxxxx

DRIVE_OAUTH_REFRESH_TOKEN

DRIVE_OAUTH_REFRESH_TOKEN
string
required
The OAuth 2.0 Refresh Token for long-term access.Default: None (required)Note: This token allows the application to access Google Drive without user interaction.
DRIVE_OAUTH_REFRESH_TOKEN=1//0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Google Drive Folder IDs

These variables specify the Google Drive folder IDs where different file types are stored.
  1. Open Google Drive in your browser
  2. Navigate to the desired folder
  3. The URL will look like: https://drive.google.com/drive/folders/FOLDER_ID_HERE
  4. Copy the FOLDER_ID_HERE part
Example: https://drive.google.com/drive/folders/1A2B3C4D5E6F7G8H9I0JFolder ID: 1A2B3C4D5E6F7G8H9I0J

DRIVE_FOLDER_USER_AVATARS

DRIVE_FOLDER_USER_AVATARS
string
required
Folder ID for storing user avatar images.
DRIVE_FOLDER_USER_AVATARS=1AbCdEfGhIjKlMnOpQrStUvWxYz

DRIVE_FOLDER_USER_RESUMES

DRIVE_FOLDER_USER_RESUMES
string
required
Folder ID for storing user resume/CV files (PDF).
DRIVE_FOLDER_USER_RESUMES=2BcDeFgHiJkLmNoPqRsTuVwXyZa

DRIVE_FOLDER_PROJECTS_COVER

DRIVE_FOLDER_PROJECTS_COVER
string
required
Folder ID for storing project cover images.
DRIVE_FOLDER_PROJECTS_COVER=3CdEfGhIjKlMnOpQrStUvWxYzAb

DRIVE_FOLDER_SKILLS_ICON

DRIVE_FOLDER_SKILLS_ICON
string
required
Folder ID for storing skill icons.
DRIVE_FOLDER_SKILLS_ICON=4DeFgHiJkLmNoPqRsTuVwXyZaBc

DRIVE_FOLDER_CERTIFICATES

DRIVE_FOLDER_CERTIFICATES
string
required
Folder ID for storing certificate files.
DRIVE_FOLDER_CERTIFICATES=5EfGhIjKlMnOpQrStUvWxYzAbCd

Email Configuration

These variables configure SMTP for sending emails (e.g., contact form submissions).

SMTP_HOST

SMTP_HOST
string
required
The SMTP server hostname.Default: None (required)Common Values:
  • smtp.gmail.com - Gmail
  • smtp-mail.outlook.com - Outlook
  • smtp.sendgrid.net - SendGrid
  • smtp.mailgun.org - Mailgun
SMTP_HOST=smtp.gmail.com

SMTP_PORT

SMTP_PORT
integer
required
The SMTP server port.Default: None (required)Common Ports:
  • 587 - TLS/STARTTLS (recommended)
  • 465 - SSL
  • 25 - Unencrypted (not recommended)
SMTP_PORT=587

GMAIL_APP_EMAIL

GMAIL_APP_EMAIL
string
required
The email address used to send emails.Default: None (required)Format: Valid email address
GMAIL_APP_EMAIL=[email protected]

GMAIL_APP_PASSWORD

GMAIL_APP_PASSWORD
string
required
The password or app-specific password for SMTP authentication.Default: None (required)For Gmail: You must use an App Password, not your regular Gmail password.
GMAIL_APP_PASSWORD=abcd efgh ijkl mnop
  1. Go to your Google Account Security Settings
  2. Enable 2-Step Verification if not already enabled
  3. Go to “App passwords” section
  4. Generate a new app password for “Mail”
  5. Copy the 16-character password (spaces are optional)
App passwords can only be created if 2-Step Verification is enabled.

CORS Configuration

CORS_ALLOWED_ORIGINS

CORS_ALLOWED_ORIGINS
string
required
Comma-separated list of allowed origins for Cross-Origin Resource Sharing (CORS).Default: None (required)Format: origin1,origin2,origin3 (no spaces)Examples:
  • Development: http://localhost:3000,http://localhost:5173
  • Production: https://yourdomain.com,https://www.yourdomain.com
CORS_ALLOWED_ORIGINS=http://localhost:3000,https://yourdomain.com
Never use * (wildcard) for CORS in production. Always specify exact origins for security.

Application Properties

The following properties are defined in application.properties and reference the environment variables above:
# Database Connection
spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT:3306}/${MYSQL_DATABASE}?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
spring.datasource.username=${MYSQL_USER}
spring.datasource.password=${MYSQL_PASSWORD}

# JWT Security
application.security.jwt.secret-key=${JWT_TOKEN}
application.security.jwt.expiration=${JWT_EXPIRATION_TIME}

# Google Drive
google.drive.oauth.client-id=${DRIVE_OAUTH_CLIENT_ID}
google.drive.oauth.client-secret=${DRIVE_OAUTH_CLIENT_SECRET}
google.drive.oauth.refresh-token=${DRIVE_OAUTH_REFRESH_TOKEN}
google.drive.folders.user-avatars=${DRIVE_FOLDER_USER_AVATARS}
google.drive.folders.user-resumes=${DRIVE_FOLDER_USER_RESUMES}
google.drive.folders.projects-cover=${DRIVE_FOLDER_PROJECTS_COVER}
google.drive.folders.skills-icon=${DRIVE_FOLDER_SKILLS_ICON}
google.drive.folders.certificates=${DRIVE_FOLDER_CERTIFICATES}

# Email SMTP
spring.mail.host=${SMTP_HOST}
spring.mail.port=${SMTP_PORT}
spring.mail.username=${GMAIL_APP_EMAIL}
spring.mail.password=${GMAIL_APP_PASSWORD}

# CORS
application.security.cors.allowed-origins=${CORS_ALLOWED_ORIGINS}

Complete .env Template

Here’s a complete .env file template with all required variables:
.env
# ======================
# DATABASE CONFIGURATION
# ======================
MYSQL_HOST=mysql-db
MYSQL_PORT=3306
MYSQL_DATABASE=studiostkoh.portafolio
MYSQL_USER=portfolio_user
MYSQL_PASSWORD=your_secure_password_here

# Docker Compose only
MYSQL_ROOT_PASSWORD=root_password_here

# ======================
# JWT SECURITY
# ======================
JWT_TOKEN=your_very_long_secret_key_for_jwt_tokens_minimum_256_bits
JWT_EXPIRATION_TIME=60

# ======================
# GOOGLE DRIVE OAUTH
# ======================
DRIVE_OAUTH_CLIENT_ID=123456789012-abcdefghijklmnop.apps.googleusercontent.com
DRIVE_OAUTH_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxxxxxx
DRIVE_OAUTH_REFRESH_TOKEN=1//0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# ======================
# GOOGLE DRIVE FOLDERS
# ======================
DRIVE_FOLDER_USER_AVATARS=1AbCdEfGhIjKlMnOpQrStUvWxYz
DRIVE_FOLDER_USER_RESUMES=2BcDeFgHiJkLmNoPqRsTuVwXyZa
DRIVE_FOLDER_PROJECTS_COVER=3CdEfGhIjKlMnOpQrStUvWxYzAb
DRIVE_FOLDER_SKILLS_ICON=4DeFgHiJkLmNoPqRsTuVwXyZaBc
DRIVE_FOLDER_CERTIFICATES=5EfGhIjKlMnOpQrStUvWxYzAbCd

# ======================
# EMAIL CONFIGURATION
# ======================
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
GMAIL_APP_EMAIL=[email protected]
GMAIL_APP_PASSWORD=abcd efgh ijkl mnop

# ======================
# CORS CONFIGURATION
# ======================
CORS_ALLOWED_ORIGINS=http://localhost:3000,https://yourdomain.com
Replace all placeholder values with your actual credentials. Never commit this file to version control.

Environment-Specific Configurations

.env.development
MYSQL_HOST=localhost
MYSQL_PORT=3306
JWT_EXPIRATION_TIME=480
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
Characteristics:
  • Local database
  • Longer JWT expiration for convenience
  • Multiple localhost origins for different frontend ports

Security Best Practices

Use Strong Secrets

Generate cryptographically secure random values for all secrets, especially JWT_TOKEN and passwords.

Rotate Credentials

Regularly rotate JWT secrets, database passwords, and API credentials in production.

Never Commit Secrets

Add .env to .gitignore. Use environment-specific templates (.env.example) instead.

Use Secrets Management

In production, use dedicated secrets management tools like AWS Secrets Manager or HashiCorp Vault.

Validation Checklist

Before starting the application, verify:
1

Database Access

Ensure the MySQL server is accessible and the user has proper permissions.
2

JWT Secret Length

Verify JWT_TOKEN is at least 32 characters (256 bits).
3

Google Drive Setup

Confirm all OAuth credentials are valid and folder IDs are correct.
4

Email Configuration

Test SMTP credentials with your email provider.
5

CORS Origins

List all legitimate frontend origins without wildcards.

Troubleshooting

Your JWT_TOKEN is too short. Generate a longer secret:
openssl rand -base64 32
Update your .env file with the new value.
Verify:
  1. MySQL server is running
  2. MYSQL_HOST and MYSQL_PORT are correct
  3. Firewall rules allow connection
  4. Database user has proper permissions
Test connection manually:
mysql -h ${MYSQL_HOST} -P ${MYSQL_PORT} -u ${MYSQL_USER} -p
Check:
  1. OAuth credentials are valid and not expired
  2. Refresh token has https://www.googleapis.com/auth/drive.file scope
  3. All folder IDs exist and are accessible by the OAuth account
  4. Google Drive API is enabled in Google Cloud Console
For Gmail:
  1. Ensure 2-Step Verification is enabled
  2. Use an App Password, not your regular password
  3. Check that “Less secure app access” is not blocking the connection
For other providers:
  1. Verify SMTP host and port
  2. Check firewall rules for outbound SMTP traffic
Add your frontend origin to CORS_ALLOWED_ORIGINS:
CORS_ALLOWED_ORIGINS=http://localhost:3000,https://yourfrontend.com
Restart the application after changing this value.

Next Steps

Docker Deployment

Deploy using Docker and Docker Compose

API Authentication

Learn how to authenticate with JWT

Build docs developers (and LLMs) love