In this article, I will guide you through building RESTful API in Laravel 10. In this guide, you will create a simple API that handles basic operations on tasks. You will create the database table from scratch, create a corresponding Model, add methods in the Controller and connect them to routes. For the testing purposes, I will also show you how to fill the database with test (fake) data, and how to test the REST API through Postman.

Table of Contents

Install Laravel 10

For start, you will need to have a fresh installation of Laravel 10. The requirement is to have a Composer installed on your machine.

If you need a guide on how to install Composer you can check this article.

Open the terminal and run the following command:

composer create-project laravel/laravel tasks_api

Configure the database

After the composer has finished creating the project's files and installing the dependencies, you will need to update the correct values to connect the database to the project. Create a _tasksdb database in your MySQL schemas.

Also in the .env file, you will need to update these variables.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=tasks_db
DB_USERNAME= your database username
DB_PASSWORD= your database password

Create Table and Model

To create the tasks table, do it through Laravel migrations. Create a model Task with a migration, with the following command:

php artisan make:model Task --migration

Next, you will need to add the fields in the table.

Open the migration file in database/migrations and update the file with the following code:

Schema::create('tasks', function (Blueprint $table) {
     $table->id();
     $table->string('name');
     $table->enum('priority', ['low', 'medium', 'high']);
     $table->text('description');
     $table->boolean('completed');
     $table->timestamps();
});

After updating the file, run the migration using the following command:

php artisan migrate

Create Controller and Routes

Before creating the TaskController, let's define the API routes. For this simple API, there will be 6 routes, each of them defined and explained in the following table:

Verb URI Action Description
GET /tasks get Getting all the tasks
GET /tasks/{task} index Get a single task
POST /tasks add Create a new task
PUT /tasks/{task} update Update a task
DELETE /tasks/{task} remove Remove a task
PUT /tasks/{task}/complete complete Complete a task

Next, proceed to create the TaskController and run the following command in the terminal:

php artisan make:controller TaskController

In the TaskController.php file, add the actions that will be connected with the routes. So the file should look something like this:

<?php

namespace App\Http\Controllers;

use App\Models\Task;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;

class TaskController extends Controller
{
    /**
     * Get a task
     *
     * @param Request $request
     * @param Task $task
     * @return JsonResponse
     */
    public function get(Request $request, Task $task): JsonResponse
    {
        return response()->json($task);
    }

    /**
     * Get all tasks
     *
     * @param Request $request
     * @return JsonResponse
     */
    public function index(Request $request): JsonResponse
    {
        return response()->json(Task::all());
    }

    /**
     * Add task
     *
     * @param Request $request
     * @return JsonResponse
     * @throws \Throwable
     */
    public function add(Request $request)
    {
        $validator = Validator::make($request->post(), [
            'name' => 'required|min:3|max:120',
            'priority' => 'required|in:low,medium,high',
            'description' => 'min:3|max:500',
        ]);

        if ($validator->fails()) {
            return response()->json($validator->errors(), 400);
        }

        try {
            $task = new Task();
            $task->name = $request->get('name');
            $task->priority = $request->get('priority');
            $task->completed = false;
            $task->description = $request->get('description');

            if ($task->saveOrFail()) {
                return response()->json($task);
            }

        } catch (Exception $e) {
            Log::debug('error creating task');
        }

        return response()->json('error_creating', 400);
    }

    /**
     * Update the task
     *
     * @param Request $request
     * @param Task $task
     * @return JsonResponse
     * @throws \Throwable
     */
    public function update(Request $request, Task $task): JsonResponse
    {
        $validator = Validator::make($request->post(), [
            'name' => 'min:3|max:120',
            'priority' => 'in:low,medium,high',
            'description' => 'min:3|max:500',
        ]);

        if ($validator->fails()) {
            return response()->json($validator->errors(), 400);
        }

        try {
            if($request->has('name')){
                $task->name = $request->get('name');
            }
            if($request->has('priority')){
                $task->priority = $request->get('priority');
            }
            if($request->has('description')){
                $task->description = $request->get('description');
            }

            if ($task->updateOrFail()) {
                return response()->json($task);
            }

        } catch (Exception $e) {
            Log::debug('error updating task', ['id' => $task->id]);
        }

        return response()->json('error_updating', 400);
    }

