DevPush

How to build a REST API with PHP and Laravel

Introduction

This guide will show you how to build an API application using the popular PHP framework Laravel. Laravel comes packed with features that can help make it easy to build a fully functional API. This is a basic API that's for people who are new to building APIs as a way to show how APIs are built and how they work.

Bookmarks API

This API will allow you to create, edit, and retrieve website bookmarks, the details stored are similar to what you would save bookmarks in your web browser.

Required software

The following applications were used to produce this guide.

Equivalent applications can be used instead of the choices above, as long as you can write and run PHP code, test API endpoints, view MySQL databases, and run commands in a terminal.

Laravel project setup

To create a Laravel project in the terminal go to a directory where you want to place the project folder and run the following to generate it.

composer create-project laravel/laravel bookmarks

Start the project by running the php artisan serve command. This should use the URL http://127.0.0.1:8000.

php artisan serve

Go to http://127.0.0.1:8000 in the web browser to see the default Laravel welcome page as below.

Laravel version 11 has stripped out features that are used to make an API, use the following command to add them in manually.

php artisan install:api

You will be prompted with the following question, press the Enter key to run the extra migrations.

One new database migration has been published. Would you like to run all pending database migrations? (yes/no) [yes]

Database migration

Laravel allows you to create a script used to generate database tables in your MySQL database. Use the following command to create a migration file inside the Laravel project.

php artisan make:migration create_bookmarks_table

Add the following code for the migration file, this will be used to create the bookmarks database table.

<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('bookmarks', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('url');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('bookmarks');
    }
};

Now run the migrate command to run the new migration file to update the database.

php artisan migrate

Open your MySQL client application (this guide uses Bee Keeper Studio), connect to the bookmarks database, and view the tables.

Starting with Laravel version 11 the default database technology used is SQLite so connect to the SQLite file located in the database folder.

After selecting the database you will see the bookmarks table available in the SQL client where you can view its structure, at present the table will be empty as no data has been added to it yet.

Database seeder

To add test data to the bookmarks database table a seeder can be used to achieve this, an Eloquent Model will interact with the bookmarks DB table through the PHP code.

Use the following command to create a Bookmark Eloquent Model class.

php artisan make:model Bookmark

Go to the Laravel project and inside the app/Models directory open the file named Bookmark.php, then add the following code into it.

<?php

namespace App\Models;

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

class Bookmark extends Model
{
    use HasFactory;

    protected $table = 'bookmarks';
    protected $fillable = ['name', 'url'];
}

Go to the DatabaseSeeder.php file located in the database/seeders directory and replace the code with the following code below.

This seeder class will insert three rows inside of the bookmarks DB table.

<?php

namespace Database\Seeders;

use App\Models\Bookmark;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    protected const BOOKMARKS = [
        ['name' => 'Facebook', 'url' => 'https://www.facebook.com'],
        ['name' => 'Twitter', 'url' => 'https://twitter.com'],
        ['name' => 'TikTok', 'url' => 'https://www.tiktok.com']
    ];

    /**
     * Seed the application's database.
     */
    public function run(): void
    {
        foreach (self::BOOKMARKS as $bookmark) {
            Bookmark::create($bookmark);
        }
    }
}

Go to the terminal, change the current directory to the location of the Laravel project directory, and run the db:seed command to run the seeder.

php artisan db:seed

Re-check the bookmarks DB table inside the MySQL client and you should see the new rows have been added.

Creating the controller

Go to the terminal and then run the make:controller command. This command will create the BookController.php file with blank API methods inside the app/Http/Controllers directory. The added --api suffix will add blank class methods used for an API.

php artisan make:controller BookmarkController --api

Open the BookController.php file and it will look like the following.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class BookmarkController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     */
    public function show(string $id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, string $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(string $id)
    {
        //
    }
}

The Bookmark Model requires including at the top of the Controller as well. Add the following line under the use Illuminate\Http\Request reference.

use App\Models\Bookmark;

Getting a list of bookmarks

You will now start filling in those blank API methods inside the Bookmark controller.

The first endpoint will retrieve all the rows in the bookmarks DB table.

Inside the BookmarkController file add the following code inside of the index method.

/**
 * Display a listing of the resource.
 */
public function index()
{
    return Bookmark::get();
}

Now open the routes/web.php file and replace all the code with the following.

<?php

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

Route::group(['prefix' => 'bookmarks'], function () {
    Route::get('/', [BookmarkController::class, 'index']);
});

The code will work because the bookmarks route will link the URL path to the Controller class and method. The get method is called and the Bookmark model is used to retrieve all the bookmarks which will then be returned as the response.

So once a request is made to http://127.0.0.1:8000/api/bookmarks it will show the following.

Getting a bookmark

The next endpoint will be similar to the previous one except the difference is this endpoint will only return a single bookmark row from the database.

Go to the BookmarkController.php file and add the following to the show method.

/**
 * Display the specified resource.
 */
public function show(string $id)
{
    return Bookmark::find($id);
}

