Web Development May 14, 2026

How to Build a Laravel REST API for a Flutter Mobile App

By Jjuuko Ronald

How to Build a Laravel REST API for a Flutter Mobile App

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:

AD
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:

AD
->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:

AD
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.

AD

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.

AD

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.

AD
AD

Let's Build Something Amazing

Have a project in mind? I'm available for freelance work and collaborations.

Start a conversation
History

Kclich

Ronnie's AI assistant