Skip to main content
MediaStream is built on a modern full-stack architecture combining Laravel 12 (backend), Vue.js 3 (frontend), and Inertia.js as the bridge between them.

Technology Stack

Backend (Laravel 12)

The server-side is powered by Laravel with the following key dependencies:
{
  "php": "^8.2",
  "laravel/framework": "^12.0",
  "inertiajs/inertia-laravel": "^2.0",
  "laravel/fortify": "^1.30",
  "laravel/wayfinder": "^0.1.9",
  "laravel/tinker": "^2.10.1"
}
Laravel 12 requires PHP 8.2 or higher and includes the latest performance optimizations and type safety improvements.

Frontend (Vue.js 3 + TypeScript)

The client-side uses Vue.js 3 with TypeScript and modern tooling:
{
  "vue": "^3.5.13",
  "@inertiajs/vue3": "^2.1.0",
  "typescript": "^5.2.2",
  "vite": "^7.0.4",
  "tailwindcss": "^4.1.1"
}
UI Components:
  • reka-ui (^2.6.0) - Headless component primitives
  • lucide-vue-next (^0.468.0) - Icon system
  • vee-validate + zod - Form validation
  • @vueuse/core - Composition utilities

Architectural Patterns

Inertia.js - The Modern Monolith

Inertia.js enables building SPAs without building an API. It acts as the glue between Laravel and Vue:
// app/Http/Controllers/Web/SeriesController.php
public function index()
{
    $response = MediastreamService::request('/show', 'get');
    
    return Inertia::render('(media)/series/index', [
        'data' => $response->successful() ? $response->json('data') : [],
    ]);
}
// resources/js/app.ts
createInertiaApp({
    title: (title) => (title ? `${title} - ${appName}` : appName),
    resolve: (name) =>
        resolvePageComponent(
            `./pages/${name}.vue`,
            import.meta.glob<DefineComponent>('./pages/**/*.vue'),
        ),
    setup({ el, App, props, plugin }) {
        createApp({ render: () => h(App, props) })
            .use(plugin)
            .mount(el);
    },
});
Inertia eliminates the need for a REST or GraphQL API layer between Laravel and Vue, while still providing a SPA experience.

Shared Data via Middleware

Global data is shared across all Inertia responses through middleware:
// app/Http/Middleware/HandleInertiaRequests.php:37
public function share(Request $request): array
{
    return [
        ...parent::share($request),
        'name' => config('app.name'),
        'auth' => [
            'user' => $request->user(),
        ],
        'sidebarOpen' => ! $request->hasCookie('sidebar_state') 
            || $request->cookie('sidebar_state') === 'true',
    ];
}
This data is available in all Vue components via $page.props.

Directory Structure

Backend Structure

app/
├── Actions/          # Fortify actions (user registration, password reset)
│   └── Fortify/
├── Http/
│   ├── Controllers/  # Request handlers
│   │   ├── Api/      # API endpoints for AJAX requests
│   │   ├── Web/      # Inertia page controllers
│   │   └── Settings/ # User settings controllers
│   ├── Middleware/   # HTTP middleware (Inertia, Appearance)
│   ├── Requests/     # Form request validation
│   └── Services/     # Business logic services
├── Models/           # Eloquent models
└── Providers/        # Service providers

Frontend Structure

resources/js/
├── app.ts            # Client-side entry point
├── ssr.ts            # Server-side rendering entry point
├── components/       # Reusable components
│   ├── custom/       # Application-specific components
│   ├── template/     # Feature components (media, series)
│   └── ui/           # Base UI components (23+ components)
├── composables/      # Vue composition functions
├── layouts/          # Page layouts (App, Auth, Settings)
├── lib/              # Utility libraries
├── pages/            # Inertia page components (22+ pages)
│   ├── (media)/      # Media management pages (grouped route)
│   ├── auth/         # Authentication pages
│   └── settings/     # User settings pages
└── types/            # TypeScript type definitions

Routing Architecture

Laravel Wayfinder

MediaStream uses Laravel Wayfinder for file-based routing:
// vite.config.ts
import { wayfinder } from '@laravel/vite-plugin-wayfinder';

export default defineConfig({
    plugins: [
        wayfinder({
            formVariants: true,
        }),
    ],
});

Route Organization

Routes are split across multiple files:
  • routes/web.php - Inertia page routes (resources/js/pages)
  • routes/api.php - API routes for AJAX requests
  • routes/settings.php - User settings routes
// routes/web.php
Route::middleware('auth')->group(function () {
    Route::resource('series', SeriesController::class)
        ->parameters(['series' => 'showId']);
    
    Route::resource('series.seasons', SeasonController::class)
        ->parameters([
            'series' => 'showId',
            'seasons' => 'seasonId',
        ]);
});

External API Integration

MediaStream integrates with an external media streaming API via a centralized service:
// app/Http/Services/MediastreamService.php:10
public static function request(string $endpoint, string $method = 'get', array $data = []): Response
{
    $baseUrl = rtrim(env('MEDIASTREAM_API_URL'), '/');
    $url = $baseUrl . '/' . ltrim($endpoint, '/');
    
    $client = Http::withHeaders([
        'X-API-Token' => env('MEDIASTREAM_API_KEY'),
        'Accept' => 'application/json',
    ]);
    
    return $client->{$method}($url, $data);
}
All external API calls are centralized through MediastreamService for consistent authentication and error handling.

Build System

Vite Configuration

MediaStream uses Vite 7 for lightning-fast builds:
// vite.config.ts
export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/js/app.ts'],
            ssr: 'resources/js/ssr.ts',
            refresh: true,
        }),
        tailwindcss(),
        wayfinder({ formVariants: true }),
        vue({
            template: {
                transformAssetUrls: {
                    base: null,
                    includeAbsolute: false,
                },
            },
        }),
    ],
});

Development Scripts

The composer.json includes convenient development scripts:
{
  "scripts": {
    "dev": "npx concurrently \"php artisan serve\" \"php artisan queue:listen\" \"php artisan pail\" \"npm run dev\"",
    "dev:ssr": "npm run build:ssr && npx concurrently \"php artisan serve\" \"php artisan inertia:start-ssr\""
  }
}
Run composer dev to start all development services in parallel.

Authentication & Security

Laravel Fortify

User authentication is handled by Laravel Fortify with two-factor authentication support:
  • Registration, login, password reset
  • Two-factor authentication (TOTP)
  • Email verification (optional)
  • Password confirmation
See app/Actions/Fortify/ for implementation details.

SSR Support

MediaStream includes optional server-side rendering:
// resources/js/ssr.ts
import { createInertiaApp } from '@inertiajs/vue3';
import { renderToString } from '@vue/server-renderer';
import { createSSRApp, h } from 'vue';

createInertiaApp({
    render: renderToString,
    setup({ App, props, plugin }) {
        return createSSRApp({ render: () => h(App, props) }).use(plugin);
    },
});
Build for SSR with npm run build:ssr and start with php artisan inertia:start-ssr.

Next Steps

Frontend Development

Learn about Vue.js components, composables, and TypeScript patterns

Backend Development

Explore Laravel controllers, services, and API integration

Database Schema

Understand the database structure and migrations

Build docs developers (and LLMs) love