#สร้าง REST API ด้วย Laravel 13: ตั้งแต่เริ่มต้นจนพร้อมใช้งาน

Laravel เป็น PHP Framework ที่เหมาะทั้งสำหรับการพัฒนา Web Application แบบ Full Stack และการทำหน้าที่เป็น API Backend ให้กับ Frontend เช่น React, Vue, Next.js, Mobile Application หรือระบบ Microservices

บทความนี้สาธิตการสร้าง REST API สำหรับจัดการสินค้า (Product API) ด้วย Laravel 13 โดยครอบคลุมตั้งแต่การสร้างโปรเจกต์ ไปจนถึง CRUD, Validation, API Resource และแนวทางเพิ่ม Authentication ด้วย Laravel Sanctum

API ที่เราจะสร้างมี Endpoint หลักดังนี้

Method Endpoint หน้าที่
GET /api/products แสดงรายการสินค้า
POST /api/products เพิ่มสินค้า
GET /api/products/{product} ดูรายละเอียดสินค้า
PUT/PATCH /api/products/{product} แก้ไขสินค้า
DELETE /api/products/{product} ลบสินค้า

#1. สิ่งที่ต้องเตรียม

เครื่องควรมีเครื่องมือพื้นฐานดังนี้

  • PHP
  • Composer
  • Laravel Installer
  • Database เช่น SQLite, MySQL หรือ PostgreSQL
  • API Client เช่น Postman, Bruno, Insomnia หรือ curl

ตรวจสอบ PHP และ Composer

php -v
composer --version

ติดตั้ง Laravel Installer หากยังไม่มี

composer global require laravel/installer

#2. สร้าง Laravel Project

สร้างโปรเจกต์ใหม่

laravel new laravel-api

เข้าไปในโฟลเดอร์โปรเจกต์

cd laravel-api

ทดลองรัน Development Server

php artisan serve

จากนั้นเปิด

http://127.0.0.1:8000

Laravel รุ่นใหม่สามารถใช้คำสั่ง

composer run dev

เพื่อรัน Development Services ตามที่โปรเจกต์กำหนดไว้ได้เช่นกัน


#3. เปิดใช้งาน API Routes

Laravel สามารถติดตั้งส่วนที่จำเป็นสำหรับ API ด้วยคำสั่ง

php artisan install:api

คำสั่งนี้จะเตรียม API routing และ Laravel Sanctum สำหรับการทำ Authentication แบบ API

หลังติดตั้งแล้วจะมีไฟล์

routes/api.php

Route ที่อยู่ในไฟล์นี้จะมี prefix /api โดยอัตโนมัติ

ตัวอย่าง

<?php

use Illuminate\Support\Facades\Route;

Route::get('/hello', function () {
    return response()->json([
        'message' => 'Hello Laravel API',
    ]);
});

เรียกใช้งานได้ที่

GET /api/hello

ทดสอบด้วย

curl http://127.0.0.1:8000/api/hello

ผลลัพธ์

{
  "message": "Hello Laravel API"
}

#4. ตั้งค่า Database

Laravel สามารถใช้ SQLite สำหรับการทดลองได้อย่างสะดวก หรือเปลี่ยนไปใช้ MySQL/PostgreSQL ใน .env

ตัวอย่าง MySQL

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_api
DB_USERNAME=root
DB_PASSWORD=

จากนั้นสร้างฐานข้อมูล laravel_api ให้เรียบร้อย


#5. สร้าง Model และ Migration

เราจะสร้าง Model ชื่อ Product

php artisan make:model Product -m

คำสั่งนี้สร้าง

app/Models/Product.php
database/migrations/xxxx_xx_xx_xxxxxx_create_products_table.php

แก้ไข Migration

<?php

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

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('sku')->unique();
            $table->text('description')->nullable();
            $table->decimal('price', 10, 2);
            $table->unsignedInteger('stock')->default(0);
            $table->boolean('is_active')->default(true);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};

รัน Migration

php artisan migrate

