Building and Consuming a RESTful API in Laravel PHP

June 25, 2019
Written by
Michael Okoh
Contributor
Opinions expressed by Twilio contributors are their own

Building and Consuming a RESTful API in Laravel.png

From your favorite social networks, down to your favorite banking applications, our modern world is driven by a lot of APIs. In this article, you will learn how to build a modern RESTful API and an application that will implement the API.

Prerequisites

  • PHP 7.1 or Higher
  • Composer
  • MySql
  • Laravel 5.6 or Higher
  • Postman

To follow along with this tutorial, you should have a basic understanding of the PHP language. Basic knowledge of the Laravel framework is required.

Understanding our Application

You will be building a CRUD API. CRUD means Create, Read, Update, and Delete. Our API will have the following endpoints:

GET /api/students will return all students and will be accepting GET requests.

GET /api/students/{id} will return a student record by referencing its id and will be accepting GET requests.

POST /api/students will create a new student record and will be accepting POST requests.

PUT /api/students/{id} will update an existing student record by referencing its id and will be accepting PUT requests.

DELETE /api/students/{id} will delete a student record by referencing its id and will be accepting DELETE requests.

The Student record will only contain name and course as details. When you are done developing these endpoints you will use the endpoints to develop an actual student records application that will make use of the API.

Setup the Laravel Application

To get started, you have to create a Laravel application. To do this you have to run the following command in your terminal:

$ laravel new api-project

Next, change your current directory to the root folder of the project:

$ cd api-project

Next, start up the Laravel server if it’s not already running:

$ php artisan serve

You will be able to visit your application on https://localhost:8000.

Laravel default landing

Next, create a new database for your application by running:

$ mysql -uroot -p

You will be prompted to type your MySQL password if you have any set when you authenticate with MySQL. Run the following to create a new database named api-project:

CREATE DATABASE `api-project`;

MySQL root in terminal

We can proceed to create a model along with a migration. To do this you have to run:

$ php artisan make:model Student -m

A new file named Student.php will be created in the app directory.

NOTE: You will have to edit the file to specify the database table we will like to interact with and the fields that can be written to:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Student extends Model
{
    protected $table = 'students';

    protected $fillable = ['name', 'course'];
}

Additionally, a migration file will be created in the database/migrations directory to generate our table. You will have to modify the migration file to create a column for name and course which will accept string values.

...
public function up()
{
    Schema::create('students', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('course');
        $table->timestamps();
    });
}
...

Next, you can open the project folder in your preferred text editor and modify the .env file to input your proper database credentials. This will allow the application to properly connect to the recently created database:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=<your-database-name>
DB_USERNAME=<your-database-username>
DB_PASSWORD=<your-database-password>

Next, you will run your migration using the following command:

$ php artisan migrate

Setup the Routes

Now that we have the basics of the application set up, we can proceed to create a controller that will contain the methods for our API by running:

$ php artisan make:controller ApiController

You will find a new file named ApiController.php in the app\http\controllers directory. Next, we can add the following methods:

...
class ApiController extends Controller
{
    public function getAllStudents() {
      // logic to get all students goes here
    }

    public function createStudent(Request $request) {
      // logic to create a student record goes here
    }

    public function getStudent($id) {
      // logic to get a student record goes here
    }

    public function updateStudent(Request $request, $id) {
      // logic to update a student record goes here
    }

    public function deleteStudent ($id) {
      // logic to delete a student record goes here
    }
}

Proceed to the routes directory and open the api.php file and create the endpoints that will reference the methods created earlier in the ApiController.

