CRUD API in Laravel: How to Create, Read, Update, and Delete

Learn how to create CRUD API in Laravel by following the steps in this tutorial. These examples use a MySQL database and Laravel. To create a very simple CRUD API we will start with the database and migration to it. Then, we'll go over the code for the controller and route files and some example endpoints.

Create a database table with migration

Laravel offers a migration feature that allows you to create database tables using migration files to create migration files simply type.

php artisan make:migration create_products_table

The command above will create a migration file in database/migrations directory the file name will be {{timestamp}}_create_products_table.

After that open that migration file in database/migrations/{{timestamp}}_create_products_table and you can write the migration logic in here. The up method function is where you write logic to create a database table. The down method function is where you write logic to remove the database table basically it’s just a reverse of migrating.

After that go to database/migrations/{{timestamp}}_create_products_table.php

<?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->decimal('price');
            $table->timestamps();
        });
    }

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

To create tables run the command below. The command below will run through all migration files and create new tables based on the logic that you write in the public function up() method.

php artisan migrate

Learn more at Migrating in Laravel.

Create a Product Model for your table

The model is associated with your table remember one model belongs to one table. You can see the model as a database table object where you can create, modify and remove database records by just calling the model. 

php artisan make:model Product

After that, you will see Product.php, Laravel model automatically uses the model name to identify the table which it associates with but to avoid any problem first you need to config the table name in the model and the fields of its model in the fillable array variables. Please see the example below.

In app/Model/Product.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $fillable = ["name", "price"];
}

Create ProductController CRUD

php artisan make:controller ProductController --api

After typing the command above with the –api you will see the ProductController in the app/Http/Controllers/ProductController.php with the default function of the index used for the list, store use for create, show use for showing the detail, update use for update record by id, and destroy for the delete data record. In the example below you will see the sample logic for CRUD. Noted that we still need to create a resource and a request to transform the response plus validation.

In app/Http/Controllers/ProductController.php

<?php

namespace App\Http\Controllers;

use App\Http\Requests\ProductRequest;
use App\Http\Resources\APIPaginateCollection;
use App\Http\Resources\ProductResource;
use App\Models\Product;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        $perPage = $request->per_page ? $request->per_page : 10;
        $currentPage = $request->current_page ? $request->current_page : 1;

        $products = Product::paginate($perPage, ["*"], "page", $currentPage);
        $response = new APIPaginateCollection($products, ProductResource::class);
        return response()->json($response);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(ProductRequest $request)
    {
        Product::create($request->only("name", "price"));
        return response()->json(["data" => [
            "success" => true
        ]]);
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $product = Product::findOrFail($id);
        $response = new ProductResource($product);
        return response()->json(["data" => $response]);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(ProductRequest $request, $id)
    {
        $product = Product::findOrFail($id);
        $product->update($request->only("name", "price"));
        return response()->json(["data" => [
            "success" => true
        ]]);
    }

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

Create ProductRequest to validate the product

The Laravel request feature allows us to handle the backend validation logic as well as the authorization for now we going to use the ProductRequest as a sample to validate product data. To create a request in Laravel just type the command below.

php artisan make:request ProductRequest 

Now you will see the ProductRequest.php in app/Http/Requests/ProductRequest.php and change the public function authorize() from return false to return true (we don’t care about authorization). To validate write any validate logic in the public function rules(), the rules() function will return an array of validation. The if condition will check on the API update as the product name is unique from the database record so on the update we need to ignore its primary key ‘id’ in other to process the update with the same name. See the example below.

In app/Http/Requests/ProductRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ProductRequest 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
     */
    public function rules()
    {
        $rules = [
            "name" => "required|max:255|unique:products,name",
            "price" => "required|digits_between:1,11|regex:/^\d*(\.\d{1,2})?$/"
        ];
        
        if ($this->isMethod('PUT') || $this->isMethod('PATCH')) {
            $rules["name"] = "required|max:255|unique:products,name," . $this->product;
        }

        return $rules;
    }
}

Learn more about how to use Laravel input validation with a request.

Create ProductResource for response data

The resource in Laravel is used to transform the response data Laravel provides flexibility as with Laravel resource you can transform nested data more easily with Laravel resource moreover you can get the associate relationship data by just pointing to the relation which already created in the model and transforming it again using another resource. This flexibility allows us to write less code in the controller.

To create the resource simply type the command below.

php artisan make:resource ProductResource

Now go to app/Http/Resources/ProductResource the ProductResource.php is where the command make:resource create. You can see in our ProductController that we already call the ProductResource in the index and show the function by passing the model instance to the resource. To transform any response see the example below.

In app/Http/Resources/ProductResource.php

<?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,
            "name" => $this->name,
            "price" => $this->price
        ];
    }
}

Learn more about how to use Laravel to transform response data with resources.

Create APIPaginateCollection for API Paginate Resource

Create the APIPaginateCollection collection resource by typing the command.

php artisan make:resource APIPaginateCollection --collection

The –collection will tell Laravel that they need to create a resource collection. A resource collection is a collection that transforms another resource and it extends from the ResourceCollection class. See the full example implementation below.

In you APIPaginateCollection.php

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class APIPaginateCollection extends ResourceCollection
{
    private $resourceClass;
    public $resource;

    public function __construct($resource, $resourceClass){
        parent::__construct($resource);
        $this->resource = $resource;
        $this->resourceClass = $resourceClass;
    }

    /**
     * Transform the resource collection into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
     */
    public function toArray($request){
        return [
            'data' => $this->resourceClass::collection($this->collection),
            'pagination' => [
                'current_page' => $this->resource->currentPage(),
                'last_page' => $this->resource->lastPage(),
                'limit' => $this->resource->perPage(),
                'total' => $this->resource->total()
            ]
        ];
    }
}

The APIPaginateCollection accepts two constructors. First, the model instance itself of Laravel paginate Model::paginate(). Second, the resource class that the APIPaginateCollection uses to transform each collection.

API Route

In routes/api.php

<?php

use App\Http\Controllers\ProductController;
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::apiResource('product', ProductController::class);

Sample endpoints

List

http://127.0.0.1:8000/api/product (GET METHOD)

Create

http://127.0.0.1:8000/api/product (POST METHOD)

Show

http://127.0.0.1:8000/api/product/3 (GET METHOD)

Update

http://127.0.0.1:8000/api/product/3 (PUT METHOD)

Delete

http://127.0.0.1:8000/api/product/3 (DELETE METHOD)

Conclusion

After going through the content above you will have a good understanding of the complete set of CRUD APIs including validation with the request, response data transformation with resource, and pagination customization with resource collection.

Why do we need to transform response data?