Laravel 12

Laravel 12 REST API CRUD operations with Image Upload, CORS & Clean URLs – Step-by-Step Tutorial

Laravel 12 REST API with MySQL, Image Upload, CORS & Clean URLs

In this step-by-step tutorial, we will build a Laravel 11 REST API with MySQL, including image upload, CORS support, and clean routing. Perfect for frontend apps built in Angular, React, Vue, or Flutter.

🛠 Step 1: Create Laravel Project

composer create-project laravel/laravel laravel-rest-api

⚙️ Step 2: Setup MySQL Database

Update your .env file with DB config:

DB_DATABASE=user_api
DB_USERNAME=root
DB_PASSWORD=yourpassword

📁 Step 3: Create Migration and Model

php artisan make:model User -m

Edit the generated migration:

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('first_name');
    $table->string('last_name');
    $table->string('email')->unique();
    $table->string('password');
    $table->string('image')->nullable();
    $table->timestamps();
});

Run the migration:

php artisan migrate

🌐 Step 4: Enable CORS

In app/Http/Middleware/HandleCors.php (or config/cors.php):

'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],

📦 Step 5: Create API Controller

php artisan make:controller Api/UserController

user.create – Store User with Image

public function store(Request $request)
{
    $request->validate([
        'first_name' => 'required',
        'last_name' => 'required',
        'email' => 'required|email|unique:users',
        'password' => 'required',
        'image' => 'nullable|image'
    ]);

    $imagePath = null;
    if ($request->hasFile('image')) {
        $imagePath = $request->file('image')->store('uploads', 'public');
    }

    $user = User::create([
        'first_name' => $request->first_name,
        'last_name' => $request->last_name,
        'email' => $request->email,
        'password' => Hash::make($request->password),
        'image' => $imagePath
    ]);

    return response()->json(['message' => 'User created successfully']);
}

user.index – Get All Users

public function index()
{
    return response()->json(User::all());
}

user.show – Get Single User

public function show($id)
{
    $user = User::find($id);
    return $user ? response()->json($user) : response()->json(['error' => 'User not found'], 404);
}

user.update – Update User

public function update(Request $request, $id)
{
    $user = User::find($id);
    if (!$user) return response()->json(['error' => 'User not found'], 404);

    $user->update($request->only('first_name', 'last_name', 'email'));

    if ($request->password) {
        $user->password = Hash::make($request->password);
        $user->save();
    }

    return response()->json(['message' => 'User updated']);
}

user.destroy – Delete User

public function destroy($id)
{
    $user = User::find($id);
    if (!$user) return response()->json(['error' => 'User not found'], 404);

    if ($user->image && Storage::disk('public')->exists($user->image)) {
        Storage::disk('public')->delete($user->image);
    }

    $user->delete();
    return response()->json(['message' => 'User deleted']);
}

🧭 Step 6: Define API Routes

In routes/api.php:


<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\ProfilesController;

// Get all profiles
Route::get('users', [ProfilesController::class, 'index']);

// Get a single profile
Route::get('users/{id}', [ProfilesController::class, 'show']);

// Create a profile
Route::post('users', [ProfilesController::class, 'store']);

// Update a profile
Route::put('users/{id}', [ProfilesController::class, 'update']);
Route::patch('users/{id}', [ProfilesController::class, 'update']); // optional

// Delete a profile
Route::delete('users/{id}', [ProfilesController::class, 'destroy']);

✅ Final Controller Code

<?php
namespace App\Http\Controllers\Api;

use App\Models\Profile;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    public function index()
    {
        return response()->json(Profile::all(), 200);
    }

    public function show($id)
    {
        $profile = Profile::find($id);
        if (!$profile) {
            return response()->json(['error' => 'Profile not found'], 404);
        }
        return response()->json($profile, 200);
    }

    public function store(Request $request)
    {
        $request->validate([
            'first_name' => 'required|string|max:100',
            'last_name'  => 'required|string|max:100',
            'email'      => 'required|email|unique:profiles,email',
            'password'   => 'required|min:6',
            'image'      => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
        ]);

        $imagePath = null;

        if ($request->hasFile('image')) {
            $file     = $request->file('image');
            $fileName = time() . '_' . $file->getClientOriginalName();
            $file->move(public_path('images/profiles'), $fileName);
            $imagePath = 'images/profiles/' . $fileName;
        }

        $profile = Profile::create([
            'first_name' => $request->first_name,
            'last_name'  => $request->last_name,
            'email'      => $request->email,
            'password'   => bcrypt($request->password),
            'image'      => $imagePath,
        ]);

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

    public function update(Request $request, $id)
    {
        $profile = Profile::find($id);
        if (!$profile) {
            return response()->json(['error' => 'Profile not found'], 404);
        }

        $request->validate([
            'first_name' => 'required|string|max:100',
            'last_name'  => 'required|string|max:100',
            'email'      => 'required|email|unique:profiles,email,' . $id,
            'password'   => 'nullable|min:6',
            'image'      => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
        ]);

        if ($request->hasFile('image')) {
            if ($profile->image && File::exists(public_path($profile->image))) {
                File::delete(public_path($profile->image));
            }

            $file     = $request->file('image');
            $fileName = time() . '_' . $file->getClientOriginalName();
            $file->move(public_path('images/profiles'), $fileName);
            $profile->image = 'images/profiles/' . $fileName;
        }

        $profile->first_name = $request->first_name;
        $profile->last_name  = $request->last_name;
        $profile->email      = $request->email;

        if ($request->filled('password')) {
            $profile->password = bcrypt($request->password);
        }

        $profile->save();

        return response()->json(['message' => 'Profile updated successfully', 'data' => $profile], 200);
    }

    public function destroy($id)
    {
        $profile = Profile::find($id);
        if (!$profile) {
            return response()->json(['error' => 'Profile not found'], 404);
        }

        if ($profile->image && File::exists(public_path($profile->image))) {
            File::delete(public_path($profile->image));
        }

        $profile->delete();

        return response()->json(['message' => 'Profile deleted successfully'], 200);
    }
}

 

✅ Step 7: Sample API Requests

  • GET /api/users – List all users
  • GET /api/users/{id} – Get user by ID
  • POST /api/users – Create new user (multipart form)
  • PUT /api/users/{id} – Update user
  • DELETE /api/users/{id} – Delete user

📦 Step 8: Use with Any Frontend

Now your Laravel REST API is ready to connect with any frontend: Angular, React, Vue, Flutter, or Postman.

🎉 Conclusion

You’ve just built a full-featured Laravel 11 REST API with CRUD operations, image upload, CORS, and clean API structure. This backend is ready to serve any modern frontend app.

Leave a Reply

Your email address will not be published. Required fields are marked *