DEV Community

Ian Kumu
Ian Kumu

Posted on • Originally published at iankumu.com on

How to Build a Rest API with Laravel: A Beginners Guide

With the rise of mobile and javascript web frameworks such as React and Vue, Restful APIs have seen their popularity increase. This is because you can maintain one backend serving multiple front-end clients.

Laravel provides a good environment and ecosystem for building your Rest API.

First-party packages such as Laravel Passport and Laravel Sanctum provide API authentication implementation making authentication easy.

Laravel Breeze provides starter templates that can help with reset password features.

Socialite and Scout provide Social Login implementations and Full-text search functionalities.

The laravel ecosystem provides solutions to almost all problems you can encounter in your development journey thus providing maximum productivity to a developer.

This tutorial will explore how to create a Laravel Rest API with authentication using Laravel Sanctum.

What is a Restful API?

REST stands for REpresentational State Transfer and it is a pattern used for communication between applications through HTTP. The protocol is stateless meaning no session is stored between the applications. Each request is therefore processed as if it is a new request even though it is repeated.

A benefit of REST APIs is that they can easily be cached. It is easy to cache a response from the Rest API in a service such as Redis or Memcached and thus easy to scale.

For an API to be considered Restful, it has to have the following

  • Must be accessible over a URL or endpoint
  • Must use any of the REST Methods

The common REST Methods are:

GET -Fetch resources from an API

POST -Create a resource in the API

PUT/PATCH -Update a resource in the API

DELETE – Delete a resource from an API

  • Can have HTTP headers
  • Must return a valid response code in each response.

How to build a REST API with Laravel

Create a new Application

The first step is to create a new Laravel application.

laravel new rest 
Enter fullscreen mode Exit fullscreen mode

Set up a Model and Migration

The next step is to create a Model and its corresponding migration file. This acts as a blueprint for our database table. In this tutorial, I will use Products as my resource.

php artisan make:model Products -m 
Enter fullscreen mode Exit fullscreen mode

The -m flag will instruct Laravel to create the corresponding migration file of the Products Model.

//App/Models/Products
<?php

namespace App\Models;

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

class Products extends Model
{

    use HasFactory;

}
Enter fullscreen mode Exit fullscreen mode

The migration file generated will resemble the one below.

<?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('products', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

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

Enter fullscreen mode Exit fullscreen mode

We can start by updating the migration file by adding more columns to our database. I will add the following columns: product name , product price and product description

<?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('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->double('price');
            $table->longText('description');
            $table->timestamps();
        });
    }

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

Enter fullscreen mode Exit fullscreen mode

We can then update the Products model by registering the mass-assignable variables. This helps prevent SQL injection by instructing laravel only to accept data containing the specified keys/variables.

//App/Models/Products
<?php

namespace App\Models;

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

class Products extends Model
{
    use HasFactory;

    protected $fillable = [
        'name', 'price', 'description'
    ];
}
Enter fullscreen mode Exit fullscreen mode

The last step is to set up the database credentials in the .env file and create the database

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-rest
DB_USERNAME=root
DB_PASSWORD=password
Enter fullscreen mode Exit fullscreen mode

The final step is to migrate the database.

php artisan migrate 
Enter fullscreen mode Exit fullscreen mode

Create a Database Seeder and Factory

When developing, I prefer to use fake data to ensure that I develop as fast as possible. Laravel provides a handy Factory facade that can allow us to use Faker to generate dummy data.

We can run this command to create a Factory

php artisan make:factory ProductsFactory
Enter fullscreen mode Exit fullscreen mode

This will create a file in the databases/factories folder

We can update the file as follows

//database/factories/ProductsFactory
<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Products>
 */
class ProductsFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition()
    {
        return [
            'name' => $this->faker->word,
            'price' => $this->faker->numberBetween(1, 99),
            'description' => $this->faker->sentence()
        ];
    }
}

Enter fullscreen mode Exit fullscreen mode

Now that our factory is ready, we can call it in the DatabaseSeeder file to seed our database.

//database/seeders/DatabaseSeeder
<?php

namespace Database\Seeders;

// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        \App\Models\Products::factory(10)->create();
    }
}

Enter fullscreen mode Exit fullscreen mode

We can now seed the database

php artisan db:seed
Enter fullscreen mode Exit fullscreen mode

Create a Controller

Let’s now create a Controller which will contain all the Business logic for the API.

php artisan make:controller ProductsController -r
Enter fullscreen mode Exit fullscreen mode

The -r flag will generate a Controller that is resourceful. This means it will create a controller with all the required methods for a Restful API.