Go to the routes/web.php file and update the group routes to include the new route, this route will require an ID which will be included in the URL.

Route::group(['prefix' => 'bookmarks'], function () {
    Route::get('/', [BookmarkController::class, 'index']);
    Route::get('/{id}', [BookmarkController::class, 'show']);
});

Use the URL http://127.0.0.1:8000/api/bookmarks/1 to get the first bookmark. Change the number to match the ID of the bookmark you want, the IDs can be viewed in the database.

Creating a bookmark

The next endpoint will create a new bookmark, go to the app/Http/BookmarkController.php file to update it.

This endpoint will involve passing in data from the endpoint call, which will require validation.

Start by adding these lines to the following class namespaces to use those classes.

use Illuminate\Support\Facades\Validator;
use Illuminate\Http\JsonResponse;

At the bottom of the Controller add this new method, this method will take in parameters sent through the endpoint and validate them so they comply with the following rules.

  • name - It's a required value and has a maximum character limit of 100
  • url - It's a required value, is of type URL, and has a maximum character limit of 200
/**
 * Verify that request params are valid values.
 */
protected function validationCheck(array $params): JsonResponse|true
{
    $validator = Validator::make($params, [
        'name' => 'required|max:100',
        'url' => 'required|url:http,https|max:200'
    ]);

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

Now fill in the body for the store method inside the BookmarkController which uses the validationCheck method, this will return the validation errors if validation fails.

If validation passes the new bookmark will be created and sent as the response.

/**
 * Store a newly created resource in storage.
 */
public function store(Request $request)
{
    $result = $this->validationCheck($request->all());

    if ($result instanceof JsonResponse) {
        return $result;
    }

    $params = $request->all();
    return Bookmark::create($params);
}

Include the new route except this time the request type is a POST instead of a GET.

Route::group(['prefix' => 'bookmarks'], function ($router) {
    Route::get('/', [BookmarkController::class, 'index']);
    Route::get('/{id}', [BookmarkController::class, 'show']);
    Route::post('/', [BookmarkController::class, 'store']);
});

When calling the API endpoint the data has to be sent along with the request. Use the following as the body as part of the request.

{
	"name": "YouTube",
  "url": "https://www.youtube.com"
}

Call the request and you will see the data for the new bookmark returned as the response.

Go into the MySQL client which will show the new row added to the bookmarks table.

Editing a bookmark

This endpoint will allow you to edit an existing bookmark by changing the name and URL.

Go to the app/Http/BookmarkController.php and fill in the update method, this method will use the validationCheck method to validate the name and URL parameters.

/**
 * Update the specified resource in storage.
 */
public function update(Request $request, string $id)
{
    $result = $this->validationCheck($request->all());

    if ($result instanceof JsonResponse) {
        return $result;
    }

    $bookmark = Bookmark::find($id);
    $bookmark->name = $request->name;
    $bookmark->url = $request->url;
    $bookmark->save();

    return $bookmark;
}

Add the new route, this will be a PUT request type and the ID of the bookmark will be a required URL parameter.

Route::group(['prefix' => 'bookmarks'], function ($router) {
    Route::get('/', [BookmarkController::class, 'index']);
    Route::get('/{id}', [BookmarkController::class, 'show']);
    Route::post('/', [BookmarkController::class, 'store']);
    Route::put('/{id}', [BookmarkController::class, 'update']);
});

Test the new endpoint using this body data.

{
	"name": "X",
  "url": "https://x.com"
}

After calling the endpoint the data entry will be updated with the new name and URL details.

Deleting a bookmark

The last endpoint will delete the bookmark based on the ID passed in as the URL parameter.

Fill in the destroy method in the BookmarkController.php file, the response won't respond with data since the bookmark won't exist.

/**
 * Remove the specified resource from storage.
 */
public function destroy(string $id)
{
    $bookmark = Bookmark::find($id);
    $bookmark?->delete();
    
    return response()->noContent();
}

Add the delete route into the routes/web.php.

Route::group(['prefix' => 'bookmarks'], function ($router) {
    Route::get('/', [BookmarkController::class, 'index']);
    Route::get('/{id}', [BookmarkController::class, 'show']);
    Route::post('/', [BookmarkController::class, 'store']);
    Route::put('/{id}', [BookmarkController::class, 'update']);
    Route::delete('/{id}', [BookmarkController::class, 'destroy']);
});

Run the endpoint in the API client and see the result. If the status code is 204 then the endpoint worked, you can check the MySQL client to see if the data has been deleted.

Final code

The complete final code for this project is available on the GitHub bookmarks page.

Click the Code button and then the Download ZIP link to get the code or use the HTTPS Git clone command in the terminal.

Conclusion

After reaching this point of the guide you will have become more familiar with building a REST API application using the Laravel PHP framework. Not only that but also testing API endpoints using an API client application such as Postman.

Building APIs is now a standard, as a full-stack or backend developer you are expected to have the knowledge and experience of working with APIs. The same way of building the API is not just for PHP but all other backend languages.