Rock8Cloud
Blueprints

PHP — Blueprint

REST API and HTML frontend with plain PHP 8.3, nginx, and PostgreSQL

REST API backend and HTML frontend built with plain PHP 8.3, nginx, and PostgreSQL — no framework. Intended as a starting point for Kubernetes-deployed services: one table, one API endpoint, full Docker support out of the box.

Tech stack:

LayerTechnology
LanguagePHP 8.3
Web servernginx + PHP-FPM
DatabasePostgreSQL (PDO)
MigrationsCustom runner (plain SQL)

The application runs on port 80. The root / serves an HTML page with a space targets table; the API is available under /api/.


Project Structure

public/
└── index.php                # Front controller: matches URI to handler

src/
├── db.php                   # PDO singleton (DATABASE_URL or DB_*)
├── migrate.php              # Reads and executes SQL from migrations/
└── handlers/
    ├── home.php             # GET / — HTML page with inline CSS + JS
    ├── targets.php          # GET /api/targets — JSON response
    └── info.php             # GET /api/info — JSON response

migrations/
└── V1__init.sql             # Creates space_target table + seed data

nginx/
└── default.conf             # nginx config: passes .php requests to PHP-FPM

entrypoint.sh                # Runs migration, starts php-fpm + nginx

Key design decisions:

  • public/index.php is the single entry point. It uses PHP 8 match to route by REQUEST_URI to handler files. To add a new endpoint, add a match arm and create the corresponding file under src/handlers/.
  • src/db.php creates a PDO instance once (static variable) and reuses it across all requests in the same FPM worker. It parses DATABASE_URL (the postgres:// format) or falls back to individual DB_* variables.
  • entrypoint.sh runs src/migrate.php before starting the server. Migrations are plain SQL files in migrations/; the runner tracks executed files in a schema_migrations table.
  • The Dockerfile is a single-stage build: it installs nginx, PHP-FPM, and the pdo_pgsql extension in one Alpine image, then copies the app. There is no compiled artifact — PHP files are served directly.
  • The HTML page in src/handlers/home.php includes inline CSS and JavaScript. The JS fetches /api/info and /api/targets on load and renders the table.

Environment Variables

The application runs on port 80 and requires a PostgreSQL database.

Database connection is configured via one of two approaches:

VariableRequiredExample
DATABASE_URLyespostgres://user:pass@host:5432/dbname?sslmode=require

Option B — Individual variables

VariableRequiredDefaultDescription
DB_HOSTnolocalhostDatabase host
DB_PORTno5432Database port
DB_NAMEnopostgresDatabase name
DB_USERnopostgresDatabase user
DB_PASSWORDno(empty)Database password

DATABASE_URL takes precedence. If it is set, the individual DB_* variables are ignored.


Running Locally

With Docker Compose

docker compose up

Starts the application together with a PostgreSQL instance and sets DATABASE_URL automatically. No additional configuration needed.

Standalone

Requires PHP 8.3 with pdo_pgsql, and a running PostgreSQL instance.

# run migrations
DATABASE_URL=postgres://postgres:postgres@localhost:5432/spacedb php src/migrate.php

# start the built-in development server
DATABASE_URL=postgres://postgres:postgres@localhost:5432/spacedb php -S localhost:8080 public/index.php

# or with individual variables
DB_HOST=localhost DB_USER=postgres DB_PASSWORD=secret php src/migrate.php
DB_HOST=localhost DB_USER=postgres DB_PASSWORD=secret php -S localhost:8080 public/index.php

On this page