The main methods we will use are index , show , store , update and destroy. We can delete the create and edit methods as we will not need them. We can update the Products Controller as shown below

//App/Http/Controllers/ProductsController

<?php

namespace App\Http\Controllers;

use App\Http\Resources\ProductResource;
use App\Models\Products;
use Illuminate\Http\Request;

class ProductsController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param Products $product
     * @return \Illuminate\Http\Response
     */
    public function show(Products $product)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param \Illuminate\Http\Request $request
     * @param Products $product
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Products $product)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param Products $product
     * @return \Illuminate\Http\Response
     */
    public function destroy(Products $product)
    {
        //
    }
}

Enter fullscreen mode Exit fullscreen mode

These methods can be mapped to the default HTTP verbs (get, post, patch/put and delete).

Index(Get all Products)

We can use this method to return all products present in the database

use App\Models\Products;
public function index()
{
   return Products::all();
}
Enter fullscreen mode Exit fullscreen mode

We can further customise it by paginating it or caching the response from the database.

Show(Get a Single Product)

We can use this method to return a single product present in the database

We can pass the product id as a parameter and we can then fetch it from the database

Note: I am using API Resources which I will discuss later in the article.

use App\Http\Resources\ProductResource;
use App\Models\Products;

public function show(Products $product)
{
   return new ProductResource($product);
}
Enter fullscreen mode Exit fullscreen mode

Store(Create a Product)

We can create a new product record in the database using this method. A post method can be made to this method through an endpoint to create the record

use App\Http\Resources\ProductResource;
use App\Models\Products;

public function store(Request $request)
{
        $product_name = $request->input('name');
        $product_price = $request->input('price');
        $product_description = $request->input('description');

        $product = Products::create([
            'name' => $product_name,
            'price' => $product_price,
            'description' => $product_description,
        ]);
        return response()->json([
            'data' => new ProductResource($product)
        ], 201);
}
Enter fullscreen mode Exit fullscreen mode

Update(Update Product Details)

To update the product details , we can update the logic of the update method

use App\Http\Resources\ProductResource;
use App\Models\Products;

public function update(Request $request, Products $product)
{
        $product_name = $request->input('name');
        $product_price = $request->input('price');
        $product_description = $request->input('description');

        $product->update([
            'name' => $product_name,
            'price' => $product_price,
            'description' => $product_description,
        ]);
        return response()->json([
            'data' => new ProductResource($product)
        ], 200);
}
Enter fullscreen mode Exit fullscreen mode

Destroy(Delete a Product)

At times you might want to delete products from the database. We can add the following code to delete products from the database.

use App\Models\Products;

public function destroy(Products $product)
{
   $product->delete();
   return response()->json(null,204);
}
Enter fullscreen mode Exit fullscreen mode

Routes and endpoints

Let’s now create the endpoints that will be accessible over HTTP. We can add the routes in the routes/api.php file

//routes/api.php
<?php

use App\Http\Controllers\ProductsController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| 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:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Route::get('products', [ProductsController::class, 'index'])->name('products.index');
Route::get('products/{product}', [ProductsController::class, 'show'])->name('products.show');
Route::post('products', [ProductsController::class, 'store'])->name('products.store');
Route::put('products/{product}', [ProductsController::class, 'update'])->name('products.update');
Route::delete('products/{product}', [ProductsController::class, 'destroy'])->name('products.destroy');

Enter fullscreen mode Exit fullscreen mode

These endpoints are mapped to the methods in the ProductsController we created earlier.

We can now test the index method through a GET request. This will return the following response

[
    {
        "id": 1,
        "name": "quo",
        "price": 15,
        "description": "Ut rerum aut deleniti eveniet ad et ullam perferendis.",
        "created_at": "2022-11-18T15:18:13.000000Z",
        "updated_at": "2022-11-18T15:18:13.000000Z"
    },
    {
        "id": 2,
        "name": "maxime",
        "price": 70,
        "description": "Natus officiis repellat vero ea voluptatem mollitia similique.",
        "created_at": "2022-11-18T15:18:13.000000Z",
        "updated_at": "2022-11-18T15:18:13.000000Z"
    }
]
Enter fullscreen mode Exit fullscreen mode

Formatting the Response

The response above is returned in JSON format. It includes details from the database with the column names as the keys.

What if you want to format this response? One thing you might not want to expose is the created_at and updated_at data. You might also want to calculate and return a predefined discount back as a response.

Laravel allows us to customize our responses using API resources.

In this example, I will assume that all products have a 10% discount. I will therefore return the product price and the discounted price as part of the payload in the response.