#6. กำหนด Product Model

เปิดไฟล์

app/Models/Product.php

กำหนดฟิลด์ที่อนุญาตให้ Mass Assignment

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $fillable = [
        'name',
        'sku',
        'description',
        'price',
        'stock',
        'is_active',
    ];

    protected function casts(): array
    {
        return [
            'price' => 'decimal:2',
            'stock' => 'integer',
            'is_active' => 'boolean',
        ];
    }
}

$fillable ช่วยควบคุมว่าฟิลด์ใดสามารถส่งเข้า Product::create() หรือ $product->update() ได้


#7. สร้าง Form Request สำหรับ Validation

ในงานจริงควรแยก Validation ออกจาก Controller เพื่อให้โค้ดดูแลง่าย

สร้าง Request สำหรับการเพิ่มสินค้า

php artisan make:request StoreProductRequest

สร้าง Request สำหรับการแก้ไขสินค้า

php artisan make:request UpdateProductRequest

#StoreProductRequest

แก้ไข

app/Http/Requests/StoreProductRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreProductRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'sku' => ['required', 'string', 'max:100', 'unique:products,sku'],
            'description' => ['nullable', 'string'],
            'price' => ['required', 'numeric', 'min:0'],
            'stock' => ['required', 'integer', 'min:0'],
            'is_active' => ['sometimes', 'boolean'],
        ];
    }
}

#UpdateProductRequest

แก้ไข

app/Http/Requests/UpdateProductRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UpdateProductRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name' => ['sometimes', 'required', 'string', 'max:255'],
            'sku' => [
                'sometimes',
                'required',
                'string',
                'max:100',
                Rule::unique('products', 'sku')
                    ->ignore($this->route('product')),
            ],
            'description' => ['nullable', 'string'],
            'price' => ['sometimes', 'required', 'numeric', 'min:0'],
            'stock' => ['sometimes', 'required', 'integer', 'min:0'],
            'is_active' => ['sometimes', 'boolean'],
        ];
    }
}

เมื่อ Validation ของ API ไม่ผ่าน Laravel จะส่ง JSON response พร้อม HTTP status 422 Unprocessable Entity


#8. สร้าง API Resource

ไม่ควรคืน Eloquent Model ตรง ๆ ในทุกกรณี เพราะโครงสร้างฐานข้อมูลอาจเปลี่ยนได้ การใช้ API Resource ช่วยสร้างชั้นสำหรับกำหนดรูปแบบ JSON Response

สร้าง Resource

php artisan make:resource ProductResource

แก้ไข

app/Http/Resources/ProductResource.php
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class ProductResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'sku' => $this->sku,
            'description' => $this->description,
            'price' => (float) $this->price,
            'stock' => $this->stock,
            'is_active' => $this->is_active,
            'created_at' => $this->created_at?->toISOString(),
            'updated_at' => $this->updated_at?->toISOString(),
        ];
    }
}

#9. สร้าง API Controller

สร้าง Controller

php artisan make:controller Api/ProductController --api

แก้ไข

app/Http/Controllers/Api/ProductController.php
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Requests\StoreProductRequest;
use App\Http\Requests\UpdateProductRequest;
use App\Http\Resources\ProductResource;
use App\Models\Product;
use Illuminate\Http\Response;

class ProductController extends Controller
{
    public function index()
    {
        $products = Product::query()
            ->latest()
            ->paginate(10);

        return ProductResource::collection($products);
    }

