Building a Laravel Marketplace with PostgreSQL While Debugging React State
Project Overview
I spent the last few days building instead of just watching - a small Laravel marketplace project backed by PostgreSQL, plus some hands-on React work. Writing down where I got stuck proved to be the most useful part of the process.
Laravel Backend Development
On the Laravel side, I went back to fundamentals first: routes, controllers, views, and Blade. I practiced the full request cycle - a route hitting a controller, the controller passing data to a view, and Blade handling variables, loops, and conditionals. I also worked through basic form validation to build muscle memory before tackling more complex scenarios.
The more interesting part was designing the database for a marketplace-style project with PostgreSQL. The plan includes four roles - Admin, Moderator, Seller, and Buyer - guided by two business rules that shaped the schema:
- A seller cannot buy their own product (though buying from other sellers is allowed).
- A seller's product does not go live immediately upon creation; it must be approved by an Admin or Moderator first.
Translating these rules into actual migrations was where the real learning happened. I added a role field to the users table (defaulting to buyer), then built a products table with columns: seller_id, title, description, price, stock, status, approved_by, and approved_at. New products start with a pending status so the approval workflow has something to act on.
Working through this made migration concepts click in a way tutorials hadn't - covering up() and down() methods, foreign keys, nullable columns, defaults, and relationships. The seller relationship uses cascading deletes (deleting a seller removes their products), while the approver relationship sets approved_by to null if that admin or moderator is removed - the product itself remains. Seeing why those two behaviors differ made foreign key constraints feel far less abstract.
Database Migration Concepts
The following elements were central to understanding the schema:
- Roles: Admin, Moderator, Seller, Buyer (with
rolecolumn inuserstable, defaulting tobuyer) - Products Table Columns:
seller_id,title,description,price,stock,status,approved_by,approved_at - Business Rules:
- A seller cannot buy their own product (inter-seller purchases are permitted).
- A seller's product requires approval by an Admin or Moderator before going live.
Environment Issues
Running Laravel's db:table command failed due to a missing PHP intl extension. It initially seemed like a coding problem, but realizing the error was unrelated to the code - simply a missing PHP extension - was its own challenge. Debugging this environmental issue felt just as valuable as writing the migrations themselves.
React Frontend Implementation
On the React side, I worked with useState and useEffect, fetching show data from the TVMaze API. The core lesson was that fetch only rejects on network failures - not on HTTP errors like 404 or 500. To handle this properly, I checked response.ok explicitly and threw an error when it failed, wrapping the entire request in try/catch/finally to keep loading, data, and error states cleanly separated:
useEffect(() => {
const fetchShows = async () => {
setLoading(true);
try {
const res = await fetch("https://api.tvmaze.com/search/shows?q=all");
if (!res.ok) throw new Error("Failed to fetch shows");
const data = await res.json();
setShows(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchShows();
}, []);
Common Bugs and Lessons Learned
Two specific issues surfaced during development:
Stale State Logging: Logging state immediately after calling
setShowsstill displayed the old value. This occurred because the log read a stale value from that render's closure before React had re-rendered with the new state. Chasing this down taught me more about how React's state updates and re-renders actually work than any explanation could.Fetch Behavior Misconception: Understanding that
fetchonly rejects on network failures (not on HTTP error responses like 404 or 500) led to proper error handling with explicitresponse.okchecks and structuredtry/catch/finallyblocks.
Key Takeaways
The biggest takeaway from this stretch is that learning a framework isn't about memorizing syntax alone - it's about how routes, controllers, migrations, relationships, API calls, and state management all work together inside one real application. The next steps involve building out the approval flow and seller dashboard on top of this schema, along with refining React's data-fetching patterns. This article was originally published on Zahid Hasan Tonmoy's Portfolio. Connect with Zahid on GitHub & LinkedIn.
Comments
No comments yet. Start the discussion.