Back to blog

P8 | Building an API for Your Mobile App with Laravel & Craftable PRO

Oct 18, 2024
.
Samuel Trstenský

Hi, I’m Samuel from the Craftable PRO team, and in this part of our tutorial, we’ll tackle the most intricate section so far: preparing the API. While it may seem daunting, rest assured that the upcoming third version of Craftable PRO will include an API generator to simplify most of the steps we’ll cover today.

For additional insights, you can also refer to a series of articles by my colleague Kristina on our Craftable PRO blog. These articles detail creating APIs for mobile apps using Laravel.

Let’s dive in—we have a lot to cover!

Setting Up API Authentication

To authenticate mobile app users, we’ll utilize Laravel Sanctum and its API token authentication. Sanctum is already integrated into the Craftable PRO package dependencies, making it a natural choice. Here’s how to get started

1. Install Sanctum API: For Laravel 11, run:

sail artisan install:api

For older versions, refer to Kristina’s article.

2. Add HasApiTokens Trait: In the User model, include the HasApiTokens trait:

use HasApiTokens;

3. Configure the Guard: Update config/auth.php to add a guard for mobile app users

'guards' => [
    ...,
    'mobile-app' => [
        'driver' => 'sanctum',
        'provider' => 'users',
    ],
]

4. Generate an API Token: Create a token for testing:

sail artisan tinker App\Models\User::first()->createToken('test')->plainTextToken;

For detailed authentication setup, including login and registration routes, check Kristina’s article.

Creating API Endpoints

Now, let’s create API endpoints for the Training and Exercise models. To format the JSON responses for these endpoints, we’ll use resources and resource collections.

Generate Resources and Collections

Run the following commands to generate resources:

sail artisan make:resource TrainingResource 
sail artisan make:resource ExerciseResource 
sail artisan make:resource TrainingCollection 
sail artisan make:resource ExerciseCollection

Define Resource Classes

Customize the toArray method in the resource classes to define the JSON structure:

ExerciseCollection:

<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class ExerciseCollection extends ResourceCollection
{
    public $collects = ExerciseResource::class;
}

ExerciseResource:

<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ExerciseResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array
     */
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'description' => $this->description,
            'duration' => $this->duration,
        ];
    }
}

TrainingCollection:

<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TrainingCollection extends ResourceCollection
{
    public $collects = TrainingResource::class;
}

TrainingResource:

<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TrainingResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array
     */
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'description' => $this->description,
            'exercises' => new ExerciseResource($this->exercises),
            'is_user_signed_up' => $this->is_user_signed_up,
        ];
    }
}

Add a Custom Attribute for User Sign-Up

In the Training model, create a custom attribute to show whether the user is signed up for the training:

protected function isUserSignedUp(): Attribute
    {
        return Attribute::make(
            get: fn() => $this->users()->where('id', Auth::id())->exists()
        );
    }

We cannot forget to add this attribute to appends array.

Building the Training Controller

Create a controller to handle the API endpoints for trainings:

Retrieve All Trainings:

<?php
namespace App\Http\Controllers;
use App\Http\Resources\EventCollection;
use App\Http\Resources\EventResource;
use App\Models\Event;
use Illuminate\Http\Request;
class TrainingController extends Controller
{
    public function get(): TrainingCollection
    {
        return new TrainingCollection(EventCategory::all());
    }
}

Sign Up for and Sign Out from a Training:

<?php
namespace App\Http\Controllers;
use App\Http\Resources\EventCollection;
use App\Http\Resources\EventResource;
use App\Models\Event;
use Illuminate\Http\Request;
class TrainingController extends Controller
{
    ...
    
    public function signup(Request $request, Training $training): JsonResponse
    {
        $user = Auth::guard('mobile-app')->user();
        
        if($training->users()->where('users.id', $user->id)->exists()) {
            return response()->json(['message' => 'Already signed up'], 400);
        }
        
        $training->users()->attach($user->id);
        
        return response()->json(new TrainingResource($training));
    }
    
    public function signout(Request $request, Event $event): JsonResponse
    {
        $user = Auth::guard('mobile-app')->user();

        $training->users()->detach($user->id);
        
        return response()->json(new TrainingResource($training));
    }
}

Creating the routes

We need to add the controller methods to the api routes in the routes/api.php file:

<?php

use App\Http\Controllers\TrainingController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:mobile-app');

Route::middleware('auth:mobile-app')->group(function () {
    Route::get('/trainings', [TrainingController::class, 'get']);
    Route::post('/trainings/{training}/signup', [TrainingController::class, 'signup']);
    Route::post('/trainings/{training}/signout', [TrainingController::class, 'signout']);
    Route::get('/user/trainings', [TrainingController::class, 'getUsersTrainings']);
});

Testing the API

Use Insomnia or a similar tool to:

  1. Retrieve all training sessions.
  2. Sign up for a training session.
  3. Sign out from a training session.

Ensure you include the Sanctum token as a Bearer token in your requests.

What’s Next?

Congratulations! You’ve successfully set up an API for your training app. As a reminder, Craftable PRO’s upcoming API generator will simplify these steps by automating resource and endpoint generation. However, you’ll still need to implement custom API endpoints like signup and signout manually.

In the next part, we’ll explore advanced permissions in Craftable PRO. See you then!