...
Route::get('students', 'ApiController@getAllStudents');
Route::get('students/{id}', 'ApiController@getStudent');
Route::post('students, 'ApiController@createStudent');
Route::put('students/{id}', 'ApiController@updateStudent');
Route::delete('students/{id}','ApiController@deleteStudent');

Note: All routes in api.php are prefixed with /api by default

Create a Student Record

Locate the createStudent method in our ApiController

public function createStudent(Request $request) {
  // logic to create a student record goes here
}

We will be using the Laravel request class to fetch the data passed to the endpoint. The endpoint will be expecting name of type string and course of type string as well. When we have successfully fetched the data we will store the data in our database.

...
use App\Student;

class ApiController extends Controller
{
  ...
  public function createStudent(Request $request) {
    $student = new Student;
    $student->name = $request->name;
    $student->course = $request->course;
    $student->save();

    return response()->json([
        "message" => "student record created"
    ], 201);
  }
  ...
}

The snippet above imports the Student model which will interact with our students table in the database. In the createStudent method, we instantiated a new Request object in the method parameter followed by a new Student object. Lastly, for every $student-><column-name> the equivalent request is fetched and saved.

If the operation is successful, a JSON response will be sent back to the API user with the message student record created and with response code 201.

This method is already tied to the api/students as we previously defined it in our routes file located at routes/api.php:

...
Route::post('students, 'ApiController@createStudent');
...

Testing

Before testing, make sure your application is running. You can use the inbuilt command as mentioned earlier:

$ php artisan serve

Or you can use Valet which is a nice tool for creating a proxy pass for all your PHP applications,  providing you with a *.test or *.dev domain for your applications to test locally.

To test this endpoint open Postman and make a POST request to http://localhost:8000/api/students or if you use Valet http://<folder-name>/api/students. Select the form-data option and pass the following values as seen in the image below:

Postman view of POST request

It works if it returns the success message along with the 201 response code, Now try adding a few more records to populate our database for the next task.

Return all Student Records

Now let us visit the getAllStudents method in our ApiController

public function getAllStudents() {
  // logic to get all students goes here
}

We will use the already imported Student model to make a simple eloquent query to return all students in the database.

class ApiController extends Controller
{
  public function getAllStudents() {
    $students = Student::get()->toJson(JSON_PRETTY_PRINT);
    return response($students, 200);
  }
  ...
}

The eloquent query ends with ->toJson(JSON_PRETTY_PRINT); which will serialize the object data return by eloquent into a nicely formatted JSON. The JSON is returned with the response code 200.

This method is already tied to the api/students route as we previously defined it in our routes file located at routes/api.php:

...
Route::get('students', 'ApiController@getAllStudents');
...

Testing

Assuming our application is running in the background, make a GET request to the /api/students endpoint in Postman.

Postman view of GET request

As seen in the image above, the endpoint returns all the student records in the database.

Return a student record

You will be creating an endpoint to return just a single student record. To begin you have to visit the getStudent method in the ApiController.

public function getStudent($id) {
  // logic to get a student record goes here
}

We will retrieve a student record by its id and to this, we will be making an eloquent query to return student records by their id.

...
class ApiController extends Controller
{
  ...
  public function getStudent($id) {
    if (Student::where('id', $id)->exists()) {
        $student = Student::where('id', $id)->get()->toJson(JSON_PRETTY_PRINT);
        return response($student, 200);
      } else {
        return response()->json([
          "message" => "Student not found"
        ], 404);
      }
  }
  ...
}

The snippet above first checks if a student record with the given id exists. If it does, it queries the database using eloquent to return the record with matching id in JSON with 200 as the response code. If the id given is not found in the database it will return a student not found message with a 404 response code.

This method is already tied to the api/students/{id} route as we previously defined it in our routes file located at routes/api.php:

…
Route::get('students/{id}', 'ApiController@getStudent');
...

Testing

Open Postman and make a GET request to the /api/students/{id} endpoint {id} can be the id of an existing record you may have in your database.

Postman view of GET request for a single record
As seen in the image above, I made a request to http://api-project.test/api/students/3 and the details of the student assigned to that id were returned. Next, let us try requesting a non-existent student record.

Postman view of GET request for a single deleted record

As seen in the image above, a request was made to the endpoint to return the details of the student record with the id of 100 which is non-existent. Our API did a good job by returning an error message along with the 404 status code.

Update a student record

We will now be creating an endpoint to update the details of an existing student record. To begin you have to visit the updateStudent method in the ApiController.

public function updateStudent(Request $request, $id) {
  // logic to update a student record goes here
}

To do this we will have to check if the record we are trying to update exists. If it does exist it will update the records which match the id specified and return status code 204. If it does not exist, it will return a message indicating that the record was not found along with status code 404.

public function updateStudent(Request $request, $id) {
    if (Student::where('id', $id)->exists()) {
        $student = Student::find($id);
        $student->name = is_null($request->name) ? $student->name : $request->name;
        $student->course = is_null($request->course) ? $student->course : $request->course;
        $student->save();

        return response()->json([
            "message" => "records updated successfully"
        ], 200);
        } else {
        return response()->json([
            "message" => "Student not found"
        ], 404);
        
    }
}

Validation was added just in case you need to only update a single attribute such asname or course. As the request comes in it checks if name or course is null. If it is null, it replaces the request with its existing value. If it isn’t, null it passed as the new value. All this was done using ternary operators.

NOTE: The format for the ternary operator is condition ? true : false

This method is already tied to the api/students/{id} route as we previously defined it in our routes file located at routes/api.php:

…
Route::put('students/{id}', 'ApiController@updateStudent');
...

Testing

To test this endpoint, return the details of the student record with the id of 1 by making a GET request to /api/students/1.

Postman view of GET request for a single record

The following records were returned:


    {
        "id": 1

Next, let us change the course to “Software Engineering” by making a PUT request to api/students/1. In order to make a PUT request, you have to pass a JSON payload via form-data. Now let us change the value of name to Trojan Okoh and the value of course to Software Engineering.

{
    "name": "Trojan Okoh",
    "course": "Software Engineering"
}

The snippet above is the JSON payload we will be using to update the records. Open Postman and change to raw and change type to JSON(application/json) as seen below.

Postman view for changing the form data type

Next, paste the JSON payload into the text area and send the PUT request to the endpoint.

Postman view of updated record

As seen in the image above, the endpoint returned a success message. Now let us make a GET request to /api/students/1 to confirm if the records were actually updated.

Postman view of GET request

Delete a Student Record

Finally, to delete a student record we will have to visit the deleteStudent method in our ApiController.

public function deleteStudent ($id) {
    // logic to delete a student record goes here
}

Using eloquent, we will check if the id of the record requested to be deleted exists. If it exists we will delete the record. If it does not exist, we will return a not found message along with the 404 status code.

...
class ApiController extends Controller
{
    ...
    public function deleteStudent ($id) {
      if(Student::where('id', $id)->exists()) {
        $student = Student::find($id);
        $student->delete();

        return response()->json([
          "message" => "records deleted"
        ], 202);
      } else {
        return response()->json([
          "message" => "Student not found"
        ], 404);
      }
    }
}

This method is already tied to the api/students/{id} route as we previously defined it in our routes file located at routes/api.php:

...
Route::delete('students/{id}', 'ApiController@deleteStudent');

Testing

To test this endpoint, we will have to list all the records we currently have in our database by making a GET request to the /api/students endpoint.

Postman view of GET request

Next, we will make a DELETE request to students/{id} where {id} is the id of the record we are requesting to be deleted. For the purpose of testing, I will delete the record with the id of 2.

Postman view of deleted record

The endpoint returned a success message along with status code 202 which means the request was accepted. To confirm if the record was actually deleted, let us try making a GET request to the /api/students endpoint to list all the student records we have in the database.

Postman view of GET request for updated records

As seen in the image above, the record with the id of 2 no longer exist. Also, we can check by trying to request the record with the id of 2 by making a GET request to the /api/students/{id} endpoint. It should return a 404 indicating that the record could not be found.

Postman view of GET request for missing record

Conclusion

Now that you have gotten to the end of this Article, let us confirm the contents of some important files.

app\http\controllers\ApiController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Student;

class ApiController extends Controller
{
    public function getAllStudents() {
      $students = Student::get()->toJson(JSON_PRETTY_PRINT);
      return response($students, 200);
    }

    public function createStudent(Request $request) {
      $student = new Student;
      $student->name = $request->name;
      $student->course = $request->course;
      $student->save();

      return response()->json([
        "message" => "student record created"
      ], 201);
    }

    public function getStudent($id) {
      if (Student::where('id', $id)->exists()) {
        $student = Student::where('id', $id)->get()->toJson(JSON_PRETTY_PRINT);
        return response($student, 200);
      } else {
        return response()->json([
          "message" => "Student not found"
        ], 404);
      }
    }

    public function updateStudent(Request $request, $id) {
      if (Student::where('id', $id)->exists()) {
        $student = Student::find($id);

        $student->name = is_null($request->name) ? $student->name : $request->name;
        $student->course = is_null($request->course) ? $student->course : $request->course;
        $student->save();

        return response()->json([
          "message" => "records updated successfully"
        ], 200);
      } else {
        return response()->json([
          "message" => "Student not found"
        ], 404);
      }
    }

    public function deleteStudent ($id) {
      if(Student::where('id', $id)->exists()) {
        $student = Student::find($id);
        $student->delete();

        return response()->json([
          "message" => "records deleted"
        ], 202);
      } else {
        return response()->json([
          "message" => "Student not found"
        ], 404);
      }
    }
}

routes\web.php

<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

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


Route::get('students', 'ApiController@getAllStudents');
Route::get('students/{id}', 'ApiController@getStudent');
Route::post('students, 'ApiController@createStudent');
Route::put('students/{id}', 'ApiController@updateStudent');
Route::delete('students/{id}', 'ApiController@deleteStudent');

app\Student.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Student extends Model
{
    protected $table = 'students';

    protected $fillable = ['name', 'course'];
}

We have been able to build a simple CRUD RESTful API using Laravel. This article covered the basics of the subject matter. I did not cover request validation and API security which would make a great next step for you to implement.

 

Twitter: @ichtrojan

GitHub: @ichtrojan

E-mail: michael@okoh.co.uk