Almost every Flutter app needs a backend. Whether you are building an e-commerce platform, a delivery app, or a business management system for a client in Uganda, your mobile app will need to fetch data, authenticate users, and process transactions through a server. Laravel is my first choice for this backend — it is fast to build with, has excellent built-in authentication, and deploys cleanly to any Linux server.
This guide covers building a real, production-ready REST API in Laravel that you can connect to a Flutter frontend.
Project Setup
composer create-project laravel/laravel my-api
cd my-api
php artisan key:generate
Configure your database in .env:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_api_db
DB_USERNAME=root
DB_PASSWORD=secret
Authentication with Laravel Sanctum
Sanctum is the right choice for mobile app authentication in Laravel. It issues plain tokens — simpler and more reliable than Passport's OAuth2 for mobile clients.
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Update app/Models/User.php to use the HasApiTokens trait:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
// ...
}
Add Sanctum middleware to your api routes in bootstrap/app.php:
->withMiddleware(function (Middleware $middleware) {
$middleware->api(append: [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
]);
})
Auth Controller
Create app/Http/Controllers/Api/AuthController.php:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
class AuthController extends Controller
{
public function register(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
$user = User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
]);
$token = $user->createToken('mobile-app')->plainTextToken;
return response()->json([
'user' => $user,
'token' => $token,
], 201);
}
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
$token = $user->createToken('mobile-app')->plainTextToken;
return response()->json(['user' => $user, 'token' => $token]);
}
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();
return response()->json(['message' => 'Logged out successfully']);
}
public function me(Request $request)
{
return response()->json($request->user());
}
}
API Resources for Clean Responses
Never return raw Eloquent models from your API. Use API Resources to control exactly what data Flutter receives:
php artisan make:resource UserResource
Edit app/Http/Resources/UserResource.php:
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'avatar' => $this->avatar_url,
'created_at' => $this->created_at->toIso8601String(),
];
}
Consistent Error Handling
Flutter needs predictable JSON responses, even for errors. Add this to bootstrap/app.php:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->render(function (\Illuminate\Validation\ValidationException $e, $request) {
if ($request->expectsJson()) {
return response()->json([
'status' => 'error',
'message' => 'Validation failed',
'errors' => $e->errors(),
], 422);
}
});
$exceptions->render(function (\Illuminate\Auth\AuthenticationException $e, $request) {
if ($request->expectsJson()) {
return response()->json([
'status' => 'error',
'message' => 'Unauthenticated.',
], 401);
}
});
})
Pagination That Works Well with Flutter
Always use cursor pagination for mobile apps — it performs better on large datasets and avoids count queries:
public function index()
{
$items = Item::latest()
->cursorPaginate(20);
return ItemResource::collection($items);
}
Flutter reads the links.next field to load the next page.
API Routes
In routes/api.php:
use App\Http\Controllers\Api\AuthController;
// Public routes
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
// Protected routes
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
Route::get('/me', [AuthController::class, 'me']);
// Add your app's resource routes here
Route::apiResource('items', ItemController::class);
});
Deploying to a VPS in Uganda
For Laravel APIs I typically deploy to DigitalOcean or a Ugandan VPS (Camellia Networks and IS.UG both offer good hosting). Basic deployment checklist:
# On the server
git clone https://github.com/yourusername/my-api.git
cd my-api
composer install --no-dev --optimize-autoloader
cp .env.example .env
php artisan key:generate
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan optimize
Set up Nginx to point to /public and configure SSL with Certbot (free). Your Flutter app then makes requests to https://api.yourdomain.com.
Connecting Flutter to the API
In Flutter, store the token after login and attach it to every request:
// After login
final prefs = await SharedPreferences.getInstance();
await prefs.setString('auth_token', response['token']);
// On every API call
final token = prefs.getString('auth_token');
final response = await http.get(
Uri.parse('https://api.yourdomain.com/api/me'),
headers: {
'Authorization': 'Bearer $token',
'Accept': 'application/json',
},
);
Summary
Laravel + Sanctum gives you a secure, fast, and maintainable API backend for any Flutter app. The combination powers most of the mobile-first products I build for clients in Uganda. The key principles are: use Resources for every response, handle errors consistently, use cursor pagination, and deploy with optimize commands for speed.
Building a mobile app and need a Laravel backend? Contact me — I build and maintain Laravel APIs for Flutter apps for clients across Uganda and East Africa.