loading...

How to Create a Secure CRUD RESTful API in Laravel 8 and 7 Using Laravel Passport

kingsconsult profile image Kingsconsult ・10 min read

Good day, today we are going to be coding strictly in the backend, we are going to convert our Laravel 8 CRUD app to RESTful APIs. API is a software intermediary that allows two applications to talk to each other. Somethings you might need to create an app that can be run on different languages or frameworks, for example, you can use Laravel to create the backend for your application while the frontend might run on any JavaScript frameworks. API allows two or more programs to communicate with each other.
There are different types of APIs, but today we are going to concentrate on RESTful APIs. REST stands for Representational State Transfer, while API stands for Application Programming Interface. You can read more about API from the internet or other programming material.

Click on my profile to follow me to get more updates.

Normally, when we build an application, we need to secure the application so that unauthorized access will be blocked. Laravel already makes it easy to perform authentication via traditional login forms, but what about APIs? API does not maintain a session state between requests, so there is a need to use tokens to authenticate the app and also authorize the usage of the app.
Laravel provided a package that makes authentication of API very easy by using Laravel Passport, though, there are other options, this is the official packages by the Laravel team, this package provides a full OAuth2 server implementation. It is easy to apply and can be achieved in minutes. Without saying much, let's dive into it.

STEP 1: install laravel 8

To install the latest laravel framework, which is laravel 8.0 as of the time of publishing this article, run the command below

composer create-project --prefer-dist laravel/laravel laravel_8_api_crud

This will automatically create a laravel 8 app and some couple of things have been set up, we don’t need to copy and rename the env.example file, Laravel 8 does that automatically for us
alt text
Another important thing about Laravel 8, you don’t need to generate APP_KEY, this new version will also generate it for us.
alt text
With that all set up, our app is ready.

Step 2: Database setup

Create an empty database, Open the .env file, and update your database configurations.
alt text
For more information on this step, you can visit my previous article, where I explained the steps very well, Laravel 8 CRUD.

Step 3: Install Laravel Passport

let us install Laravel Passport, Passport service provider registers its own database migration directory, this means that it creates the table that we will be needing for storing clients. The table will be used to store the token generated which will be used to identify a currently authenticated user. This token will then be attached to every request allowing each user access to protected routes.

composer require laravel/passport

install Laravel/passport
After installation, then we need to migrate, but before we run our migration command, we need to specify the default string length, else, we are going to run into errors. So go to app/Providers/AppServiceProvider.php and add this to the boot function

Schema::defaultstringLength(191);

also, add this to the top of the class

use Illuminate\Support\Facades\Schema;

alt text
Finally, we run our migration command

php artisan migrate

migrations

Step 4: Create the encryption keys

We need to create the encryption keys that are needed to generate our secure access tokens.

php artisan passport:install

This command will also create "personal access" and "password grant" clients which will be used to generate access tokens.
install passport

Step 5: Add the HasApiTokens trait to our user model

Go to App\Models\User.php and tell the User class to

use HasApiTokens,

Also add this to the top

use Laravel\Passport\HasApiTokens;

User Model

Step 6: Call the passport routes in AuthServiceProvider

Go to App/Providers/AuthServiceProvider.php and add

Passport::routes();

To the boot method, also add the path before the class at the top

use Laravel\Passport\Passport;

Uncomment the policy in the protected method of $policies
AuthServiceProvider

Step 7: Set the driver

This will be the final step in the setting up and configuration of Laravel\Passport, we going to change our api driver from the default token to passport.
Go to config\auth.php and locate the guards array. In the api key, change the driver from token to passport
auth config

Step 8: Create the Migration file for our CRUD api project

php artisan make:model Project -m

make migration
A migration file will be created in the database/migrations folder, and we need to create our schema, I added name (string), introduction (string), location (string), cost of the project (integer).

<?php

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

class CreateProjectsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('projects', function (Blueprint $table) {
            $table->id();
            $table->string('name', 255);
            $table->string('introduction', 500)->nullable();
            $table->string('location', 255)->nullable();
            $table->integer('cost')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('projects');
    }
}
Enter fullscreen mode Exit fullscreen mode

We need to update our Project Model so that it can be able to accept the fields. We need to add a protected $fillable method that will contain the fields that a user of the app can fill, this helps to prevent someone from hacking into the app through input fields.

<?php

namespace App\Models;

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

class Project extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'location',
        'introduction',
        'cost',
    ];


    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'cost' => 'int',
    ];
}
Enter fullscreen mode Exit fullscreen mode

Step 9: Migrate the new table

migration

Step 10: Create a Resource

When building an API, the response will always be in JSON format, so we need a transformation layer that sits between our Eloquent models and the JSON responses. This is what will serve the response to the application’s user in a JSON format. Laravel provides us with a resource class that will help in transforming our models and the model collections into JSON. So we are going to create that

php artisan make:resource ProjectResource

This will create a folder in the app directory called Resources and also a file ProjectResource.php inside the resources.
create Project Resource

Step 11: Create our Controllers

The Controller is responsible for the direction of the flow of data and an interface between the user and the database and views. In this case, we are not interacting with views now because we are dealing with API, so our response will be in JSON format. The standard for RESTful APIs is to send the response in JSON.
We are going to be creating two controllers, the first will be the Authentication Controller and the second is our Project Controller, we need the Authentication Controller in order to generate the token to use in Project Controller.

php artisan make:controller API/AuthController

This will create a folder called API in App/Http/Controllers. It will also create a new file called AuthController.php. Click on AuthController.php and update it with the following code.

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Hash;



class AuthController extends Controller
{
    public function register(Request $request)
    {
        $validatedData = $request->validate([
            'name' => 'required|max:55',
            'email' => 'email|required|unique:users',
            'password' => 'required|confirmed'
        ]);

        $validatedData['password'] = Hash::make($request->password);

        $user = User::create($validatedData);

        $accessToken = $user->createToken('authToken')->accessToken;

        return response(['user' => $user, 'access_token' => $accessToken], 201);
    }

    public function login(Request $request)
    {
        $loginData = $request->validate([
            'email' => 'email|required',
            'password' => 'required'
        ]);

        if (!auth()->attempt($loginData)) {
            return response(['message' => 'This User does not exist, check your details'], 400);
        }

        $accessToken = auth()->user()->createToken('authToken')->accessToken;

        return response(['user' => auth()->user(), 'access_token' => $accessToken]);
    }
}
Enter fullscreen mode Exit fullscreen mode

In our AuthController, we created two methods: register and logic methods
In the register method, we use the Laravel Validate method to make sure that the name, email, and password is provided, this will also make sure that the email has not been taken and is a valid email address, the password must be confirmed before the user will be added.

After the validation, we use hash to encrypt the password before creating the user, we can't store plain password, lastly, we grab the access token and return it with the user’s information.
In the login method, we also validate the data been pass, to make sure the email and password are submitted, if the data did not correspond to any user, it will return a message that the user does not exist, if it corresponds, then it returns the user and the access token.

Let us create our ProjectController in the API using --api switch.

php artisan make:controller API/ProjectController --api --model=Project

The --api switch will create our Controller without the create and edit methods, those methods will present HTML templates.
Go to App/Http/Controller/API, and click on ProjectController, copy the code below and update the methods.

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Models\Project;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Http\Resources\ProjectResource;

class ProjectController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $projects = Project::all();
        return response([ 'projects' => ProjectResource::collection($projects), 'message' => 'Retrieved successfully'], 200);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $data = $request->all();

        $validator = Validator::make($data, [
            'name' => 'required|max:255',
            'description' => 'required|max:255',
            'cost' => 'required'
        ]);

        if ($validator->fails()) {
            return response(['error' => $validator->errors(), 'Validation Error']);
        }

        $project = Project::create($data);

        return response(['project' => new ProjectResource($project), 'message' => 'Created successfully'], 201);
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Models\Project  $project
     * @return \Illuminate\Http\Response
     */
    public function show(Project $project)
    {
        return response(['project' => new ProjectResource($project), 'message' => 'Retrieved successfully'], 200);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Models\Project  $project
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Project $project)
    {
        $project->update($request->all());

        return response(['project' => new ProjectResource($project), 'message' => 'Update successfully'], 200);
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Models\Project  $project
     * @return \Illuminate\Http\Response
     */
    public function destroy(Project $project)
    {
        $project->delete();

        return response(['message' => 'Deleted']);
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. The index method will retrieve all the projects in the database with a success message (Retrieved successfully) and returns a status code of 200.
  2. The store method will validate and store a new project, just like the AuthController, and returns a status code of 201, also a message of "Created successfully".
  3. The show method will retrieve just one project that was passed through the implicit route model binding, and also returns an HTTP code of 200 if successful.
  4. The update method receives the HTTP request and the particular item that needs to be edited as a parameter. It updates the project and returns the appropriate response.
  5. The destroy method also receives a particular project through implicit route model binding and deletes it from the database. ### Step 12: Create our Routes Go to routes folder and click on api.php, and updates with the following code
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\API\AuthController;
use App\Http\Controllers\API\ProjectController;

/*
|--------------------------------------------------------------------------
| 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::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);

Route::apiResource('projects', ProjectController::class)->middleware('auth:api');
Enter fullscreen mode Exit fullscreen mode

We added a register route and login routes which are post routes and also an apiResource route for projects utilizing the auth:api middleware.

Step 13: Testing

We are going to be testing our API with Postman, due to complexity and time, I might not be able to explain everything on Postman, but observe the red squares on this page
postman
register a user
register route
trying to get a user that does not exist
User not found
Login a registered user
Login successful
In other to access the projects routes, we must authenticate the user, in other to achieve this, we must add the user's token.
Copy the access token generated for the user when the user login in, click on Authorization on postman and select the type, Bearer Token, and paste the token by the input field by the right.
access token
Retrieve all the projects, but no project was created
empty database
Create a project
post a project
Retrieve all the projects
All projects
Retrieve just 1 project
One project
Update a project
Update
Delete a project
Delete

Finally, we have come to the end of the article, if you follow the article religiously, you will not make a mistake, but in case you can't follow all through, this is the link to the repo

You can encourage me by clicking the heart button to show love, you are can also leave a comment, suggestion, etc. You are also free to contact me through any of my contact details.

click the link to view my profile and follow me

Visit my other posts

Discussion

pic
Editor guide