Laravel 9 Rest API With JWT Authentication Tutorial

Laravel 9 Rest API With JWT Authentication Tutorial

Last Updated: 30 Nov, 2022

Dear friends, greetings. In this tutorial, we will learn How to Create a CRUD REST API with JWT (JSON Web Token) Authentication in Laravel 9. To acheive this, we will be using tymon/jwt-auth laravel package.

How to Create Rest API CRUD With JWT Authentication in Laravel 9?

Follow the below mentioned step-by-step guidance to learn how to create REST APIs with JWT (JSON Web Token) in laravel 9 application from scratch:

  • Step 1: Install Fresh Laravel 9 Application
  • Step 2: Set Up Database Configuration
  • Step 3: Install and Set Up JWT Authentication Package
  • Step 4: Set Up User Model
  • Step 5: Set Up Product Model and Migration
  • Step 6: Configure Auth Guard
  • Step 7: Create and Set Up Auth Controller
  • Step 8: Create and Set Up Product Controller
  • Step 9: Create Auth and CRUD API Routes
  • Step 10: Run Laravel Application
  • Step 11: Test the REST API using Postman

Now, we will go through the above given steps one-by-one to and get it done.

Step 1: Install Fresh Laravel 9 Application

First of all, let's install a fresh laravel 9 application. Open your terminal and run the following command:

composer create-project laravel/laravel laravel9app

Step 2: Setup Database Configuration

Next, lets update database configuration in .env file as follows:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel9db
DB_USERNAME=dbusername
DB_PASSWORD=dbpassword

Step 3: Install and Configure JWT Authentication Package

Next, install tymon/jwt-auth laravel package to authenticate the apis. It is a third-party JWT package and allows user authentication using JSON Web Token in Laravel securely. Run below command in terminal:

composer require tymon/jwt-auth:^1.0

Once you have successfully install JWT Auth package, register the JWT auth providers. Open the config/app.php file and copy below code in it:

config/app.php

'providers' => [
    ...
    ...
    Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
],
'aliases' => [
    ...
    ...
    'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
    'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
]

Next, publish the JWT Auth package’s configuration. Run below command that will copy JWT Auth files from vendor folder to config/jwt.php file:

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

Next, run the below command in terminal to generate a secret key that will handle the JWT token encryption:

php artisan jwt:secret

Once, this command is successfully executed, a secret key will be genrated inside .env file as follows:

JWT_SECRET=jwt_secret_key_string

Step 4: Set Up User Model

Next, we need to set up User model. In fresh installation, laravel comes with a pre-defined User model that is used for authentication purpose. In this step, we will learn how to implement the jwt-auth package in the User model.

Import the Tymon\JWTAuth\Contracts\JWTSubject in contract inside User model. Also, define below mentioned two methods:

getJWTIdentifier() : This method helps to get get the identifier that will be stored in the subject claim of the JWT.

getJWTCustomClaims() : This method returns a key value array, containing any custom claims to be added to the JWT.

Next, open the app/Models/User.php and copy the below given code in it:

<?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 Laravel\Sanctum\HasApiTokens;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    use HasApiTokens, 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 [];
    }
}

Step 5: Set Up Product Model and migration

Next, we have to create Product model and migration using below command:

php artisan make:model Product -m