    public function store(StoreProductRequest $request)
    {
        $product = Product::create($request->validated());

        return (new ProductResource($product))
            ->response()
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function show(Product $product)
    {
        return new ProductResource($product);
    }

    public function update(
        UpdateProductRequest $request,
        Product $product
    ) {
        $product->update($request->validated());

        return new ProductResource($product->refresh());
    }

    public function destroy(Product $product)
    {
        $product->delete();

        return response()->noContent();
    }
}

จุดสำคัญคือ Laravel ใช้ Route Model Binding ดังนั้น parameter

Product $product

จะถูกค้นหาจาก {product} ใน URL ให้อัตโนมัติ

หากไม่พบข้อมูล Laravel จะตอบกลับ 404 Not Found


#10. กำหนด API Routes

เปิด

routes/api.php

เพิ่ม

<?php

use App\Http\Controllers\Api\ProductController;
use Illuminate\Support\Facades\Route;

Route::apiResource('products', ProductController::class);

เพียงบรรทัดเดียว Laravel จะสร้าง RESTful routes ที่จำเป็น

ตรวจสอบ Route

php artisan route:list --path=api

โครงสร้างจะใกล้เคียงกับ

GET|HEAD   api/products
POST       api/products
GET|HEAD   api/products/{product}
PUT|PATCH  api/products/{product}
DELETE     api/products/{product}

#11. ทดสอบ Create Product

ส่ง Request

curl -X POST http://127.0.0.1:8000/api/products \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Mechanical Keyboard",
    "sku": "KB-001",
    "description": "75% mechanical keyboard",
    "price": 2490,
    "stock": 20,
    "is_active": true
  }'

ตัวอย่าง Response

{
  "data": {
    "id": 1,
    "name": "Mechanical Keyboard",
    "sku": "KB-001",
    "description": "75% mechanical keyboard",
    "price": 2490,
    "stock": 20,
    "is_active": true,
    "created_at": "2026-09-15T01:00:00.000000Z",
    "updated_at": "2026-09-15T01:00:00.000000Z"
  }
}

HTTP Status ที่เหมาะสมคือ

201 Created

#12. ทดสอบ Get Products

curl \
  -H "Accept: application/json" \
  http://127.0.0.1:8000/api/products

เนื่องจาก Controller ใช้

paginate(10)

Response จะมีทั้งข้อมูลสินค้าและข้อมูล Pagination


#13. ทดสอบ Get Product By ID

curl \
  -H "Accept: application/json" \
  http://127.0.0.1:8000/api/products/1

#14. ทดสอบ Update Product

curl -X PATCH http://127.0.0.1:8000/api/products/1 \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "price": 2290,
    "stock": 15
  }'

การใช้ PATCH เหมาะกับการแก้ไขบางฟิลด์ โดยไม่จำเป็นต้องส่งข้อมูลทั้งหมด


#15. ทดสอบ Delete Product

curl -X DELETE \
  -H "Accept: application/json" \
  http://127.0.0.1:8000/api/products/1

เมื่อสำเร็จ API ควรตอบ

204 No Content

#16. Validation Error

ลองส่งข้อมูลที่ไม่ครบ

curl -X POST http://127.0.0.1:8000/api/products \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "",
    "price": -100
  }'

Laravel จะคืน Validation Error ในรูปแบบ JSON พร้อม status 422

ตัวอย่างแนวคิดของ Response

{
  "message": "The name field is required. (and more errors)",
  "errors": {
    "name": [
      "The name field is required."
    ],
    "sku": [
      "The sku field is required."
    ],
    "price": [
      "The price field must be at least 0."
    ],
    "stock": [
      "The stock field is required."
    ]
  }
}

#17. เพิ่ม Search และ Filter

เราสามารถขยาย index() ให้ค้นหาสินค้าได้

public function index()
{
    $products = Product::query()
        ->when(request('search'), function ($query, $search) {
            $query->where(function ($query) use ($search) {
                $query
                    ->where('name', 'like', "%{$search}%")
                    ->orWhere('sku', 'like', "%{$search}%");
            });
        })
        ->when(
            request()->has('active'),
            fn ($query) => $query->where(
                'is_active',
                request()->boolean('active')
            )
        )
        ->latest()
        ->paginate(10)
        ->withQueryString();

    return ProductResource::collection($products);
}

เรียกใช้งาน

GET /api/products?search=keyboard

หรือ

GET /api/products?active=1

#18. Versioning API

เมื่อระบบเริ่มมี Client หลายรุ่น ควรพิจารณา API Versioning

