<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mahmoud ELDegwey</title>
    <description>The latest articles on DEV Community by Mahmoud ELDegwey (@mahmoudeldegwey).</description>
    <link>https://dev.to/mahmoudeldegwey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F852017%2F7976a13b-52c4-4e16-9595-1e30232f234b.png</url>
      <title>DEV Community: Mahmoud ELDegwey</title>
      <link>https://dev.to/mahmoudeldegwey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mahmoudeldegwey"/>
    <language>en</language>
    <item>
      <title>Building a Production-Ready Headless eCommerce API with Laravel</title>
      <dc:creator>Mahmoud ELDegwey</dc:creator>
      <pubDate>Sat, 27 Dec 2025 19:57:27 +0000</pubDate>
      <link>https://dev.to/mahmoudeldegwey/building-a-production-ready-headless-ecommerce-api-with-laravel-26dd</link>
      <guid>https://dev.to/mahmoudeldegwey/building-a-production-ready-headless-ecommerce-api-with-laravel-26dd</guid>
      <description>&lt;p&gt;Headless eCommerce has become a popular architecture for modern web and mobile applications. Instead of coupling the frontend with the backend, developers can build flexible systems where the backend exposes APIs and the frontend consumes them independently.&lt;/p&gt;

&lt;p&gt;In this article, I’ll walk through how I built a production-ready headless eCommerce API using Laravel, the architectural decisions behind it, and lessons learned from real-world projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Headless eCommerce?
&lt;/h2&gt;

&lt;p&gt;Headless eCommerce means separating the backend business logic from the frontend presentation layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The backend:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages authentication&lt;/li&gt;
&lt;li&gt;Handles products, carts, and orders&lt;/li&gt;
&lt;li&gt;Exposes REST APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The frontend:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can be built with Next.js, React, Vue, or mobile apps&lt;/li&gt;
&lt;li&gt;Focuses purely on UI and UX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend flexibility&lt;/li&gt;
&lt;li&gt;Better scalability&lt;/li&gt;
&lt;li&gt;Multi-channel support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I Chose Laravel for a Headless eCommerce API
&lt;/h2&gt;

&lt;p&gt;Laravel is an excellent choice for building APIs because it offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean MVC architecture&lt;/li&gt;
&lt;li&gt;Powerful validation and request handling&lt;/li&gt;
&lt;li&gt;API resources for consistent responses&lt;/li&gt;
&lt;li&gt;Strong ecosystem for authentication and security&lt;/li&gt;
&lt;li&gt;For eCommerce backends, Laravel allows you to move fast without sacrificing code quality.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Defining the Core Requirements
&lt;/h2&gt;

&lt;p&gt;Before starting development, I defined the essential features any eCommerce API must have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure API authentication&lt;/li&gt;
&lt;li&gt;Product and category management&lt;/li&gt;
&lt;li&gt;Brand management &lt;/li&gt;
&lt;li&gt;Product Size and colors managements&lt;/li&gt;
&lt;li&gt;Cart handling&lt;/li&gt;
&lt;li&gt;Checkout and order creation&lt;/li&gt;
&lt;li&gt;Order management&lt;/li&gt;
&lt;li&gt;Order tracking and history&lt;/li&gt;
&lt;li&gt;Clean and predictable API responses&lt;/li&gt;
&lt;li&gt;Coupons management &lt;/li&gt;
&lt;li&gt;Announcements&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Architectural Decisions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Controllers are kept thin&lt;/li&gt;
&lt;li&gt;Business logic lives in service classes&lt;/li&gt;
&lt;li&gt;Validation is handled via Form Requests&lt;/li&gt;
&lt;li&gt;API Resources control response formatting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This makes the codebase:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier to maintain&lt;/li&gt;
&lt;li&gt;Easier to test&lt;/li&gt;
&lt;li&gt;Safer to extend in future versions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  API Authentication Strategy
&lt;/h2&gt;

&lt;p&gt;Because this is a headless API, session-based authentication is not suitable.&lt;br&gt;
Instead, the API uses token-based authentication, making it compatible with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js frontends&lt;/li&gt;
&lt;li&gt;Single Page Applications (SPA)&lt;/li&gt;
&lt;li&gt;Mobile applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Designing Cart and Order Logic
&lt;/h2&gt;

&lt;p&gt;Cart and order handling is one of the most complex parts of any eCommerce system.&lt;/p&gt;

&lt;p&gt;Key decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cart is linked to authenticated users&lt;/li&gt;
&lt;li&gt;Prices are calculated on the backend&lt;/li&gt;
&lt;li&gt;Orders store a snapshot of product data at checkout time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data consistency&lt;/li&gt;
&lt;li&gt;Secure checkout flow&lt;/li&gt;
&lt;li&gt;Accurate order history&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  API Error Handling and Response Format
&lt;/h2&gt;