Above command will create a model file named Product.php and a migration file named database/migrations/*_create_products_table.php. Open the migration file and copy below code in it:

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateProductsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('mrp');
            $table->string('price');
            $table->string('quantity');
            $table->timestamps();
        });
    }

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

once you are done with model and migration files, let's run the below command to create the tables in MySQL DB:

php artisan migrate

Step 6: Configure Auth Guard

Next, we have to set up the auth guard for JWT to secure the authentication process. Laravel guard uses the session driver to protect the guards. We will use the JWT driver for API guard. Open the config/auth.php file and copy below code in it.

<?php
return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

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

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    'password_timeout' => 10800,
];

Step 7: Create and Set Up Auth Controller

In this step, we have to create AuthController and need to define necessary methods and logics. Let's run below command in terminal that will create app/Http/Controllers/AuthController.php file.

php artisan make:controller AuthController

Next, open the app/Http/Controllers/AuthController.php and copy below code in it:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Validator;
use App\Models\User;

class AuthController extends Controller
{
    /**
     * Create a new AuthController instance.
     *
     * @return void
     */
    public function __construct() {
        $this->middleware('auth:api', ['except' => ['login', 'register']]);
    }

    /**
     * Get a JWT via given credentials.
     * @return \Illuminate\Http\JsonResponse
     */
    public function login(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'email' => 'required|email',
            'password' => 'required|string|min:8'
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => false,
                'message' => 'Invalid Inputs',
                'error' => $validator->errors()
            ], 422);
        }

        if (! $token = auth('api')->attempt($validator->validated())) {
            return response()->json([
                'status' => false,
                'message' => 'Invalid Credentials',
            ], 400);
        }

        return $this->respondWithToken($token);
    }

    /**
     * Register a User.
     * @return \Illuminate\Http\JsonResponse
     */
    public function register(Request $request) 
    {
        $validator = Validator::make($request->all(), [
            'name' => 'required|string|min:3|max:50',
            'email' => 'required|string|email|max:100|unique:users',
            'password' => 'required|string|confirmed|min:8',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => false,
                'message' => 'Invalid Inputs',
                'error' => $validator->errors()
            ], 401);
        }

        $user = new User();
        $user->name = $request->name;
        $user->email = $request->email;
        $user->password = bcrypt($request->password);
        $user->save();

        return response()->json([
            'status' => true,
            'message' => 'User successfully registered',
            'user' => $user
        ], 201);
    }

    /**
     * Log the user out (Invalidate the token).
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout(Request $request) 
    {
        try {
            auth('api')->logout();
            return response()->json([
                'status' => true,
                'message' => 'Successfully logged out'
            ]);
        } catch (Exception $e) {
            return response()->json([
                'status' => false,
                'message' => 'Sorry, cannot logout'
            ], 500);
        }
    }

    /**
     * Get the authenticated User.
     * @return \Illuminate\Http\JsonResponse
     */
    public function userProfile(Request $request) 
    {
        return response()->json([
            'status' => true,
            'message' => 'User found',
            'data' => auth('api')->user()
        ], 200);
    }

    /**
     * Refresh a token.
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh(Request $request) 
    {
        return $this->respondWithToken(auth('api')->refresh());
    }

    /**
     * Get the token array structure.
     * @param  string $token
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        $minutes = auth('api')->factory()->getTTL() * 60;
        $timestamp = now()->addMinute($minutes);
        $expires_at = date('M d, Y H:i A', strtotime($timestamp));
        return response()->json([
            'status' => true,
            'message' => 'Login successful',
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_at' => $expires_at
        ], 200);
    }
}

Step 8: Create and Set Up Product Controller

In this step, we have to create ProductController by running below command in terminal that will create app/Http/Controllers/ProductController.php file.

php artisan make:controller ProductController

Next, open the app/Http/Controllers/ProductController.php and copy below code in it:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Validator;
use App\Models\Product;

class ProductController extends Controller
{
    /**
     * Create a new AuthController instance.
     *
     * @return void
     */
    public function __construct() {
        $this->middleware('auth:api');
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $products = Product::all();
        
        return response()->json([
            "status" => true,
            "message" => "Product List",
            "data" => $products
        ]);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $request_data = $request->all();
        
        $validator = Validator::make($request_data, [
            'name' => 'required',
            'mrp' => 'required',
            'price' => 'required',
            'quantity' => 'required'
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => false,
                'message' => 'Invalid Inputs',
                'error' => $validator->errors()
            ]);
        }

        $product = Product::create($request_data);
        
        return response()->json([
            "status" => true,
            "message" => "Product created successfully.",
            "data" => $product
        ]);
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show(Product $product)
    {
        if (is_null($product)) {
            return response()->json([
                'status' => false,
                'message' => 'Product not found'
            ]);
        }

        return response()->json([
            "success" => true,
            "message" => "Product found.",
            "data" => $product
        ]);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Product $product)
    {
        $request_data = $request->all();
        
        $validator = Validator::make($request_data, [
            'name' => 'required',
            'mrp' => 'required',
            'price' => 'required',
            'quantity' => 'required'
        ]);

        if($validator->fails()){
            return response()->json([
                'status' => false,
                'message' => 'Invalid Inputs',
                'error' => $validator->errors()
            ]);      
        }

        $product->name = $request_data['name'];
        $product->mrp = $request_data['mrp'];
        $product->price = $request_data['price'];
        $product->quantity = $request_data['quantity'];
        $product->save();
        
        return response()->json([
            "status" => true,
            "message" => "Product updated successfully.",
            "data" => $product
        ]);
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy(Product $product)
    {
        $product->delete();
        return response()->json([
            "status" => true,
            "message" => "Product deleted successfully.",
            "data" => $product
        ]);
    }
}

