Full-Stack Development: Bridging Frontend and Backend in a Real Project

Building MediaDroppy as a full-stack application highlighted the challenges of coordinating between a React frontend and multiple Spring Boot microservices. While the separation provided architectural benefits, it also introduced integration complexities that required careful planning and execution. This article explores the practical challenges of full-stack development in a distributed system.

The Authentication Dance

Implementing secure authentication across frontend and backend proved more complex than anticipated. The flow involves multiple steps: user login triggers OAuth2 authentication, the backend issues JWT tokens, the frontend stores them securely, and every API request includes the token for verification.

Challenge: Token management. Deciding where to store tokens (localStorage vs. memory vs. httpOnly cookies) involved security tradeoffs. localStorage is vulnerable to XSS attacks, memory-only storage loses tokens on page refresh, and httpOnly cookies complicate CORS.

We settled on a hybrid approach: short-lived access tokens in memory with refresh tokens in httpOnly cookies. This required implementing token refresh logic that gracefully handled expired tokens and retry failed requests.

Challenge: Authentication state synchronization. The React application needed to maintain authentication state across components. Initially using prop drilling became unmaintainable, leading to the implementation of an AuthProvider context. This centralized auth state management but required careful handling of state updates to prevent unnecessary re-renders.

File Uploads: Bridging Binary Data

Implementing file uploads exposed the complexity of handling binary data across the stack. The frontend needed to support drag-and-drop, show upload progress, handle large files efficiently, and provide clear error feedback.

Key implementation decisions:

API Design and Versioning

As the application evolved, maintaining API stability while adding features became critical. Breaking changes in backend APIs would break the frontend, requiring coordinated releases.

Lessons learned:

Error Handling Across Layers

Proper error handling in a full-stack application requires coordination across multiple layers. Backend exceptions must be translated to meaningful HTTP status codes, which the frontend then interprets and presents to users.

We implemented a consistent error response format:

{
  "error": "RESOURCE_NOT_FOUND",
  "message": "The requested file does not exist",
  "timestamp": "2025-11-20T10:30:00Z"
}
    

The frontend error handling middleware intercepts failed requests and translates error codes to user-friendly messages. This centralized approach prevented duplicate error handling logic across components.

The challenge: Network errors, backend errors, and application errors all require different handling strategies. Distinguishing between these error types and responding appropriately took several iterations to get right.

State Management and Data Flow

Managing application state across a React frontend interfacing with multiple backend services revealed the importance of thoughtful data flow architecture.

Initial problems:

Solutions implemented:

Development Environment Coordination

Running the full stack locally for development required orchestrating multiple services. Initially, this meant starting six separate processes manually, which was error-prone and time-consuming.

We addressed this through:

CORS: The Inevitable Headache

Cross-Origin Resource Sharing (CORS) caused more debugging hours than I care to admit. With the React app served from one domain and APIs from another, every request triggered preflight OPTIONS requests.

Key lessons:

Key Takeaways

Full-stack development in a microservices architecture amplifies both the benefits and challenges of separating concerns. The key to success lies in establishing clear contracts, implementing consistent patterns across layers, and investing in tooling that makes the development experience manageable. MediaDroppy taught me that being a full-stack developer means more than knowing both frontend and backend technologies—it means understanding how they integrate and interact as a cohesive system.