Skip to content
CASE STUDY
Production
System Case Study

First Onw HR

Production

Modern full-stack employee management platform — Laravel 12, PHP 8, MySQL, Blade & Eloquent, re-engineered stateless-first for Vercel serverless PHP on AWS Lambda.

Laravel 12PHPMySQLBladeEloquent

Overview

First Onw HR is a complete, authenticated employee management platform: registration, login, email verification, password reset, profile management, and a paginated employee directory with full CRUD flows. Build on a modern Laravel 12 + MySQL full-stack, then re-engineered stateless-first so it runs flawlessly on Vercel's serverless PHP runtime. The engineering thesis is not the CRUD — it is the deployment substrate. Every framework default that assumed a persistent filesystem was deliberately re-bound.

Problem Statement

Laravel expects a persistent, writable filesystem — storage/ for compiled views, sessions, cache and logs; bootstrap/cache for configuration; and a long-lived web server process to boot the framework. A stateless function runtime provides none of these: the filesystem is read-only after deploy, /tmp is ephemeral and instance-local, and every invocation may boot cold.

Architecture & Tech Stack

A bespoke api/index.php front controller pre-declares the storage override, idempotently materializes the writable tree under /tmp, then rebinds storage via Application::useStoragePath() before Laravel boots. All state moves out of the filesystem: SESSION_DRIVER, CACHE_STORE and QUEUE_CONNECTION run on the shared Aiven MySQL instance, converting stateless functions into a stateful application. Blade views and Eloquent models stay idiomatic Laravel while the hybrid Vercel build graph (framework: null) keeps the PHP lambda and the CDN-served static edge on one domain.

Laravel 12PHP 8MySQLBladeEloquentVercel PHPPestVite

Challenges

  • ▸Read-only filesystem after deploy — Laravel's first eager-loaded compiled view threw file_put_contents errors until the storage path was re-bound to /tmp
  • ▸Ephemeral /tmp breaks sessions — file-backed sessions silently 'forget' users, so all state moved to database drivers
  • ▸Cold-start latency — dev-only providers suppressed via dont-discover, optimized autoloader, and committed Vite assets served from the edge
  • ▸TLS terminated at the CDN — URL::forceScheme('https') in production to avoid mixed-content and broken redirects

Key Metrics

25

Records per page — simplePaginate(25)

4

Employee CRUD ops over 6 fields

5

Feature tests on the full lifecycle + auth guards

17

Commits incl. an explicit serverless-hardening phase

STATUS: Production