php artisan make:resource ProductResource
Enter fullscreen mode Exit fullscreen mode

This will transform a model into an array.

//App/Http/Resources/ProductResource
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class ProductResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param \Illuminate\Http\Request $request
     * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'product_name' => $this->name,
            'product_price' => "$" . $this->price,
            'discounted_price' => "$" . ($this->price * 0.9),
            'discount' => "$" . ($this->price * 0.1),
            'product_description' => $this->description,
        ];
    }
}

Enter fullscreen mode Exit fullscreen mode

Let’s update our index method in Products Controller to use the Product resource

public function index()
{
   return ProductResource::collection(Products::all());
}
Enter fullscreen mode Exit fullscreen mode

This will return the newly formatted response

{
    "data": [
        {
            "id": 1,
            "product_name": "quo",
            "product_price": "$15",
            "discounted_price": "$13.5",
            "discount": "$1.5",
            "product_description": "Ut rerum aut deleniti eveniet ad et ullam perferendis."
        },
{
            "id": 2,
            "product_name": "maxime",
            "product_price": "$70",
            "discounted_price": "$63",
            "discount": "$7",
            "product_description": "Natus officiis repellat vero ea voluptatem mollitia similique."
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Response Codes

It is important to always return a valid response code in each request. These response codes can be used by a consuming application or client to know what exactly happened on the server side.

Here is a list of the most common response codes

  • 200 – Ok. This signifies it is a success code and it is the default response code
  • 201- Created. This shows that a resource has been created. It is useful for POST requests.
  • 204- No Content. This signifies that the action was successful but no content is returned. It is useful for DELETE requests since a deleted resource cannot return any body content
  • 400- Bad Request. This signifies that the client passed an invalid request to the server
  • 401- Unauthorized. This signifies that the client is not authorized to access the resource. It is typically used in authentication and authorization services.
  • 403- Forbidden. This signifies that a user is authenticated but is not allowed to access the resource.
  • 404- Not Found. This signifies that a resource is not found
  • 500- Internal Server Error. This implies that there is an error at the server level

Laravel allows us to specify the correct response code using the response()->json() helper function. It is important to always return a response code so that the client/frontend can display the correct data and “fail gracefully” in the case of an error.

response()->json(data,status code)
Enter fullscreen mode Exit fullscreen mode

Setting up Authentication with Laravel Sanctum

We have discussed the most basic way of creating a CRUD REST API in Laravel. We now want to add authentication to our API in order to secure it. There are two ways in which we can implement authentication; either through Laravel Passport or Laravel Sanctum.

Passport provides a way in which applications authenticate themselves over the internet. It basically adds Oauth implementation to your API which other systems can use to authenticate themselves when interacting with your API. It is useful for public APIs where you might want to track API usage and also limit requests for each API key.

Sanctum on the other hand provides a stateless integration of the normal authentication service by using email and passwords to provide authentication to a client. It is useful for private/internal APIs that don’t need all the features provided by an OAuth Server.

In this part, I will use Laravel Sanctum to create a simple authentication service for my API. I will use the default email and password to authenticate a user.

To learn more about the two authentication packages, you can read the Laravel Sanctum article or the Laravel Passport article.

Configuration and Setup

We will first create an authentication scaffold using Laravel Breeze

composer require laravel/breeze --dev
Enter fullscreen mode Exit fullscreen mode

We can then install the package

php artisan breeze:install
php artisan migrate
npm install 
npm run dev
Enter fullscreen mode Exit fullscreen mode

This will create a basic authentication scaffold and a forgot and reset password functionality out of the box.

Let’s now install Laravel sanctum and set it up

composer require laravel/sanctum
Enter fullscreen mode Exit fullscreen mode

Next is to publish Sanctum’s configurations

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
Enter fullscreen mode Exit fullscreen mode

Finally, we can migrate the database that will store the access tokens

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

The last step is to allow Sanctum to issue access tokens which will be used to authenticate users’ requests.

App/Models/User
<?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; //import the trait

class User extends Authenticatable
{
    use HasApiTokens; //add this trait
    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',
    ];
}

Enter fullscreen mode Exit fullscreen mode

We can now create a new controller that will be responsible for the Authentication

php artisan make:controller UserAuthenticationController
Enter fullscreen mode Exit fullscreen mode

Register

We can start by fleshing out the user registration logic. We will use the default user table that comes with laravel but feel free to add and remove some columns as you please

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;

public function register(Request $request)
{
        $name = $request->input('name');
        $email = strtolower($request->input('email'));
        $password = $request->input('password');

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

        $token = $user->createToken('auth_token')->plainTextToken;

        return response()->json([
            'message' => 'User Account Created Successfully',
            'access_token' => $token,
            'token_type' => 'Bearer',
        ], 201);
}
Enter fullscreen mode Exit fullscreen mode

From the code, we can see that a user account is created in the database and an access token is issued to the user and is returned back to the user as a response.

Login

The next important feature is to allow users to log in. We can add the following logic

use Illuminate\Support\Facades\Auth;

public function login(Request $request)
    {
        $email = strtolower($request->input('email'));
        $password = $request->input('password');

        $credentials = [
            'email' => $email,
            'password' => $password
        ];
        if (!Auth::attempt($credentials)) {
            return response()->json([
                'message' => 'Invalid login credentials'
            ], 401);
        }

        $user = User::where('email', $request['email'])->firstOrFail();

        $token = $user->createToken('auth_token')->plainTextToken;

        return response()->json([
            'access_token' => $token,
            'token_type' => 'Bearer',
        ],200);
    }
Enter fullscreen mode Exit fullscreen mode

Similar to the registration, we need to return the access token as soon as the user credentials have been validated.

If the credentials are wrong, we need to alert the user that their credentials are wrong. But how?🤔

Since a REST API is stateless, there is no way of flashing responses in real-time. We, therefore, need to return a valid response code and a descriptive message so that a user knows what happened at the server level.

We can use a simple if else statement but this might make our code less clean. A solution is to use the default Exception Handler provided by Laravel.

We can add some logic to apply to the whole API and catch any Authentication Exceptions and return back a valid API Response back to the client.

We can update the App/Exceptions/Handler. php to resemble the one below

App/Exceptions/Handler

<?php

namespace App\Exceptions;

use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Request;
use Throwable;

class Handler extends ExceptionHandler
{
    /**
     * A list of exception types with their corresponding custom log levels.
     *
     * @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
     */
    protected $levels = [
        //
    ];

    /**
     * A list of the exception types that are not reported.
     *
     * @var array<int, class-string<\Throwable>>
     */
    protected $dontReport = [
        //
    ];

    /**
     * A list of the inputs that are never flashed to the session on validation exceptions.
     *
     * @var array<int, string>
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     *
     * @return void
     */
    public function register()
    {
        $this->renderable(function (AuthenticationException $exception, Request $request) {
            if ($request->is('api/*')) {
                if ($exception instanceof AuthenticationException) {
                    return $request->expectsJson() ?:
                        response()->json([
                            'message' => 'Unauthenticated.',
                            'status' => 401,
                            'Description' => 'Missing or Invalid Access Token'
                        ], 401);
                }
            }
        });
    }
}

Enter fullscreen mode Exit fullscreen mode

This will come in handy when we discuss middleware.

Logout

Of course in each system, we need a logout functionality so we will also create its function

public function logout()
{
    auth()->user()->tokens()->delete();

    return response()->json([
        'message' => 'Succesfully Logged out'
    ], 200);
}
Enter fullscreen mode Exit fullscreen mode

Note that we are using the delete function to invalidate an existing access token and delete it from the database. This makes the access token unusable and we can comfortably say the user is logged out.

The final piece left is to create the endpoints/routes in the routes/api.php file.

Route::post('login', [UserAuthenticationController::class, 'login']);
Route::post('register', [UserAuthenticationController::class, 'register']);
Route::post('logout', [UserAuthenticationController::class, 'logout'])->middleware('auth:sanctum');
Enter fullscreen mode Exit fullscreen mode

Only the logout endpoint needs middleware because it requires a token to be passed to v erify which user is being logged out of the system.

Scopes and Middlewares

Scopes are commonly used in APIs to assign abilities to tokens. They help flesh out what a user can and cannot do in a system. Think of them as permissions you grant to users once you onboard them. Some users can access financial records in a system while others cannot.

Middlewares on the other hand are used to protect endpoints from unauthorized access. Think of them as a gate that filters out valid requests from unauthorized and malicious requests. They help safeguard your endpoints from being accessed by users who have no role in accessing them.

We can add the auth:sanctum middleware to the product’s endpoints to safeguard them. This means that for a request to be considered valid, it has to have the Authorization: Bearer token headers(where the token is the actual access token issued by the API).

HEADERS {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer <Token>'
    }
Enter fullscreen mode Exit fullscreen mode

Each request has to include the bearer token in the headers or the server will respond with a 401(Unauthorized) response.

Grouping Endpoints

In most cases, you might have multiple endpoints that share some common things such as version (e.g v1,v2), prefixes (admin), or middleware.

It is important to always version your APIs especially if they are being used by the public. This helps in preventing breaking changes and also allows for backward compatibility in the event new APIs are available.

We can group these endpoints using the Route::group method

Route::group(['middleware' => ['auth:sanctum']], function () {

    //All Routes that share the auth:sanctum middleware
});
Enter fullscreen mode Exit fullscreen mode

This way we can have our code organized and clean. In this example, I will group the routes to be of version 1 and use the auth:sanctum middleware.

We might also want to reduce the size of the api.php file by making each resource use apiresource. This simple function reduces the file size by almost 60% and makes reading and maintaining the code an easy task.

Route::apiResource('products', ProductsController::class);
Enter fullscreen mode Exit fullscreen mode

The apiresource method under the hood works by mapping the main functions(index, show, store, update and delete) in our controller to their various endpoints.

For example products.show is mapped to the endpoint api/products/{product}.

These are some cool techniques you can use to optimize your development experience and become more productive when developing your APIs.

The final routes/api.php file should resemble the one below

//routes/api.php
<?php

use App\Http\Controllers\ProductsController;
use App\Http\Controllers\UserAuthenticationController;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| 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!
|
*/

//These routes are NOT protected using middleware

Route::prefix('v1')->group(function () {
    Route::post('login', [UserAuthenticationController::class, 'login']);
    Route::post('register', [UserAuthenticationController::class, 'register']);
});

//These routes are protected using middleware
Route::prefix('v1')->middleware('auth:sanctum')->group(function () {
    Route::post('logout', [UserAuthenticationController::class, 'logout']);
    Route::apiResource('products', ProductsController::class);
});

Enter fullscreen mode Exit fullscreen mode

Testing using Postman

The last step in this tutorial is to test our API through an HTTP Client. There are numerous HTTP Clients such as Postman and Thunderclient just to name a few.

I personally use Thunderclient because it integrates seamlessly with Vscode which is my goto editor. It can be added as a Vscode extension thereby making your Vscode editor a powerful editor and REST API client at the same time.

It improves my productivity and development experience immensely. You are free to use any other HTTP Client of your choice.

I will use postman in this article as most people are familiar with it.

We will break down the test from authentication to the basic CRUD

Authentication

We can further break down authentication into registration, login and logout.

Registration

We can make a POST request to the endpoint api/v1/register with the body on postman

Laravel REST APIs(register)
Successful Registration

Login

To test login functionality, we can make a POST request to the api/v1/login

Laravel REST APIs(login)
Failed Login

A failed login attempt returns a 401(Unauthorized) Status Code and also returns a descriptive message to the client

Laravel REST APIs(login)
Successful Login

A successful login attempt returns a 200(OK) Status Code and also returns an access token that can be stored for future use.

Logout

Logout can be tested by making a POST request to the api/v1/logout endpoint. We also need to attach the bearer token assigned to us during registration/login to the Headers.

Laravel REST APIs(logout)
Logout

Upon success, a descriptive message and a 200(OK) Status Code are returned back to a User.

We can now test the Product resource. Similar to log out, we also need to attach the bearer token assigned to us during registration/login to the Headers. This is because all the product’s endpoints are protected by the auth:sanctum middleware.

If we try to access any product’s endpoint without the access token , the server will respond with a 401(Unauthorized) Status Code.

Unauthorized Access
Unauthorized Access

This is because we registered an Exception Handler to catch any access to the API that is not authorized and return a 401 Status Code with a descriptive response. This protects our API endpoints from unauthorized access.

Get All Products

All products can be returned by making a GET request to the api/v1/products endpoint.

Get all products REST API
Get All Products

Get A Single Product

We can fetch a single product from the API by making a GET request to the api/v1/products/{id} endpoint

Get a single product REST API
Get a Single Product

Add A Product

We can add a product by making a POST request to the api/v1/products endpoint

Add a Product REST API
Add a Product

Update A Product

To update a product’s details, we can make a PUT/PATCH request to the api/v1/products/{id} endpoint.

Update a Product REST API
Update a Product

Delete A Product

We can make a DELETE request to the api/v1/products/{id} endpoint to delete a product from the database

Delete a Product REST API
Delete a Product

In Closing

In this article, we have covered what Rest APIs are and how to create one in Laravel. We have also covered API authentication and Using Middleware to control access to your API. I hope this article was insightful and helped you create a powerful REST API in laravel.

Thank you for reading.

The post How to Build a Rest API with Laravel: A Beginners Guide appeared first on Ian Kumu's Blog.

Top comments (0)