Laravel & Vue.js 3 Product Create Tutorial (API Based)

Laravel & Vue.js 3 Product Create Tutorial (API Based)

Building modern web applications requires a powerful backend and a reactive frontend. This Laravel & Vue.js 3 product create tutorial - API based explains how to build a real-world product creation system using Laravel REST API and Vue.js 3.

This tutorial follows a clean API-based architecture, making it ideal for scalable enterprise applications.


Laravel Backend

Create Product Migration Product Table

php artisan make:migration create_products_table

// database/migrations/xxxx_xx_xx_create_products_table.php Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('sku')->unique(); $table->decimal('price', 10, 2); $table->integer('stock')->default(0); $table->text('description')->nullable(); $table->string('image')->nullable(); $table->timestamps(); });
php artisan migrate

Create Product Model

php artisan make:model Product


// app/Models/Product.php

class Product extends Model
{
    protected $fillable = [
        'name',
        'sku',
        'price',
        'stock',
        'description',
        'image'
    ];
}

Create Product Controller

php artisan make:controller Api/ProductController


// app/Http/Controllers/Api/ProductController.php

use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class ProductController extends Controller
{
    public function store(Request $request)
    {
        $validated = $request->validate([
            'name'        => 'required|string|max:255',
            'sku'         => 'required|unique:products,sku',
            'price'       => 'required|numeric',
            'stock'       => 'required|integer',
            'description' => 'nullable|string',
            'image'       => 'nullable|image|mimes:jpg,png,jpeg|max:2048'
        ]);

        if ($request->hasFile('image')) {
            $validated['image'] = $request->file('image')
                ->store('products', 'public');
        }

        $product = Product::create($validated);

        return response()->json([
            'message' => 'Product created successfully',
            'data'    => $product
        ], 201);
    }
}

API Route

// routes/api.php

use App\Http\Controllers\Api\ProductController;

Route::post('/products', [ProductController::class, 'store']);

Storage Link (For Images)

php artisan storage:link

Vue.js 3 Frontend

Axios Setup

npm install axios

// src/services/api.js import axios from 'axios' export default axios.create({ baseURL: 'http://localhost:8000/api', headers: { 'Accept': 'application/json' } })

Product Create Component

<!-- src/views/ProductCreate.vue -->

<template>
  <div class="max-w-xl mx-auto p-6">
    <h2 class="text-xl font-bold mb-4">Create Product</h2>

    <form @submit.prevent="submit">
      <input v-model="form.name" placeholder="Product Name" class="input" />

      <input v-model="form.sku" placeholder="SKU" class="input" />

      <input type="number" v-model="form.price" placeholder="Price" class="input" />

      <input type="number" v-model="form.stock" placeholder="Stock" class="input" />

      <textarea v-model="form.description" placeholder="Description" class="input"></textarea>

      <input type="file" @change="handleFile" />

      <button class="btn">Save Product</button>
    </form>

    <p v-if="error" class="text-red-500 mt-2">{{ error }}</p>
  </div>
</template>

<script setup>
import { reactive, ref } from 'vue'
import api from '@/services/api'

const error = ref(null)

const form = reactive({
  name: '',
  sku: '',
  price: '',
  stock: '',
  description: '',
  image: null
})

const handleFile = (e) => {
  form.image = e.target.files[0]
}

const submit = async () => {
  error.value = null

  const data = new FormData()
  Object.keys(form).forEach(key => {
    data.append(key, form[key])
  })

  try {
    await api.post('/products', data, {
      headers: { 'Content-Type': 'multipart/form-data' }
    })

    alert('Product Created Successfully')
  } catch (err) {
    error.value = err.response?.data?.message || 'Something went wrong'
  }
}
</script>

Vue Router

// src/router/index.js

{
  path: '/products/create',
  component: () => import('@/views/ProductCreate.vue')
}