ตัวอย่าง

Route::prefix('v1')->group(function () {
    Route::apiResource('products', ProductController::class);
});

Endpoint จะกลายเป็น

/api/v1/products

ข้อดีคือสามารถพัฒนา /api/v2/... ในอนาคตโดยลดผลกระทบต่อ Client เดิม


#19. ป้องกัน API ด้วย Laravel Sanctum

หาก API ต้องการ Authentication สามารถใช้ Laravel Sanctum ได้

หลังจาก

php artisan install:api

เราสามารถป้องกัน Routes ด้วย middleware

Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('products', ProductController::class);
});

Request ที่ใช้ Personal Access Token จะส่ง Header ลักษณะ

Authorization: Bearer YOUR_API_TOKEN

แนวคิดนี้เหมาะสำหรับ Mobile App, Internal API หรือ API ที่ใช้ Token Authentication

สำหรับ SPA ที่อยู่ใน first-party domain ควรศึกษา Sanctum SPA Authentication เพิ่มเติม เพราะรูปแบบการยืนยันตัวตนแตกต่างจาก Personal Access Token


#20. HTTP Status Code ที่ควรรู้

Status ความหมาย ตัวอย่าง
200 OK Request สำเร็จ อ่าน/แก้ไขข้อมูล
201 Created สร้าง Resource สำเร็จ POST
204 No Content สำเร็จแต่ไม่มี Response Body DELETE
401 Unauthorized ยังไม่ได้ Authentication Token ไม่ถูกต้อง/ไม่มี Token
403 Forbidden ไม่มีสิทธิ์ ผ่าน Authentication แต่ไม่มี Permission
404 Not Found ไม่พบ Resource Product ID ไม่มี
422 Unprocessable Entity Validation ไม่ผ่าน Input ไม่ถูกต้อง
429 Too Many Requests Request มากเกิน Rate Limit API ถูกเรียกถี่เกินไป
500 Internal Server Error Server Error Exception ที่ไม่ได้จัดการ

#21. โครงสร้างโปรเจกต์ที่เกี่ยวข้อง

หลังทำ Tutorial นี้ ส่วนสำคัญจะมีประมาณนี้

app/
├── Http/
│   ├── Controllers/
│   │   └── Api/
│   │       └── ProductController.php
│   ├── Requests/
│   │   ├── StoreProductRequest.php
│   │   └── UpdateProductRequest.php
│   └── Resources/
│       └── ProductResource.php
└── Models/
    └── Product.php

database/
└── migrations/
    └── xxxx_xx_xx_xxxxxx_create_products_table.php

routes/
└── api.php

โครงสร้างนี้แยกหน้าที่ชัดเจน

  • Route — กำหนด URL และ HTTP Method
  • Controller — จัดการ Application Flow
  • Form Request — ตรวจสอบ Input
  • Model — ติดต่อและจัดการข้อมูล
  • API Resource — กำหนด JSON Output
  • Sanctum / Middleware — Authentication และ Authorization

#22. Data Flow ของ Laravel API

ภาพรวมการทำงานสามารถอธิบายได้ดังนี้

Client
  |
  | HTTP Request
  v
Route
  |
  v
Middleware
  |
  v
Form Request Validation
  |
  v
Controller
  |
  v
Eloquent Model
  |
  v
Database
  |
  v
API Resource
  |
  v
JSON Response

การแยก Layer แบบนี้ทำให้ระบบสามารถขยายและทดสอบได้ง่ายกว่าเขียน Business Logic ทั้งหมดไว้ใน Route หรือ Controller


#23. แนวทางที่ควรใช้ใน Production

เมื่อ API เริ่มใช้งานจริง ควรพิจารณาประเด็นต่อไปนี้

#ใช้ Form Request

หลีกเลี่ยง Validation ขนาดใหญ่ใน Controller เพราะจะทำให้ Controller ซับซ้อน

#ใช้ API Resource

อย่าผูก JSON Response กับ Database Schema โดยตรง

