DEV Community

Carlos Viana
Carlos Viana

Posted on • Edited on

1 1 1 1 1

Creating a REST API with Laravel

In this tutorial, we will build a complete RESTful API in Laravel to manage tasks, taking advantage of the power of Laravel, a PHP framework developed with developer productivity in mind. Laravel is written and maintained by Taylor Otwell, and it is a highly opinionated framework designed to save developers' time by favoring convention over configuration.

Before we begin, let's understand what a RESTful API is. REST, which stands for REpresentational State Transfer, is an architectural style for network communication between applications, relying on a stateless protocol (usually HTTP) for interaction.

In RESTful APIs, we use HTTP verbs as actions, and the endpoints represent the resources being acted upon. Each HTTP verb has a specific semantic meaning:

  • GET: retrieve resources
  • POST: create resources
  • PUT: update resources
  • DELETE: delete resources

Now, let's get started with setting up the project and creating automated tests that will be part of our task management API in Laravel.

Step 1: With PHP and Composer installed, let's start our task API project.

Run the following command to create a new Laravel project:

composer create-project laravel/laravel task-api
Enter fullscreen mode Exit fullscreen mode

After creating the project, navigate to the folder and run the following commands in the terminal:

cd task-api  
code .
Enter fullscreen mode Exit fullscreen mode

Alternatively, you can open VS Code and then select the project.

Now let's configure the environment variables so our project can access the database!

In the .env file, set your database configurations:

DB_DATABASE=task_api  
DB_USERNAME=your_username  
DB_PASSWORD=your_password  
Enter fullscreen mode Exit fullscreen mode

After configuring the database, let's create our first migration, which is a way to version the database. Laravel uses migrations, just like many other frameworks in the market.

To create the tasks table, run:

php artisan make:migration create_tasks_table --create=tasks
Enter fullscreen mode Exit fullscreen mode

In the migration file (database/migrations/xxxx_xx_xx_create_tasks_table.php), define the table structure:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('description')->nullable();
            $table->boolean('completed')->default(false);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('tasks');
    }
};
Enter fullscreen mode Exit fullscreen mode

Run the migration to create the table:

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Step 2: Laravel is a framework that works well with the MVC model. Following this pattern, let's create the model and controller for our tasks.

Run the following commands:

php artisan make:model Task  
php artisan make:controller TaskController --api  
Enter fullscreen mode Exit fullscreen mode

After creating the model (our class responsible for database access) for the Task, we need to add some values to the fillable property. This allows us to populate these fields when creating or editing a task (app/Models/Task.php):

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    use HasFactory;

    protected $fillable = ['title', 'description', 'completed'];
}
Enter fullscreen mode Exit fullscreen mode

Step 3: After creating the model and controller (which we will explore soon), we need to define the routes for our tasks resource. Laravel makes this process simple and efficient.

In the routes/api.php file, add the routes for the TaskController:

<?php

use App\Http\Controllers\TaskController;
use Illuminate\Support\Facades\Route;

Route::apiResource('tasks', TaskController::class);
Enter fullscreen mode Exit fullscreen mode

Step 4: Now, let's discuss the controller. After creating the model and configuring the properties that can be inserted and updated in the database, we’ll move on to the implementation of the TaskController. This controller is responsible for handling all user requests based on the defined routes.

In the TaskController, we’ll implement the basic CRUD methods:

<?php

namespace App\Http\Controllers;

use App\Models\Task;
use Illuminate\Http\Request;

class TaskController extends Controller
{
    public function index()
    {
        $tasks = Task::all();
        return response()->json($tasks, 200);
    }

