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.