28 de febrero de 2023

Cómo crear una API REST con Laravel 13: guía completa con Sanctum y API Resources

Foto de Marco Orta Marco Orta | 7 mins de lectura
Compartir
Editor de código mostrando una ruta API de Laravel devolviendo JSON

Construir una API REST con Laravel es una de las habilidades más demandadas en 2026: cualquier app móvil, frontend SPA o agente de IA que se conecte a tu sistema lo hará a través de una API. En esta guía vamos a montar una API REST funcional con Laravel 13, incluyendo rutas, controladores, Form Requests para validación, API Resources para serialización y Sanctum para autenticación por tokens.

¿Qué es una API REST?

Una API REST (Representational State Transfer) es una interfaz que expone recursos accesibles por HTTP, devolviendo casi siempre JSON. Los principios clave: cada recurso tiene una URL única, las operaciones usan los verbos HTTP estándar (GET, POST, PUT/PATCH, DELETE), las respuestas son sin estado y siguen convenciones predecibles.

1. Crear una nueva aplicación Laravel 13

Si no tienes el proyecto, revisa primero Cómo instalar Laravel 13. En resumen:

laravel new mi-api
cd mi-api

En Laravel 11+, el archivo routes/api.php ya no se crea por defecto. Para habilitarlo, ejecuta:

php artisan install:api

Este comando crea routes/api.php, registra el prefijo /api automáticamente, e instala Laravel Sanctum para autenticación por tokens.

2. Configurar la base de datos

Edita el archivo .env con tus credenciales:

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

Para empezar rápido, también puedes usar SQLite:

DB_CONNECTION=sqlite

Luego ejecuta las migraciones:

php artisan migrate

3. Crear modelo, migración y factory

Vamos a montar un CRUD de “productos”. Generamos todo en un comando:

php artisan make:model Product -mfr

Las flags -mfr crean migración, factory y resource controller. Edita la migración generada:

public function up(): void
{
    Schema::create('products', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->text('description')->nullable();
        $table->decimal('price', 10, 2);
        $table->integer('stock')->default(0);
        $table->timestamps();
    });
}

Y en el modelo app/Models/Product.php, declara los $fillable:

class Product extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'description', 'price', 'stock'];
}

Ejecuta la migración:

php artisan migrate

4. Crear el API Resource

Los API Resources son la forma idiomática de Laravel para controlar exactamente qué campos expone tu API:

php artisan make:resource ProductResource

Edita app/Http/Resources/ProductResource.php:

public function toArray(Request $request): array
{
    return [
        'id'          => $this->id,
        'name'        => $this->name,
        'description' => $this->description,
        'price'       => (float) $this->price,
        'stock'       => $this->stock,
        'created_at'  => $this->created_at->toIso8601String(),
    ];
}

5. Crear Form Requests para validación

En lugar de validar dentro del controlador, en 2026 lo idiomático es usar Form Requests dedicados:

php artisan make:request StoreProductRequest
php artisan make:request UpdateProductRequest

Edita StoreProductRequest:

public function authorize(): bool
{
    return true;
}

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

6. Implementar el controlador

app/Http/Controllers/ProductController.php ya tiene el esqueleto. Llénalo así:

use App\Http\Requests\StoreProductRequest;
use App\Http\Requests\UpdateProductRequest;
use App\Http\Resources\ProductResource;
use App\Models\Product;

class ProductController extends Controller
{
    public function index()
    {
        return ProductResource::collection(Product::latest()->paginate(20));
    }

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

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

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

    public function destroy(Product $product)
    {
        $product->delete();
        return response()->noContent();
    }
}

Fíjate en dos patrones clave de Laravel moderno: route model binding (Product $product ya viene resuelto desde la URL) y response()->noContent() (devuelve 204 No Content correctamente).

7. Definir las rutas

Edita routes/api.php:

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

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

Una sola línea registra las cinco rutas REST: GET /api/products, POST /api/products, GET /api/products/{id}, PUT/PATCH /api/products/{id} y DELETE /api/products/{id}.

Verifica con:

php artisan route:list --path=api

8. Autenticación con Laravel Sanctum

Cuando ejecutaste php artisan install:api, Sanctum quedó instalado. Para emitir tokens, añade el trait HasApiTokens a App\Models\User:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
    // ...
}

Crea un endpoint de login que devuelva el token:

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

Route::post('/login', function (Request $request) {
    $request->validate([
        'email'    => ['required', 'email'],
        'password' => ['required'],
    ]);

    $user = User::where('email', $request->email)->first();

    if (! $user || ! Hash::check($request->password, $user->password)) {
        return response()->json(['message' => 'Credenciales inválidas'], 401);
    }

    return response()->json([
        'token' => $user->createToken('api')->plainTextToken,
        'user'  => $user,
    ]);
});

Y protege las rutas de productos:

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

Ahora cada petición a /api/products necesita el header:

Authorization: Bearer {token}

Sanctum emite tokens opacos, pero si tu API usa JSON Web Tokens (JWT) puedes inspeccionar su header y payload y verificar la firma con esta herramienta:

9. Probar la API

Usa Bruno, Thunder Client, Postman o curl directamente:

# Login
curl -X POST http://localhost:8000/api/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"password"}'

# Crear producto (sustituye TOKEN por el devuelto arriba)
curl -X POST http://localhost:8000/api/products \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Laptop","price":1500,"stock":10}'

10. Tests con PHPUnit (o Pest)

Laravel 13 trae Pest integrado por defecto. Un test básico:

use App\Models\User;
use Laravel\Sanctum\Sanctum;

it('lista productos para usuarios autenticados', function () {
    Sanctum::actingAs(User::factory()->create());

    $this->getJson('/api/products')
        ->assertOk()
        ->assertJsonStructure(['data', 'meta', 'links']);
});

it('rechaza peticiones sin token', function () {
    $this->getJson('/api/products')->assertStatus(401);
});

Ejecuta los tests:

php artisan test

Conclusión

En 2026 construir una API REST con Laravel es prácticamente “ejecutar install:api, crear un resource controller y listo”. Lo que marca la diferencia entre una API juguete y una API profesional son los detalles: API Resources para serialización limpia, Form Requests para validación, Sanctum para autenticación por tokens, y tests para no romper nada en cada release.

Una vez que tu API esté en pie, dos siguientes pasos típicos son: optimizar el rendimiento con Cómo optimizar el rendimiento de tu proyecto Laravel y, si quieres exponer tu lógica a agentes de IA, leer Cómo construir un agente de IA con Laravel y MCP.

Si te ha servido la guía, recuerda compartirla. ¡Saludos!

Compartir

Buscar

Etiquetas