DEV Community

I Built a Job Platform With Laravel 13, Livewire 4, and GPT-5 — Here's What I Learned

TL;DR

I'm building UWork.kz — a local services marketplace in Kazakhstan. The stack: Laravel 13 + Livewire 4 + Alpine.js + Tailwind CSS (the TALL stack), with GPT-5 handling SEO content and smart internal linking. In this post I'll break down the architecture decisions, the trade-offs, and the things that surprised me along the way.

Why TALL Stack in 2026?

Every second dev.to post right now is about Next.js, Nuxt, or some React meta-framework. Fair enough — they're great tools. But when you're a solo developer building a marketplace for a specific regional market, you need to ship fast and maintain easily.
Here's why I went with TALL:
One language, one mental model. With Livewire 4, my reactive components are just PHP classes. No context-switching between a REST API and a React frontend. No state management library. No hydration headaches. I write a PHP class, drop a Blade component, and it just works.
Filament 5 for admin. Building a custom admin panel from scratch is a time sink. Filament gave me a fully functional back-office in days — user management, content moderation, analytics dashboards — all with minimal custom code.
Alpine.js for the small stuff. Modals, dropdowns, mobile menus, form validation feedback — Alpine handles these without pulling in a 50kb JavaScript framework.

The Architecture

Here's a simplified view of the stack:
┌──────────────────────────────────┐
│ Nginx │
│ SSL · FastCGI Cache · GeoIP │
└──────────────┬───────────────────┘

┌──────────────▼───────────────────┐
│ PHP 8.5 FPM │
│ Laravel 13 Application │
│ │
│ ┌───────────┐ ┌─────────────┐ │
│ │ Livewire 4│ │ Filament 5 │ │
│ │ (Frontend)│ │ (Admin) │ │
│ └───────────┘ └─────────────┘ │
│ │
│ ┌───────────┐ ┌─────────────┐ │
│ │ Eloquent │ │ Horizon │ │
│ │ (ORM) │ │ (Queues) │ │
│ └─────┬─────┘ └──────┬──────┘ │
└────────┼────────────────┼────────┘
│ │
┌────▼────┐ ┌─────▼─────┐
│ MySQL │ │ Redis │
│ 8.4 │ │Cache/Queue│
└─────────┘ └───────────┘

Everything runs in Docker Compose — one docker compose up and you have the full environment. Same config for dev and prod, just different .env values.

How GPT-5 Fits Into a Laravel App

This is where it gets interesting. I use the OpenAI API for three things:

1. SEO Meta Generation

Every service listing needs a unique meta title and description. Writing these manually for thousands of listings? Not happening. I have a queued job that generates SEO-optimized meta tags:

class GenerateListingSeo implements ShouldQueue
{
    public function handle(OpenAIService $ai): void
    {
        $prompt = "Write a meta title (max 60 chars) and 
                   meta description (max 155 chars) for a 
                   service listing: {$this->listing->title} 
                   in {$this->listing->city}. 
                   Language: Russian.";

        $result = $ai->complete($prompt);

        $this->listing->update([
            'meta_title' => $result['title'],
            'meta_description' => $result['description'],
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Smart Internal Linking

GPT analyzes page content and suggests relevant internal links. This boosted our internal link density significantly — something that's hard to do manually at scale.

3. Content Analysis

New listings go through a content quality check. The AI flags low-effort descriptions, duplicate content, and suggests improvements to the service provider.
The key insight: Don't call the API synchronously. Everything goes through Laravel's queue system with Redis as the driver. Horizon monitors the queues, and if something fails, it retries with exponential backoff.

Redis: The Swiss Army Knife

Redis does a lot of heavy lifting in this stack:

  • Cache: Query results, computed statistics, category trees
  • Queues: All async jobs (emails, SEO generation, notifications)
  • Sessions: User session storage
  • Rate limiting: API and bot protection
  • Stats buffer: Real-time counters (views, clicks) that flush to MySQL periodically

That last one is worth explaining. Instead of running an UPDATE query on every page view, I increment a Redis counter. A scheduled command runs every 5 minutes, reads the counters, batch-updates MySQL, and resets them. This pattern reduced write load on the database dramatically.
`// On page view
Redis::hincrby("listing:views:{$date}", $listingId, 1);

// Scheduled flush (every 5 min)
$views = Redis::hgetall("listing:views:{$date}");
foreach (array_chunk($views, 500, true) as $batch) {
foreach ($batch as $id => $count) {
Listing::where('id', $id)
->increment('daily_views', (int) $count);
}
}
Redis::del("listing:views:{$date}");`

Nginx: More Than Just a Reverse Proxy

Our Nginx config does quite a bit:

  • FastCGI caching for anonymous users — category pages and landing pages serve from cache, bypassing PHP entirely
  • GeoIP filtering — the platform targets Kazakhstan, so we handle geo-specific logic at the Nginx level
  • SSL termination — Let's Encrypt with auto-renewal

The FastCGI cache alone cut TTFB from ~200ms to ~15ms for cached pages. For a job platform where most visitors are anonymous searchers, this is a massive win.

The Frontend: No Build Step (Almost)

One thing I love about the TALL stack is the simplicity of the frontend:

  • Tailwind CSS v4 + DaisyUI v5 for design system components
  • Livewire 4 for reactive server-side components (search, filters, forms)
  • Alpine.js v3 for client-side interactions

We use Vite 8 (with the Rolldown bundler) only to compile Tailwind and handle asset versioning. There's almost no custom JavaScript. The entire frontend is Blade templates + Livewire components + Alpine directives.
For example, our real-time search:
<div x-data="{ open: false }">
<input
wire:model.live.debounce.300ms="search"
@focus="open = true"
@click.away="open = false"
placeholder="Search services..."
>
<div x-show="open" x-transition>
@foreach($results as $result)
<a href="{{ $result->url }}">
{{ $result->title }}
</a>
@endforeach
</div>
</div>

No REST endpoint. No fetch(). No state management. Livewire handles the server communication, Alpine handles the dropdown visibility. Clean and simple.

Notifications: Telegram First

Instead of building a custom notification system, we went Telegram-first:

  • New order notifications → Telegram bot
  • Verification codes → Telegram
  • Admin alerts → Telegram channel

Why? Our target audience (service providers in Kazakhstan) lives in Telegram. Email open rates here are around 15%. Telegram message read rates? Over 90%.
We also support Web Push for browser notifications as a fallback, but Telegram is the primary channel.

Testing and DevOps

The testing and deployment setup:

  • PHPUnit 12 for unit and integration tests
  • Laravel Pint for code style (runs in CI)
  • k6 for load testing before releases
  • Docker Compose handles everything — no Kubernetes, no AWS ECS, no complexity tax I know this is controversial in 2026, but Docker Compose on a decent VPS handles a lot more traffic than people think. We're not Netflix. We're a regional marketplace. A single well-configured server with proper caching handles our load just fine.

Lessons Learned

  1. TALL stack is underrated for MVPs. The developer experience is incredible when you don't have to maintain two separate codebases (API + SPA).
  2. AI-generated content needs guardrails. GPT-5 is amazing, but you need validation layers. We check every generated meta tag for length, language, and relevance before saving.
  3. Redis is worth learning deeply. Most tutorials cover basic caching. But using Redis for stats buffering, rate limiting, and queue management transforms your app's performance.
  4. Ship for your audience, not for Hacker News. Telegram notifications > email. Russian-language SEO > English best practices. GeoIP filtering > global CDN. Know your users.
  5. Docker Compose is fine. Seriously. If your app fits on one server (and most apps do), skip the orchestration complexity.

What's Next

We're working on:

  • A recommendation engine for matching users with service providers
  • Real-time chat between clients and providers (Livewire + WebSockets)
  • Mobile app via a PWA approach

If you're building something similar or have questions about the TALL stack in production, drop a comment. Happy to share more details.
Check out the platform: UWork.kz — a local services marketplace connecting people with trusted professionals in Kazakhstan.
If you found this useful, follow me for more posts about Laravel, AI integration, and building products for emerging markets.

Top comments (0)