    /**
     * Remove task
     *
     * @param Request $request
     * @param Task $task
     * @return JsonResponse
     */
    public function remove(Request $request, Task $task): JsonResponse
    {
        if ($task->delete()) {
            return response()->json(null, 204);
        }

        return response()->json('error_removing', 400);
    }

    /**
     * Complete the task
     *
     * @param Request $request
     * @param Task $task
     * @return JsonResponse
     * @throws \Throwable
     */
    public function complete(Request $request, Task $task): JsonResponse
    {
        try {
            $task->completed = true;
            if ($task->saveOrFail()) {
                return response()->json(null, 204);
            }
        } catch (Exception $e) {
            Log::debug('error completing task.', ['id' => $task->id]);
        }

        return response()->json('error_completing', 400);
    }
}

Next, you need to create the routes and connect them with the TaskController.

Open the routes/api.php file and update with the following code:

Route::get('/tasks', [TaskController::class, 'index']);
Route::post('/tasks', [TaskController::class, 'add']);
Route::get('/tasks/{task}', [TaskController::class, 'get']);
Route::put('/tasks/{task}', [TaskController::class, 'update']);
Route::delete('/tasks/{task}', [TaskController::class, 'remove']);
Route::put('/tasks/{task}/complete', [TaskController::class, 'complete']);

Run application

Now, you can run the application using this command in terminal:

php artisan serve

Create Factories

Currently, your database is empty and filling it up by hand it will require a lot of time. For that purpose, we will use Factories. Create a TaskFactory using the following command:

php artisan make:factory TaskFactory

Open the TaskFactory.php in /database/factories and update with the following code:

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Task>
 */
class TaskFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition()
    {
        $priorityKeys = ['low', 'medium', 'high'];
        $randKey = array_rand($priorityKeys);

        return [
            'name' => fake()->sentence(),
            'priority' => $priorityKeys[$randKey],
            'description' => fake()->text(),
            'completed' => fake()->boolean,
            'created_at' => now(),
            'updated_at' => now()->addHours(2)
        ];
    }
}

Next, with using the TaskFactory in the /database/seeders/DatabaseSeeder.php, create 15 tasks in the database.

<?php

namespace Database\Seeders;

use App\Models\Task;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        Task::factory(15)->create();
    }
}

To run the seeder and fill the database, run the following command:

php artisan db:seed

Now, you should have a filled database with 15 tasks.

Open the TaskFactory.php in /database/factories and update with the following code:

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Task>
 */
class TaskFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition()
    {
        $priorityKeys = ['low', 'medium', 'high'];
        $randKey = array_rand($priorityKeys);

        return [
            'name' => fake()->sentence(),
            'priority' => $priorityKeys[$randKey],
            'description' => fake()->text(),
            'completed' => fake()->boolean,
            'created_at' => now(),
            'updated_at' => now()->addHours(2)
        ];
    }
}

Next, with using the TaskFactory in the /database/seeders/DatabaseSeeder.php, create 15 tasks in the database.

<?php

namespace Database\Seeders;

use App\Models\Task;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        Task::factory(15)->create();
    }
}

To run the seeder and fill the database, run the following command:

php artisan db:seed

Now, you should have a filled database with 15 tasks.

Testing

In this part of the guide, you will test the routes of the application in Postman.

  • Get a Single Task Screenshot-2023-04-05-at-17.40.39-1024x371
  • Get All Tasks Screenshot-2023-04-05-at-17.40.21-1024x858
  • Create a Task Screenshot-2023-04-05-at-17.41.08-1024x489
  • Update a Task Screenshot-2023-04-05-at-17.41.41-1024x612
  • Delete a Task Screenshot-2023-04-05-at-17.41.54-1024x371
  • Complete a Task Screenshot-2023-04-05-at-17.42.29-1024x442

Conclusion

In this guide, we created a simple REST API that executes few operations on the tasks database. We created routes for creating, updating and deleting a task, also added a route to complete a task, and added the general methods for retrieving a task and retrieving all of the tasks. We also filled the database with some test (fake) data, that later we used for testing. For our testing purposes we used Postman, but you are free to use other platforms for testing APIs.

The complete code in this article is available at my Github.