Step 9: Create Auth and CRUD API Routes

In this step, we have to create route for the apis. API routes are stored and served through routes/api.php file. These routes are prfixed with api/ and authentication routes are denoted by auth/. We will be creating a resource route for CRUD operation on Product table.

So, lets open the routes/api.php and copy below given code in it:

<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
use App\Http\Controllers\ProductController;

/*
|--------------------------------------------------------------------------
| 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::group(['middleware' => 'api'], function () {
    Route::group(['prefix' => 'auth'], function () {
        Route::post('/login', [AuthController::class, 'login']);
        Route::post('/register', [AuthController::class, 'register']);
    });
    Route::post('/logout', [AuthController::class, 'logout']);
    Route::post('/refresh', [AuthController::class, 'refresh']);
    Route::get('/profile', [AuthController::class, 'userProfile']);  
    Route::resource('/products', ProductController::class);  
});

Step 10: Run Laravel Application

Great, we are all set. Lets start the application now by using below command in terminal:

php artisan serve

Once development server is started, you can test your application with postman.

Step 11: Test the REST API using Postman

Now, in this step we will use the postman to test our application. Open postman and test the APIs one-by-one.

Test User Registration API

First of all, register a new user using /api/auth/register API. You have to select POST as request method and in the request body select form-data and fill in all the required parameters like name, email, password, etc. Then call the api by clicking on Send button in postman. Please refer the below attached screenshot:

Laravel 9 Rest API With JWT Authentication Tutorial

Test User Login API

Next, test the User Login by calling the /api/auth/login API. Select POST as request method and fill in required parameters (email and password) under form-data in request body. On successful call, this API will return a JWT access token along with other details like token type, token expiration time, etc. Please refer the below screenshot:

Laravel 9 Rest API With JWT Authentication Tutorial
Once you have got the API Access Token, you can easily call other APIs (User Profile, Token Refresh and Logout) by providing the access token in request header.

Test User Profile API

Next, get the User Profile by calling /api/profile API. Please refer the below screenshot:

Laravel 9 Rest API With JWT Authentication Tutorial

 

CRUD APIs on Product Table

 

Product Create API

Next, create the prodcut by calling /api/products POST API. Please refer the below screenshot:

Laravel 9 Rest API With JWT Authentication Tutorial

Product List API

Next, get the prodcuts by calling /api/products GET API. Please refer the below screenshot:

Laravel 9 Rest API With JWT Authentication Tutorial

Product Update API

Next, update the prodcut by calling /api/products PUT API. Please refer the below screenshot:

Laravel 9 Rest API With JWT Authentication Tutorial

Product Delete API

Next, delete the prodcut by calling /api/products DELETE API. Please refer the below screenshot:

Laravel 9 Rest API With JWT Authentication Tutorial

Dear friends, if you have followed this tutorial step-by-step, I can proudly say that you have successfully learnt how to implement restful api in laravel 9 with JSON Web Token (JWT) authentication.

HAPPY LEARNING:)

 

Thank You, Please Share.