
Laravel + Vue.js website that scaled to 40k products
In this article, I will share my story of how I managed to handle 40,000 products on a website running on a shared server on just Laravel and Vue.js. No kafka, no redis, no kubernetes, no horizontal scaling, just pure optimizations and smart logics.
How it started
It all started with a YouTube tutorial. I was learning Node.js and MongoDB, and when I learned enough, I thought that I should create a tutorial on this and teach others by building a real web application. So I started developing a social network and a video streaming website, both in Node.js and MongoDB. At that time, I was using EJS (a templating engine) as frontend of those websites.
A client reached out to me asking for a social network similar to the current one I have, but with some extra features. His plan was to build a hybrid website that has social network, video streaming platform, and an ecommerce platform. Once the social network platform is done, then it was the time to deploy it to a server and make it a live website. As we all know, Node.js requires a dedicated or VPS hosting at the least. The client was on a tight budget. He wanted to start with something cheaper, like shared hosting, and then upgrade to VPS if needed.
Moving from Node.js to Laravel
So I rewrote everything. I build the entire website again but this time in Laravel and used Vue.js as a frontend. It took me about 3 months to rewrite all the modules from Node.js to Laravel and MongoDB to MySQL. Finally, when it completed, I deployed it to a shared hosting environment on hostinger for just $5/month. By the way, the cheapest VPS I found was for $69/month.
Adding more platforms
Once client starts getting users, he starts discussing to add more platforms in it. I added video streaming platform where user can upload videos, and an ecommerce platform where sellers can add products from their dashboard and buyers can buy them. It also has a feature where you can upload your video on streaming platform and link it with your product on ecommerce platform.
First big problem: Database load
With over 40,000 products, the website starts getting slower on product listing and also on product detail page. Because in order to fetch the detail of a single product, we have to join with other tables to fetch relevant data. We all know we can optimize the database using indexes. But I did 2 more things:
- Converted all the Eloquent models to database query builders.
- Changed DB structure to save related fields in the same table.
For example, with each product, I have to show the name of seller. So instead of using “seller_id” in products table and joining it with users table, I have created a column “seller” and save the ID and name of seller in JSON format. This prevents INNER JOIN with each row and significantly reduced the load on database.
Eloquent is an easy way that comes with a lot of built-in functions and you can use relationships using belongsTo method. But it comes with a cost. If you are looping through a collection and you call a relationship, it calls another query to that table and fetch it’s record. So if your query returns 15 records, then you have called 16 times to the database (1 for main query and 1 in each loop).
So using query builder solves this problem. You have to write all the DB logic, joins, column projections by yourself, but now you will be calling DB only 1 time to show 15 records. So there is a great difference between 16 and 1.
Rendering issues
I was doing most of the rendering on client side via Vue.js. That takes the rendering load out of the server and put it on each client. But with Vue.js rendering mechanism, it handles large and complex UIs efficiently.
I used lazy loading for images. But I went 1 step ahead. I used lazy loading for each component too. So if user has opened a large page, then the bottom section of the page will only be rendered when user scrolled to them. Because there is no need to scroll the whole page if user does not scroll to it. I used this technique to do that.
Handling images
Except for handling the images as lazy loading, I also added a function that is called when seller is uploading a product. That function grabs all the uploaded images of the seller and apply lossless compression to it. I have seen great results of that compression. Original image of ~3 MB is reduced to just 324 KB and it did not lose the quality.
Queued tasks
I ran most of my background tasks in a cron job. For example, a cron job that runs daily and normalize the tables by coping the columns from related table and save them in the same table. This prevents joins on multiple table, so I get all the required data from the same table without applying joins. It wasn’t that nice but it did worked.
What I learned ?
It’s not about just writing the code and using the latest tech stacks, it’s about solving the problem of a client. You just need to get started with minimal and simpler version. Then you can optimize, and change your own code and make it efficient.
I have learned that in order to deliver the best results, you must have both frontend and backend knowledge. Even if your expertise is in 1 field, you must have a little bit of knowledge of the other one.
If you’re reading this and thinking, “I need a developer who can handle large-scale projects like that!“—I might be the right fit for you. Whether you need an e-commerce platform, a custom web app, or even just a few tweaks to your existing site, I offer freelance web development services that focus on performance, scalability, and delivering value to your users.
I specialize in Laravel, Node.js, MongoDB, React.js, Vue.js, and modern web development practices, and I’m passionate about creating websites that not only meet your immediate goals but also grow with your business. If you’re looking for a developer who is dedicated to creating high-quality, efficient solutions that scale, I’d love to chat and learn more about your project.