    public function store(Request $request)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string'
        ]);
        $task = Task::create($request->all());
        return response()->json($task, 201);
    }

    public function show(Task $task)
    {
        return response()->json($task, 200);
    }

    public function update(Request $request, Task $task)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string',
            'completed' => 'boolean'
        ]);
        $task->update($request->all());
        return response()->json($task, 201);
    }

    public function destroy(Task $task)
    {
        $task->delete();
        return response()->json(null, 204);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: After creating the complete MVC for our task resource, let's test the API endpoints. You can do this using VS Code or tools like Insomnia or Postman.

Now, we’ll test each endpoint manually using a VS Code extension called REST Client (https://marketplace.visualstudio.com/items?itemName=humao.rest-client).

After installing the extension, create an .http file in your project folder with the following content:

### Create New Task
POST http://127.0.0.1:8000/api/tasks HTTP/1.1
content-type: application/json
Accept: application/json

{
    "title": "Study Laravel"
}

### Show Tasks
GET http://127.0.0.1:8000/api/tasks HTTP/1.1
content-type: application/json
Accept: application/json

### Show Task
GET http://127.0.0.1:8000/api/tasks/1 HTTP/1.1
content-type: application/json
Accept: application/json

### Update Task
PUT http://127.0.0.1:8000/api/tasks/1 HTTP/1.1
content-type: application/json
Accept: application/json

{
    "title": "Study Laravel and Docker",
    "description": "We are studying!",
    "completed": false
}

### Delete Task
DELETE http://127.0.0.1:8000/api/tasks/1 HTTP/1.1
content-type: application/json
Accept: application/json
Enter fullscreen mode Exit fullscreen mode

This file lets you send requests directly from VS Code using the REST Client extension, making it easy to test each route in your API.

Step 6: After creating everything, the only way to ensure that our API is functioning correctly is through tests. For this example, we will test the API endpoints to guarantee that, but first, let's create something to help us populate the database for testing.

First, create a factory for the Task model:

php artisan make:factory TaskFactory
Enter fullscreen mode Exit fullscreen mode

Then, define the factory like this:

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

class TaskFactory extends Factory
{
    public function definition(): array
    {
        return [
            'title' => fake()->sentence(),
            'description' => fake()->paragraph(),
            'completed' => false,
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Here’s my PHPUnit configuration:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
    bootstrap="vendor/autoload.php"
    colors="true"
>
    <testsuites>
        <testsuite name="Unit">
            <directory>tests/Unit</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory>tests/Feature</directory>
        </testsuite>
    </testsuites>
    <source>
        <include>
            <directory>app</directory>
        </include>
    </source>
    <php>
        <env name="APP_ENV" value="testing" />
        <env name="BCRYPT_ROUNDS" value="4" />
        <env name="CACHE_DRIVER" value="array" />
        <env name="DB_CONNECTION" value="sqlite" />
        <env name="DB_DATABASE" value=":memory:" />
        <env name="MAIL_MAILER" value="array" />
        <env name="PULSE_ENABLED" value="false" />
        <env name="QUEUE_CONNECTION" value="sync" />
        <env name="SESSION_DRIVER" value="array" />
        <env name="TELESCOPE_ENABLED" value="false" />
    </php>
</phpunit>
Enter fullscreen mode Exit fullscreen mode

Now, let's test our API! You can create the test with the following command, Laravel makes it very easy to develop and test our applications:

php artisan make:test TaskApiTest
Enter fullscreen mode Exit fullscreen mode

In the tests/Feature/TaskApiTest.php file, implement the tests:

<?php

namespace Tests\Feature;

use App\Models\Task;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class TaskApiTest extends TestCase
{
    use RefreshDatabase;

    public function test_can_create_task(): void
    {
        $response = $this->postJson('/api/tasks', [
            'title' => 'New Task',
            'description' => 'Task Description',
            'completed' => false,
        ]);

        $response->assertStatus(201);

        $response->assertJson([
            'title' => 'New Task',
            'description' => 'Task Description',
            'completed' => false,
        ]);
    }

    public function test_can_list_tasks()
    {
        Task::factory()->count(3)->create();

        $response = $this->getJson('/api/tasks');

        $response->assertStatus(200);

        $response->assertJsonCount(3);
    }

    public function test_can_show_task()
    {
        $task = Task::factory()->create();

        $response = $this->getJson("/api/tasks/{$task->id}");

        $response->assertStatus(200);

        $response->assertJson([
            'title' => $task->title,
            'description' => $task->description,
            'completed' => false,
        ]);
    }

    public function test_can_update_task()
    {
        $task = Task::factory()->create();

        $response = $this->putJson("/api/tasks/{$task->id}", [
            'title' => 'Update Task',
            'description' => 'Update Description',
            'completed' => true,
        ]);

        $response->assertStatus(201);

        $response->assertJson([
            'title' => 'Update Task',
            'description' => 'Update Description',
            'completed' => true,
        ]);
    }

    public function test_can_delete_task()
    {
        $task = Task::factory()->create();

        $response = $this->deleteJson("/api/tasks/{$task->id}");

        $response->assertStatus(204);

        $this->assertDatabaseMissing('tasks', ['id' => $task->id]);
    }
}
Enter fullscreen mode Exit fullscreen mode

To run the tests, just type the following in the terminal:

php artisan test
Enter fullscreen mode Exit fullscreen mode

I hope you liked it! Read through it carefully, and if needed, read it again, coding part by part to understand everything. If you're a beginner, you might not fully grasp everything at first, but if you put in the effort and go through the tutorial slowly, I'm sure you'll learn a lot.

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay