Build REST API and JWT Authentication with Laravel 9
Tags: webdev, laravel, jwt, tutorial
Published: 2023-02-13 13:46:05

Welcome to this workshop on building REST APIs using Laravel! In this workshop, we'll explore the principles and practices of RESTful architecture, and learn how to use the popular PHP framework Laravel to build robust and scalable APIs. We'll cover the essential tools and techniques you need to get started, from routing and controllers to database design and authentication. By the end of this workshop, you'll have the skills and confidence to create your own REST APIs in Laravel, and be well on your way to building powerful and flexible web applications. So let's get started!

## Plan

#### Introduction to RESTful architecture and its principles.

#### Introduction to Laravel as a PHP framework and its benefits.

#### Provide an overview of the necessary tools and dependencies required to create a REST API in Laravel.

#### Set up a development environment by installing Laravel and creating a new project.

#### Explain the concept of routing and how it can be used to create RESTful endpoints.

#### Show how to create controllers to handle the business logic of the API in practice.


Talk about best practices for testing and debugging REST APIs in Laravel.

## Introduction to RESTful architecture and its principles.

**RESTful architecture** is a set of principles for designing web services that are scalable, flexible, and easy to maintain. REST, which stands for Representational State Transfer, is a style of software architecture that leverages the HTTP protocol to communicate between client and server.

The key principles of RESTful architecture include:

- **Resource identification through URLs:**
 RESTful APIs use URLs to identify resources that can be accessed or manipulated. Each resource should have a unique URL that can be used to retrieve or modify its state.

- **Use of HTTP methods:**
RESTful APIs use the HTTP protocol and its methods (such as GET, POST, PUT, and DELETE) to perform actions on resources. Each method has a specific meaning and can be used to retrieve, create, update, or delete a resource.

- **Statelessness:**
RESTful APIs are stateless, which means that each request from a client to a server contains all the information needed to complete the request. The server does not store any information about the client's previous requests.

- **Representation of resources:**
RESTful APIs use a variety of formats to represent resources, including JSON, XML, and plain text. The client and server can negotiate the format that is most appropriate for their needs.

By following these principles, RESTful APIs can provide a simple and effective way to build scalable and maintainable web services.

## Introduction to Laravel as a PHP framework and its benefits

Laravel is a free, open-source PHP web framework designed to help developers build robust, scalable, and maintainable web applications. It's a popular framework with a large and active community of developers, and it has several key benefits, including:

Some of the key benefits of using Laravel include:

- **Elegant syntax:** Laravel is known for its elegant and expressive syntax, which makes writing code a more enjoyable experience.

- **MVC architecture:** Laravel follows the Model-View-Controller (MVC) architecture pattern, which makes it easier to write maintainable and scalable code.

- **Artisan command line interface:** Laravel includes a powerful command line interface called Artisan, which allows developers to automate repetitive tasks and generate boilerplate code.

- **Blade templating engine:** Laravel's Blade templating engine provides a simple and intuitive way to build dynamic views.

- **Powerful ORM:** Laravel's built-in Object Relational Mapping (ORM) system, Eloquent, provides an easy and intuitive way to interact with databases.

- **Robust ecosystem:** Laravel has a large and active community of developers, and a wide range of third-party packages and libraries are available to extend its functionality.

These benefits make Laravel a popular choice for developers who want to build robust, maintainable, and scalable web applications.

## Provide an overview of the necessary tools and dependencies required to create a REST API in Laravel.

To create a REST API in Laravel, you will need several tools and dependencies. Here is an overview of the key ones:

- **PHP:** Laravel is a PHP web framework, so you will need to have PHP installed on your development machine. The minimum required version of PHP for Laravel 9 is PHP 8.0.

- **Composer:** Composer is a package manager for PHP that is used to manage Laravel's dependencies. You will need to have Composer installed on your machine to install Laravel and its dependencies.

- **Laravel:** You will need to install Laravel itself, either by downloading it directly or by using Composer to create a new Laravel project.

- **Database:** Laravel supports a wide range of databases, including MySQL, PostgreSQL, and SQLite. You will need to have a database server installed and configured to work with Laravel.

- **API testing tool:** To test your REST API, you will need an API testing tool such as [Postman](https://www.postman.com/downloads/) or [Insomnia](https://insomnia.rest/download).

- **Text editor or IDE:** You will need a text editor or integrated development environment (IDE) to write your code. Some popular options include Visual Studio Code, Sublime Text, and PHPStorm.

By having these tools and dependencies installed and configured, you will be able to create a robust and scalable REST API in Laravel.

## Set up a development environment by installing Laravel and creating a new project

In this workshop, we'll guide you through installing Laravel 9 on your local machine and starting your first Laravel project. Get ready to enhance your web development skills with Laravel 9.

Here are the steps to install Laravel 9 on your local machine:
1. Make sure you have PHP 8.0.2 and MySQL installed on your machine.
2. Install Composer if you haven't already. You can download the Composer installer from their [official website](https://getcomposer.org/download/).
3. Open your terminal or command prompt and run the following command to install Laravel 9 via Composer:

```bash
composer create-project --prefer-dist laravel/laravel:^9.0 rest-api-jwt
```
4. Once the installation is complete, navigate to the project directory using the following command:
```bash
cd rest-api-jwt
```
5. Start the development server by running the following command:
```bash
php artisan serve
```
6. Open your web browser and go to [http://localhost:8000](http://localhost:8000) to see the default Laravel welcome page.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r7moq7ovyrftis3jzx36.png)

Congratulations!

You have successfully installed Laravel 9 on your local machine and created your first Laravel project.

You now have a development environment set up and a new Laravel project created. You can start building your REST API by defining routes, controllers, and models.

## Explain the concept of routing and how it can be used to create RESTful endpoints.

In web development, routing refers to the process of **matching a URL to a specific action or resource in an application**. In Laravel, routing is the mechanism that maps a URL to a specific controller action.

To create RESTful endpoints in Laravel, you can define routes that correspond to different HTTP methods (GET, POST, PUT, DELETE, etc.) and map them to specific controller actions. This allows clients to interact with the application by making requests to specific URLs, and the application responds with the appropriate data or action.

For example, let's say you want to create a RESTful endpoint to get a list of articles. You can define a route in `routes/api.php` like this:

```php
use App\Http\Controllers\ArticleController;
 
Route::get('/articles', [ArticleController::class, 'index']);
```
The routes defined in the `routes/api.php` file are automatically nested within a route group by the `RouteServiceProvider`. This group applies the `/api` URI prefix to all routes in the file, so you do not need to manually apply it to each one. The prefix and other route group options can be modified by editing the `RouteServiceProvider` class.

# Show how to create controllers to handle the business logic of the API

## Implement JSON WEB TOKEN (JWT) In Laravel 9

To implement JSON Web Tokens (JWT) in Laravel 9, you need to follow these steps:
- Install the required package:
```bash
composer require php-open-source-saver/jwt-auth
```
- Publish the configuration file:
Make the JWT package configurations accessible. To do this, you need to copy the JWT configuration file from the vendor to the `config/jwt.php` directory using the following command:
```bash
php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
```
- Generate a secret key:
To securely handle the encryption of tokens, a secret key is needed. To generate this key, you can run the following command:
```bash
php artisan jwt:secret
```
This will generate a secret key and save it in the `.env` file, which can be used to encrypt and decrypt JWT tokens.
```
JWT_SECRET=***********************
```
- Configure the JWT AuthGuard:
To configure the JWT AuthGuard in Laravel, you need to follow these steps:

Open the `config/auth.php` file and add the JWT guard to the guards array:
```php
'defaults' => [
        'guard' => 'api',
        'passwords' => 'users',
    ],


    'guards' => [
        // ...

        'api' => [
                'driver' => 'jwt',
                'provider' => 'users',
        ],

    ],
```
This file is the main configuration file for Laravel's authentication system and it determines how authentication should be handled in your application.

The guards array defines the available authentication guards in your application, and each guard is defined as an array with two keys: `driver` and `provider`. The driver key specifies the driver to be used by the guard (in this case, the JWT driver), while the provider key specifies the provider to be used by the guard to retrieve users from persistent storage (in this case, the users provider).

- Changing default User model:
To implement the `PHPOpenSourceSaver\JWT\Contracts\JWTSubject` contract on the User model, you need to add two methods: `getJWTCustomClaims()` and `getJWTIdentifier()`. You can replace the code in the `app/Models/User.php` file with the following code:
```php
<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;
// use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable Implements JWTSubject
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

```
With these modifications, the User model will now implement the `PHPOpenSourceSaver\JWT\Contracts\JWTSubject` contract, and you can use it to create, manage, and validate JWT tokens in your application.

- Create the AuthController:

To handle the authentication process in your application, you need to create an `AuthController` class. You can generate the controller using the following command:

```bash
php artisan make:controller AuthController
```
Once the controller is generated, you can replace its contents with the following code snippet:

```php
<?php

namespace App\Http\Controllers;

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

class AuthController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login', 'register']]);
    }

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

        $token = Auth::attempt($credentials);
        if (!$token) {
            return response()->json([
                'status' => 'error',
                'message' => 'Unauthorized',
            ], 401);
        }

        $user = Auth::user();
        return response()->json([
            'status' => 'success',
            'user' => $user,
            'authorisation' => [
                'token' => $token,
                'type' => 'bearer',
            ]
        ]);
    }

    public function register(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:6',
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        $token = Auth::login($user);
        return response()->json([
            'status' => 'success',
            'message' => 'User created successfully',
            'user' => $user,
            'authorisation' => [
                'token' => $token,
                'type' => 'bearer',
            ]
        ]);
    }

    public function logout()
    {
        Auth::logout();
        return response()->json([
            'status' => 'success',
            'message' => 'Successfully logged out',
        ]);
    }

    public function refresh()
    {
        return response()->json([
            'status' => 'success',
            'user' => Auth::user(),
            'authorisation' => [
                'token' => Auth::refresh(),
                'type' => 'bearer',
            ]
        ]);
    }
}

```
In the AuthController class, the constructor is established to allow the use of the auth:api middleware, which blocks unauthenticated access to certain methods within the controller. The following methods are also included:

**login:** This method authenticates a user using their email and password. If the authentication is successful, the Auth facade's attempt() method returns the JWT token, which is retrieved and returned as a JSON response along with the user object.

**register:** This method creates a user record and logs the user in by generating a token.

**logout:** This method invalidates the user's Auth token.

**refresh:** This method invalidates the user's Auth token and generates a new token.

In this task, you are to implement a basic REST API for managing a collection of news articles. The API should allow for creation, retrieval, updating, and deletion of news articles. With the implementation of JWT authentication, only authenticated users will be able to access and manage the articles.

Each article is a JSON entry with the following keys:

    - id: The unique article ID. (Integer)
    - title: The title of the article. (String)
    - content: The content of the article. (String)
    - author: Name of the author of the article. (String)
    - category: The category of the article. (String)
    - published_at: The publishing date of the article. (Date)

Here is an example of an article JSON object:

```json
{
    "id": 1,
    "title": "New Rest api with laravel or lumen",
    "content": "A REST API (Representational State Transfer Application Programming Interface) is a type of web API that uses standard HTTP requests to perform operations on resources. These resources can be represented as data structures such as JSON or XML, and can be manipulated using standard HTTP methods such as GET, POST, PUT, and DELETE. REST APIs are often used to expose the functionality of web-based applications to other systems or clients over the internet. REST is an architectural style that provides a set of constraints and best practices for building scalable, maintainable, and easily consumable APIs",
    "author": "Aymane Benhima",
    "category": "Backend",
    "published_at": "2023-02-14"
}
```
We will generate the Article model, controller, request, and migration with the following command:

```bash
php artisan make:model Article -mcrR
```
Modify the create_articles_table.php file located in the database/migrations directory with the following code:

```php
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string('title', 30)->nullable(false);
            $table->string('content')->nullable(false);
            $table->string('author')->nullable(false);
            $table->string('category')->nullable(false);
            $table->date('published_at')->nullable(false);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
};
```
 The purpose of the script is to create the "articles" table in the database.

The up() method uses the Schema facade to create a new table in the database with the name "articles". The table has seven columns:

- id: An auto-incremented primary key column
- title: A string column with a maximum length of 30 characters and it cannot be null
- content: A string column that cannot be null
- author: A string column that cannot be null
- category: A string column that cannot be null
- published_at: A date column that cannot be null
- timestamps: Two columns that are automatically managed by Laravel, created_at and updated_at, to keep track of the creation and modification time of each record
The **down()** method uses the Schema facade to drop the "articles" table if it exists.

This migration script can be executed using the Artisan command line interface to create the _"articles"_ table in the database.

Go to the file located at `app/Models/Article.php` and replace its contents with the new code.

```php
<?php

namespace App\Models;

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

class Article extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'content',
        'author',
        'category',
        'published_at'
    ];
}

```
This code defines the Article model in Laravel using the Eloquent ORM.

The use `HasFactory` statement includes the use of Eloquent Factories, which provide a convenient way to generate model instances in tests.

The `$fillable` property defines which columns can be mass-assigned, meaning you can update multiple columns at once using the model's fill method or by directly accessing the model's properties.

By defining this model, you can interact with the "articles" table in the database and perform CRUD operations on it.

Next, go to the `app/Http/Requests/StoreArticleRequest.php` file, and replace the content with the following code:

```php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreArticleRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, mixed>
     */
    public function rules()
    {
        return [

            'title' => 'required|max:30',
            'content' => 'required',
            'author' => 'required',
            'category' => 'required',
            'published_at' => 'required|date',
        ];
    }
}

```
The `StoreArticleRequest` is a form request class in Laravel. It is used to validate incoming HTTP requests before they are processed by the controller.

The `authorize` method returns a boolean indicating whether the user is authorized to make the request. If the method returns true, the request will be processed by the controller. If it returns false, the user will receive a 403 Forbidden response. In this case, the method returns true so the request will always be authorized.

The `rules` method returns an array of validation rules that should be applied to the incoming request data. In this case, the rules require the **title**, **content**, **author**, **category**, and **published_at** fields to be present in the request and have certain conditions, such as a maximum length of 30 for the **title** field and a date format for the **published_at** field. If any of these rules are not satisfied, the request will be considered invalid and will be rejected by the server.

Replace the code in the `app/Http/Controllers/ArticleController.php` file with the following:
```php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreArticleRequest;
use App\Models\Article;
use Illuminate\Http\Request;

class ArticleController extends Controller
{

    public function __construct()
    {
        $this->middleware('auth:api');
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $articles = Article::orderBy('id')->get();

        return response()->json([
            'status' => 'success',
            'articles' => $articles
        ]);
    }

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

        return response()->json([
            'status' => true,
            'message' => "Article Created successfully!",
            'article' => $article
        ], 201);
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Models\Article  $article
     * @return \Illuminate\Http\Response
     */
    public function show(Article $article)
    {
        $article->find($article->id);
        if (!$article) {
            return response()->json(['message' => 'Article not found'], 404);
        }
        return response()->json($article, 200);
    }

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

        if (!$article) {
            return response()->json(['message' => 'Article not found'], 404);
        }

        return response()->json([
            'status' => true,
            'message' => "Article Updated successfully!",
            'article' => $article
        ], 200);
    }

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

        if (!$article) {
            return response()->json([
                'message' => 'Article not found'
            ], 404);
        }

        return response()->json([
            'status' => true,
            'message' => 'Article deleted successfully'
        ], 200);
    }
}
```

The code defines a class **ArticleController** which is a part of the **Http\Controllers** namespace. This class is used to manage the articles stored in the database. The class is built using the Laravel framework and makes use of the Model **Article** and the Request **StoreArticleRequest** to interact with the database.

The class has a constructor that specifies the middleware used by the controller. The middleware defined is auth:api which means that any request made to the routes associated with this controller must be authenticated using the API authentication mechanism.

The class contains several methods that handle different types of requests:

- GET request to /articles:

    - returns JSON of a collection of all articles, ordered by id in increasing order
    - returns response code 200

**index** - returns a listing of all the articles in the database, ordered by their IDs.

- POST request to /articles:

    - Validates the following conditions:
        title is provided
        length of title is less than 30 characters long
        content is provided
        author is provided
        category is provided
        published_at is provided
    - If any of the above requirements fail, the server should return the response code 400. Otherwise, in the case of a successful request, the server should return the response code 201 and the article information in JSON format.
    - expects a JSON article object without an id property as a body payload.
    - adds the given article object to the collection of articles and assigns a unique integer id to it. The first created article must have id 1, the second one 2, and so on.
    - the response code is 201, and the response body is the created article object.

**store** - stores a new article in the database using the data provided in the request. The **StoreArticleRequest** is used to validate the request data before storing it.

- GET request to /articles/:id:

    - returns an article with the given id
    - if the matching article exists, the response code is 200 and the response body is the matching article object
    - if there is no article with the given id in the collection, the response code is 404

**show** - returns the details of a single article specified by its ID.

- PUT request to /articles/:id:

    - Update a particular article object which has the given id
    expects a JSON object of article events for a successful 200 response
    - if there is no article with the given id in the collection, the response code is 404

**update** - updates an existing article in the database using the data provided in the request. The StoreArticleRequest is used to validate the request data before updating it.

- DELETE request to /articles/:id:

    - Delete a particular article object which has the given id
    expects successful 200 response message
    - if there is no article with the given id in the collection, the response code is 404

**destroy** - deletes an article from the database specified by its ID.

All the methods return a JSON response, which contains the status of the operation and the relevant data.

Navigate to the `routes/api.php` file, and replace the content with the following code:

```php
<?php

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

/*
|--------------------------------------------------------------------------
| 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::controller(AuthController::class)->group(function () {
    Route::post('login', 'login');
    Route::post('register', 'register');
    Route::post('logout', 'logout');
    Route::post('refresh', 'refresh');
});

Route::apiResource('articles', ArticleController::class);
```

The code is defining two sets of API routes in a Laravel web application.

**Authentication Routes:** The first set of routes is related to authentication. The code is creating a group of routes that will be handled by the AuthController. The group includes the following routes:
- **Login:** a route that allows a user to log in to the application.
- **Register:** a route that allows a user to register for a new account.
- **Logout:** a route that allows a user to log out of the application.
- **Refresh:** a route that allows a user to refresh their authentication token.

**Article Resource Routes:** The second set of routes is for managing articles. The code is creating a RESTful resource controller for articles using the `Route::apiResource` method. This method creates a set of routes for the specified resource (in this case, articles), including routes for creating, reading, updating, and deleting articles.

The code uses the Route facade provided by Laravel to define the routes, and the `->group` method to define the authentication routes. The Route::controller method is used to specify the controller class that will handle the routes in the group.

And Finally don't forget to do the command of migration:
```bash
php artisan migrate
```
## Conclusion
The code above uses the latest syntax available in Laravel 9. If you're using an older version of Laravel, you will need to declare your routes using the traditional method.

### Test the application

Run the below command to start the Laravel application:

```bash
php artisan serve
```

### Register the API

To begin using Postman, you can follow these steps:

1. Open the Postman application.
2. Click on the "New" button on the top left corner of the Postman app and select "Collection" from the dropdown menu.
3. Give a name to the collection and click "Create."
4. Click on the collection to open it and then click the "New" button again to add a request.
5. Enter the request URL and choose the appropriate HTTP method (GET, POST, PUT, DELETE, etc.) from the dropdown menu.
6. Enter the URL for the registration API in the address bar.
7. Select "POST" as the HTTP request method from the dropdown menu.
8. In the "Body" tab, select the "form-data" option.
9. Add the input fields for name, email, and password.
10. Click the "Send" button to see the server response.

Step 1:

![Step 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jeqe7lkjdi5rfua8x07f.png)

Step 2:

![Step 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tehhnrpqruahgtc53nzo.png)

Step 3:

![Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8ltwdqam6snyp8kpjdb.png)