Case Study

IssueHub — Decoupled Ticket Management System with Laravel API and React SPA

IssueHub is a fullstack ticket management application built to explore API-first development with a clear separation between backend and frontend responsibilities. This case study focuses on how the Laravel REST API and the React SPA were structured, how authentication and ticket workflows were modeled, and how the project was designed to feel closer to a real internal tool or lightweight SaaS product than a simple CRUD exercise.

Portfolio fullstack case study
Role: Fullstack Laravel Engineer Laravel 13 · React 19 · TypeScript · Sanctum · TanStack Query · Axios · TailwindCSS

Overview

IssueHub was built as a portfolio-ready fullstack project focused on a common product scenario: managing tickets through a clean internal workflow. The application combines a Laravel 13 REST API with a React + TypeScript SPA, allowing authenticated users to create, browse, update and delete tickets while navigating the interface like a modern client-side application.

The main objective was to move beyond server-rendered CRUD pages and work with a clearer separation between backend and frontend concerns. That meant handling authentication, protected access, API contracts, server state, filters and ticket workflows in a way that feels much closer to a real internal tool or early-stage SaaS product.

System Architecture

Monorepo Structure

The project was organized as a monorepo containing both the Laravel backend and the React SPA. This made it easier to keep the system aligned during development while still preserving a clear architectural boundary between API logic and frontend responsibilities.

Decoupled Application Layers

Laravel acts purely as a REST API, exposing authenticated endpoints for ticket workflows and dashboard metrics. The frontend is a separate SPA built with React and TypeScript, consuming those endpoints through an Axios-based API layer and managing remote data with TanStack Query.

Product-Oriented Flow

Rather than focusing only on isolated CRUD screens, the project was structured around a product-like user flow: authentication, dashboard visibility, ticket creation, filtering, status updates and recent activity. This made the project feel more operational and closer to an actual internal tool.

API-Driven State

Because the frontend does not share server-rendered views with Laravel, all important interactions depend on a predictable API contract. This made response structure, loading states, mutations, cache invalidation and protected access significantly more relevant than in a traditional Blade-based application.

Key principle: keep the frontend and backend clearly separated, but make both layers feel cohesive through a clean API contract and a product-oriented user flow.

Request Flow

Frontend

React SPA

Client-side interface built with React, TypeScript and React Router.

Communication

Axios + Query

API requests, remote state handling, caching and mutation flows.

Backend

Laravel API

Sanctum auth, validation, authorization and structured JSON responses.

Data

MySQL

Persistent storage for users, tickets, statuses and related workflow data.

Backend API

The backend was built with Laravel 13 as a dedicated REST API. Its responsibilities include authenticating users, protecting private routes, validating incoming data, enforcing ownership rules and returning structured JSON responses for the SPA.

Authentication is handled with Laravel Sanctum using token-based access, which made it possible to model a more realistic SPA authentication flow than a classic session-driven interface. Protected endpoints ensure that authenticated users only access the tickets and actions they are authorized to manage.

Dedicated validation, API Resources and Authorization Policies were used to keep the backend cleaner and more maintainable. This made the API easier to reason about and closer to how a production-ready Laravel backend would typically be structured.

SPA Frontend

The frontend was built with React 19, TypeScript and Vite as a separate single-page application. React Router was used for navigation, while Axios handled communication with the backend API through a lightweight abstraction layer.

TanStack Query played an important role in keeping the interface predictable by managing remote data, cache invalidation, loading states and mutations. This made the dashboard and ticket management screens feel more stable and app-like without overengineering the frontend.

Protected SPA routes were also introduced to ensure that the authenticated experience remained coherent on the client side. Tailwind CSS was used to keep the UI clean, responsive and fast to iterate on, with the goal of producing a polished internal-tool style interface rather than a heavily branded product.

Technical Decisions

  • Decoupled architecture instead of a monolithic Laravel frontend: the project was intentionally split into API and SPA layers to practice cleaner boundaries and a more modern fullstack workflow.
  • Sanctum for token-based authentication: a practical fit for protected API access and a better match for a frontend consuming the backend independently.
  • TanStack Query for server-state management: it simplified remote data handling, cache invalidation and mutation flows across dashboard and ticket views.
  • Axios abstraction for API communication: centralizing requests made it easier to keep the frontend cleaner and maintain a more consistent interaction layer.
  • URL-driven filtering and navigation: search, status and priority filters were treated as part of the product flow, not just visual extras.
  • Monorepo organization: keeping both applications in the same repository improved coordination during development while preserving separation of concerns.

Ticket Workflow

The ticket workflow was designed to cover the core lifecycle of a lightweight management system. Users can create new tickets, review ticket details, edit existing information, update status and remove records when needed.

Status handling was kept intentionally clear, with tickets moving through explicit states such as open, in_progress and closed. This makes the interface easier to scan and the workflow easier to reason about from both the API and the frontend.

On top of the CRUD flow, the product includes dashboard metrics, recent activity, search, filtering by status and priority, pagination and URL-driven state. These details help the system feel more like a usable internal tool and less like an isolated demo.

Lessons Learned

Building IssueHub reinforced how different Laravel feels when it is used as a dedicated backend instead of rendering the interface directly. Once the frontend is separated, authentication, response design, ownership rules and mutation flows all become more explicit and intentional.

The project was also useful for practicing a more realistic fullstack workflow: React on the client, Laravel on the server, and a clear contract between both layers. That shift made the project stronger not only as a technical exercise, but as a portfolio piece that better reflects modern product development.

Future Improvements

Future iterations could introduce role-based permissions, ticket comments, attachments, richer activity history, notifications, stronger frontend testing, broader backend test coverage and deployment using separate production environments for the API and the SPA. It would also be interesting to evolve the dashboard into a more complete reporting layer with trends, counters and team-level visibility.