&lt;p&gt;Consistency is crucial for frontend developers consuming the API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Errors are handled centrally, ensuring:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No internal exceptions leak to the client&lt;/li&gt;
&lt;li&gt;Predictable error responses&lt;/li&gt;
&lt;li&gt;Easier frontend error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Packaging the Headless eCommerce API
&lt;/h2&gt;

&lt;p&gt;After implementing similar logic across multiple projects, I decided to package the solution as a Production-Ready Headless eCommerce API built with Laravel.&lt;/p&gt;

&lt;p&gt;The goal was simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save developers weeks of backend work&lt;/li&gt;
&lt;li&gt;Provide a clean, extensible foundation&lt;/li&gt;
&lt;li&gt;Make frontend integration easy and flexible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API is frontend-agnostic and works well with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Vue&lt;/li&gt;
&lt;li&gt;Mobile apps&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who Should Use This Approach?
&lt;/h2&gt;

&lt;p&gt;This architecture (and the packaged API) is ideal if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build custom eCommerce solutions&lt;/li&gt;
&lt;li&gt;Prefer full control over frontend UX&lt;/li&gt;
&lt;li&gt;Want to avoid rebuilding the same backend logic&lt;/li&gt;
&lt;li&gt;Need a scalable Laravel-based API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re interested in the full packaged solution, you can find it here: [&lt;a href="https://eldgeway.gumroad.com/l/rrivu" rel="noopener noreferrer"&gt;https://eldgeway.gumroad.com/l/rrivu&lt;/a&gt;]&lt;/p&gt;

</description>
      <category>ecommerce</category>
      <category>api</category>
      <category>headless</category>
      <category>backend</category>
    </item>
    <item>
      <title>Print Multiple Order Labels as a Single PDF using Laravel</title>
      <dc:creator>Mahmoud ELDegwey</dc:creator>
      <pubDate>Tue, 31 Dec 2024 14:29:10 +0000</pubDate>
      <link>https://dev.to/mahmoudeldegwey/print-multiple-order-labels-as-a-single-pdf-using-laravel-hhf</link>
      <guid>https://dev.to/mahmoudeldegwey/print-multiple-order-labels-as-a-single-pdf-using-laravel-hhf</guid>
      <description>&lt;p&gt;In this tutorial, I'll walk you through implementing a feature in Laravel to print multiple orders' labels as a single PDF file, especially for scenarios where there are more than 1,000 orders. Handling large numbers of files can lead to timeouts and errors, so we'll solve this issue by using Laravel queues for processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem Statement
&lt;/h2&gt;

&lt;p&gt;When asked to implement a feature to print multiple order labels and merge them into one PDF for more than 2,000 orders using the traditional way, the system can encounter timeouts or exceptions due to take long execution. To resolve this, we can use Laravel queues to process the files in batches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Laravel Project
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer create-project --prefer-dist laravel/laravel laravelPrintMultiLabel  
cd laravelPrintMultiLabel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the .env file with your database configuration and change queue connection&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
DB_CONNECTION=mysql  
DB_HOST=127.0.0.1  
DB_PORT=3306  
DB_DATABASE=laravelPrintMultiLabel  
DB_USERNAME=root  
DB_PASSWORD=root  

QUEUE_CONNECTION=database  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Create a Model and Migration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:model Order -m
php artisan make:queue-batches-table
php artisan migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the code for the Order model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace App\Models
use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected $fillable = [
        'order_number',
        'total_price',
        'product_count',
        'user_id',
        'status',
    ];

    protected $casts = [
        'created_at' =&amp;gt; 'datetime:Y-m-d H:i:s',
        'updated_at' =&amp;gt; 'datetime:Y-m-d H:i:s',
    ];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Migration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up(): void {
        Schema::create('orders', function (Blueprint $table) {
            $table-&amp;gt;id();
            $table-&amp;gt;foreignId('user_id')-&amp;gt;constrained('users');
            $table-&amp;gt;string('order_number', 50);
            $table-&amp;gt;decimal('total_price', 8, 2);
            $table-&amp;gt;integer('product_count');
            $table-&amp;gt;enum('status', ['delivered', 'pending']);
            $table-&amp;gt;timestamps();
            $table-&amp;gt;softDeletes();
        });
    }

    public function down(): void {
        Schema::dropIfExists('orders');
    }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Add Dummy Data
&lt;/h2&gt;

&lt;p&gt;Use Laravel seeders to add to the orders table with dummy data for testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:seeder OrderSeeder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Install Required Packages
&lt;/h2&gt;

&lt;p&gt;Install the packages for generating and merging PDF files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require barryvdh/laravel-snappy
composer require webklex/laravel-pdfmerger

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Create the Job Classes
&lt;/h2&gt;

&lt;p&gt;Generate two job classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:job MultiOrdersLabelPdfExport
php artisan make:job MergePDFFilesMultiOrderLabelJob
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MultiOrdersLabelPdfExport (Responsible for generating PDFs):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Barryvdh\Snappy\Facades\SnappyPdf;

class MultiOrdersLabelPdfExport extends Jobs {
    public function __construct(public $orders) {}

    public function handle(): void {
        SnappyPdf::loadView('multi_label', ['orders' =&amp;gt; $this-&amp;gt;orders])
            -&amp;gt;setPaper('A4', 'portrait')
            -&amp;gt;save(public_path($this-&amp;gt;batch()-&amp;gt;id . '/' . microtime(true) . '.pdf'));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MergePDFFilesMultiOrderLabelJob (Responsible for merging PDFs):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Webklex\PDFMerger\Facades\PDFMergerFacade;
use Illuminate\Support\Facades\File;

class MergePDFFilesMultiOrderLabelJob extends Jobs {
    public function handle(): void {
        $files = File::allFiles(public_path($this-&amp;gt;batch()-&amp;gt;id));

        usort($files, function ($a, $b) {
            return $a-&amp;gt;getFilename() &amp;lt;=&amp;gt; $b-&amp;gt;getFilename();
        });

        $merger = PDFMergerFacade::init();
        foreach ($files as $file) {
            $merger-&amp;gt;addPDF($file-&amp;gt;getPathname());
        }

        $merger-&amp;gt;merge();
        $mergedFile = public_path($this-&amp;gt;batch()-&amp;gt;id . '/merged.pdf');
        $merger-&amp;gt;save($mergedFile);

        // Cleanup temporary files
        foreach ($files as $file) {
            File::delete($file-&amp;gt;getPathname());
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Create the View
&lt;/h2&gt;

&lt;p&gt;Create a multi_label.blade.php file under the resources/views directory for the label template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;style&amp;gt;
        body { font-family: Arial, sans-serif; font-size: 15px; }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
@foreach ($orders as $order)
    &amp;lt;div style="page-break-before: always;"&amp;gt;
        &amp;lt;h2&amp;gt;Order #{{ $order-&amp;gt;order_number }}&amp;lt;/h2&amp;gt;
        &amp;lt;p&amp;gt;Order Date: {{ $order-&amp;gt;created_at }}&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;Total Price: ${{ $order-&amp;gt;total_price }}&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;Status: {{ $order-&amp;gt;status }}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
@endforeach
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Set Up the Controller and Route
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:controller PrintOrderLabelsController
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use App\Jobs\{MultiOrdersLabelPdfExport, MergePDFFilesMultiOrderLabelJob};
use Illuminate\Support\Facades\Bus;

class PrintOrderLabelsController extends Controller {
    public function printMultiLabels(Request $request) {
        $orderIds = $request-&amp;gt;order_ids;
        $chunks = array_chunk($orderIds, 50);
        $jobs = [];

        foreach ($chunks as $chunk) {
            $orders = Order::whereIn('id', $chunk)-&amp;gt;get();
            $jobs[] = new MultiOrdersLabelPdfExport($orders);
        }

        $batch = Bus::batch([...$jobs, new MergePDFFilesMultiOrderLabelJob()])-&amp;gt;dispatch();

        return response()-&amp;gt;json(['batch_id' =&amp;gt; $batch-&amp;gt;id]);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;at function printMultiLabels() &lt;br&gt;
we send order_ids at request as array to to divided big array to multi array using array_chunk and create with each array job with data of orders &lt;br&gt;
to build the PDF file with that data and create batch with that jobs that created and finally execute the job that merge all files created at one file &lt;/p&gt;

&lt;p&gt;Add the route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Route::post('/print/multi-label', [PrintOrderLabelsController::class, 'printMultiLabels']);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 8: Test the Endpoint
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use Postman to send a POST request to the /print/multi-label endpoint with a JSON body containing the order_ids array.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fde0il1qugoakulpu149o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fde0il1qugoakulpu149o.png" alt=" " width="800" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: Start the Queue Worker
&lt;/h2&gt;

&lt;p&gt;Run the queue worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan queue:work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method ensures your application processes PDF generation and merging in manageable chunks, avoiding timeouts and ensuring scalability, you can add feature when the batch is finished send email or notification with merged file link .  Let me know if you need further clarification! 🚀&lt;/p&gt;

</description>
      <category>pdf</category>
      <category>laravel</category>
      <category>labels</category>
    </item>
  </channel>
</rss>