ใช้ Pagination

Endpoint ที่คืน Collection ไม่ควรโหลดข้อมูลทั้งหมดแบบไม่จำกัด

Product::paginate(20);

#ใช้ Authentication และ Authorization

Authentication ตอบว่า ผู้ใช้คือใคร

Authorization ตอบว่า ผู้ใช้ทำอะไรได้บ้าง

Laravel สามารถใช้ Policy, Gate และ Sanctum เพื่อควบคุมสิทธิ์ได้

#ใช้ Rate Limiting

ช่วยลด Abuse, Bot และ Request ที่ผิดปกติ

#เขียน Automated Tests

API ควรมี Feature Tests ครอบคลุม Happy Path และ Error Cases

#ทำ API Documentation

สำหรับทีมพัฒนาและ Client ควรมีเอกสาร API เช่น OpenAPI/Swagger

#ใช้ HTTPS

Production API ควรให้บริการผ่าน HTTPS เสมอ

#Logging และ Monitoring

ควรเก็บ Logs และ Metrics เพื่อวิเคราะห์ปัญหาเมื่อระบบจริงเกิด Error


#24. ตัวอย่าง Feature Test

สร้าง Test

php artisan make:test ProductApiTest

ตัวอย่าง

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class ProductApiTest extends TestCase
{
    use RefreshDatabase;

    public function test_can_create_product(): void
    {
        $response = $this->postJson('/api/products', [
            'name' => 'Mechanical Keyboard',
            'sku' => 'KB-001',
            'price' => 2490,
            'stock' => 10,
            'is_active' => true,
        ]);

        $response
            ->assertCreated()
            ->assertJsonPath('data.name', 'Mechanical Keyboard');

        $this->assertDatabaseHas('products', [
            'sku' => 'KB-001',
        ]);
    }

    public function test_product_requires_valid_data(): void
    {
        $response = $this->postJson('/api/products', []);

        $response
            ->assertUnprocessable()
            ->assertJsonValidationErrors([
                'name',
                'sku',
                'price',
                'stock',
            ]);
    }
}

รัน Test

php artisan test

หรือเลือกเฉพาะไฟล์

php artisan test tests/Feature/ProductApiTest.php

#25. สรุปคำสั่งสำคัญ

# สร้างโปรเจกต์
laravel new laravel-api

cd laravel-api

# เปิดใช้งาน API + Sanctum
php artisan install:api

# สร้าง Model และ Migration
php artisan make:model Product -m

# สร้าง Form Requests
php artisan make:request StoreProductRequest
php artisan make:request UpdateProductRequest

# สร้าง API Resource
php artisan make:resource ProductResource

# สร้าง API Controller
php artisan make:controller Api/ProductController --api

# สร้างฐานข้อมูลตาม Migration
php artisan migrate

# ดู API Routes
php artisan route:list --path=api

# รัน Development Server
php artisan serve

# รัน Automated Tests
php artisan test

#สรุป

การสร้าง REST API ด้วย Laravel สามารถพัฒนาได้อย่างเป็นระบบโดยใช้เครื่องมือที่ Framework เตรียมไว้ ได้แก่

Route
   ↓
Middleware
   ↓
Form Request
   ↓
Controller
   ↓
Eloquent Model
   ↓
Database
   ↓
API Resource
   ↓
JSON Response

สำหรับ API ขนาดเล็ก เราสามารถเริ่มจาก Route::apiResource() และ Controller แบบ CRUD ได้ทันที แต่เมื่อระบบใหญ่ขึ้นควรค่อย ๆ เพิ่ม Service Layer, Policies, Authentication, Caching, Queue, API Versioning, Automated Testing และ Observability ตามความเหมาะสม

Laravel จึงเหมาะทั้งกับการสร้าง API สำหรับ Web/Mobile Application และการพัฒนา Backend Service ที่ต้องการโครงสร้างชัดเจนและขยายต่อได้ง่าย


#เอกสารอ้างอิง